Add BoardMenu (#1592)

Signed-off-by: Dvinyanin Alexandr <dvinyanin.alexandr@gmail.com>
This commit is contained in:
Alex 2022-04-29 23:39:48 +07:00 committed by GitHub
parent a44fb47592
commit 67e6a7958f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 153 additions and 71 deletions

View File

@ -14,7 +14,7 @@
//
// To help typescript locate view plugin properly
import type { Board, Card, CardAction, CardDate, CardLabel } from '@anticrm/board'
import type { Board, Card, CardAction, CardDate, CardLabel, MenuPage } from '@anticrm/board'
import type { Employee } from '@anticrm/contact'
import { TxOperations as Client, Doc, DOMAIN_MODEL, FindOptions, IndexKind, Ref, Type, Timestamp } from '@anticrm/core'
import {
@ -122,8 +122,26 @@ export class TCardAction extends TDoc implements CardAction {
supported?: Resource<(card: Card, client: Client) => boolean>
}
@Model(board.class.MenuPage, core.class.Doc, DOMAIN_MODEL)
export class TMenuPage extends TDoc implements MenuPage {
component!: AnyComponent
pageId!: string
label!: IntlString
}
export function createModel (builder: Builder): void {
builder.createModel(TBoard, TCard, TCardLabel, TCardDate, TCardAction)
builder.createModel(TBoard, TCard, TCardLabel, TCardDate, TCardAction, TMenuPage)
builder.createDoc(board.class.MenuPage, core.space.Model, {
component: board.component.Archive,
pageId: board.menuPageId.Archive,
label: board.string.Archive
})
builder.createDoc(board.class.MenuPage, core.space.Model, {
component: board.component.MenuMainPage,
pageId: board.menuPageId.Main,
label: board.string.Menu
})
builder.mixin(board.class.Board, core.class.Class, workbench.mixin.SpaceView, {
view: {
@ -151,7 +169,8 @@ export function createModel (builder: Builder): void {
addSpaceLabel: board.string.BoardCreateLabel,
createComponent: board.component.CreateBoard
}
]
],
aside: board.component.BoardMenu
}
},
board.app.Board

View File

@ -91,6 +91,7 @@
"DeleteCard": "All actions will be removed from the activity feed and you wont be able to re-open the card. There is no undo.",
"SearchMembers": "Search members",
"Menu": "Menu",
"ShowMenu": "Show menu",
"ToArchive": "Archive",
"CopyCard": "Copy card",
"AlsoCopy": "Keep...",

View File

@ -91,6 +91,7 @@
"DeleteCard": "Все действия будут удалены из ленты, и вы не сможете повторно открыть карточку. Отмена невозможна.",
"SearchMembers": "Поиск участников",
"Menu": "Меню",
"ShowMenu": "Показать меню",
"ToArchive": "Архивировать",
"CopyCard": "Копировать карточку",
"AlsoCopy": "Также копировать...",

View File

@ -0,0 +1,29 @@
<script lang="ts">
import { Ref, Space } from '@anticrm/core'
import { Button } from '@anticrm/ui'
import board from '../plugin'
import CardsArchive from './CardArchive.svelte'
import ListArchive from './ListArchive.svelte'
import TextArea from '@anticrm/ui/src/components/TextArea.svelte'
export let space: Ref<Space>
let isCardArchive = true
let search: string = ''
$: query = { space, title: { $like: '%' + search + '%' } }
$: label = isCardArchive
? board.string.SwitchToLists
: board.string.SwitchToCards
</script>
<div class="p-4">
<Button {label} width={'100%'} on:click={() => { isCardArchive = !isCardArchive }} />
<div class="pt-4">
<TextArea bind:value={search} placeholder={board.string.SearchArchive}/>
</div>
</div>
{#if isCardArchive}
<CardsArchive {query}/>
{:else}
<ListArchive {query}/>
{/if}

View File

@ -1,30 +1,28 @@
<script lang="ts">
import core, { Ref, Space } from '@anticrm/core'
import { Button, showPopup } from '@anticrm/ui'
import { Button, getCurrentLocation, navigate } from '@anticrm/ui'
import { createQuery, getClient } from '@anticrm/presentation'
import { Header, classIcon } from '@anticrm/chunter-resources'
import Menu from './popups/Menu.svelte'
import { getPopupAlignment } from '../utils/PopupUtils'
import border from '../plugin'
export let spaceId: Ref<Space> | undefined
let space: Space
const query = createQuery()
$: query.query(core.class.Space, { _id: spaceId }, (result) => {
space = result[0]
})
$: query.query(core.class.Space, { _id: spaceId }, result => { space = result[0] })
const client = getClient()
function showMenu (e: MouseEvent) {
showPopup(Menu, { space: space._id }, getPopupAlignment(e, { h: 'left', v: 'top' }))
function showMenu () {
const loc = getCurrentLocation()
loc.path[3] = space._id
navigate(loc)
}
</script>
<div class="ac-header divide full">
{#if space}
<Header icon={classIcon(client, space._class)} label={space.name} description={space.description} />
<Button label={border.string.Menu} on:click={showMenu} />
<Header icon={classIcon(client, space._class)} label={space.name} description={space.description}/>
<Button label={border.string.ShowMenu} on:click={showMenu}/>
{/if}
</div>

View File

@ -0,0 +1,52 @@
<script lang="ts">
import { Ref, Space } from '@anticrm/core'
import Label from '@anticrm/ui/src/components/Label.svelte'
import board from '../plugin'
import { createQuery } from '@anticrm/presentation'
import { MenuPage } from '@anticrm/board'
import { Button, Component, IconBack, IconClose } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte'
export let currentSpace: Ref<Space> | undefined
const dispatch = createEventDispatcher()
let currentPageId = board.menuPageId.Main
let trace: string[] = []
let page: MenuPage
const query = createQuery()
$: query.query(board.class.MenuPage, { pageId: currentPageId }, result => {
;[page] = result
})
function setKey (e: CustomEvent) {
trace = [currentPageId, ...trace]
currentPageId = e.detail
}
function onBack () {
[currentPageId = board.menuPageId.Main, ...trace] = trace
}
</script>
{#if page}
<div class="ac-header flex-between w-full divide">
{#if trace.length}
<Button icon={IconBack} kind="transparent" size="x-large" on:click={onBack} />
{:else}
<div class="ml-12"/>
{/if}
<div class="flex-center fs-title">
<Label label={page.label} />
</div>
<Button
icon={IconClose}
kind="transparent"
size="x-large"
on:click={() => {
dispatch('close')
}} />
</div>
{#if currentSpace}
<div class="vScroll mb-4">
<Component is={page.component} props={{ space: currentSpace }} on:change={setKey}/>
</div>
{/if}
{/if}

View File

@ -0,0 +1,12 @@
<script lang="ts">
import { Button } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte'
import board from '@anticrm/board'
import plugin from '../plugin'
const dispatch = createEventDispatcher()
</script>
<div class="p-4">
<Button label={plugin.string.Archive} width={'100%'} on:click={() => { dispatch('change', board.menuPageId.Archive) }} />
</div>

View File

@ -1,55 +0,0 @@
<script lang="ts">
import { Ref, Space } from '@anticrm/core'
import { ActionIcon, Button, IconClose, Label } from '@anticrm/ui'
import TextArea from '@anticrm/ui/src/components/TextArea.svelte'
import { createEventDispatcher } from 'svelte'
import board from '../../plugin'
import CardsArchive from '../CardArchive.svelte'
import ListArchive from '../ListArchive.svelte'
export let space: Ref<Space>
const dispatch = createEventDispatcher()
let isCardArchive = true
let search: string = ''
$: query = { space, title: { $like: '%' + search + '%' } }
$: label = isCardArchive ? board.string.SwitchToLists : board.string.SwitchToCards
</script>
<div class="antiPopup antiPopup-withHeader antiPopup-withCategory w-60">
<div class="ap-space" />
<div class="flex-row-center header">
<div class="flex-center flex-grow">
<Label label={board.string.Archive} />
</div>
<div class="close-icon mr-1">
<ActionIcon
icon={IconClose}
size={'small'}
action={() => {
dispatch('close')
}}
/>
</div>
</div>
<div class="ap-space bottom-divider" />
<div class="ap-scroll">
<div class="p-4">
<Button
{label}
width={'100%'}
on:click={() => {
isCardArchive = !isCardArchive
}}
/>
<div class="pt-4">
<TextArea bind:value={search} placeholder={board.string.SearchArchive} />
</div>
</div>
{#if isCardArchive}
<CardsArchive {query} />
{:else}
<ListArchive {query} />
{/if}
</div>
</div>

View File

@ -38,6 +38,9 @@ import CardLabelPresenter from './components/presenters/LabelPresenter.svelte'
import TemplatesIcon from './components/TemplatesIcon.svelte'
import WatchCard from './components/WatchCard.svelte'
import BoardHeader from './components/BoardHeader.svelte'
import BoardMenu from './components/BoardMenu.svelte'
import MenuMainPage from './components/MenuMainPage.svelte'
import Archive from './components/Archive.svelte'
import board from './plugin'
import {
addCurrentUser,
@ -104,7 +107,10 @@ export default async (): Promise<Resources> => ({
KanbanView,
BoardPresenter,
WatchCard,
BoardHeader
BoardHeader,
BoardMenu,
Archive,
MenuMainPage
},
cardActionHandler: {
Join: addCurrentUser,

View File

@ -112,6 +112,7 @@ export default mergeIds(boardId, board, {
SearchMembers: '' as IntlString,
DeleteCard: '' as IntlString,
Menu: '' as IntlString,
ShowMenu: '' as IntlString,
ToArchive: '' as IntlString,
CopyCard: '' as IntlString,
AlsoCopy: '' as IntlString,
@ -123,6 +124,11 @@ export default mergeIds(boardId, board, {
},
component: {
EditCard: '' as AnyComponent,
BoardHeader: '' as AnyComponent
Members: '' as AnyComponent,
Settings: '' as AnyComponent,
BoardHeader: '' as AnyComponent,
BoardMenu: '' as AnyComponent,
Archive: '' as AnyComponent,
MenuMainPage: '' as AnyComponent
}
})

View File

@ -94,6 +94,14 @@ export interface CardAction extends Doc {
handler?: Resource<(card: Card, client: Client, e?: Event) => void>
supported?: Resource<(card: Card, client: Client) => boolean>
}
/**
* @public
*/
export interface MenuPage extends Doc {
component: AnyComponent
pageId: string
label: IntlString
}
/**
* @public
@ -112,7 +120,8 @@ const boards = plugin(boardId, {
Card: '' as Ref<Class<Card>>,
CardAction: '' as Ref<Class<CardAction>>,
CardDate: '' as Ref<Class<CardDate>>,
CardLabel: '' as Ref<Class<CardLabel>>
CardLabel: '' as Ref<Class<CardLabel>>,
MenuPage: '' as Ref<Class<MenuPage>>
},
icon: {
Board: '' as Asset,
@ -121,6 +130,10 @@ const boards = plugin(boardId, {
space: {
BoardTemplates: '' as Ref<KanbanTemplateSpace>
},
menuPageId: {
Main: 'main',
Archive: 'archive'
},
cardActionType: {
Suggested: 'Suggested',
Editor: 'Editor',