Board: Use Standard actions (#1766)

* Board: use standard actions

Signed-off-by: Anna No <anna.no@xored.com>

* Board: use standard actions

Signed-off-by: Anna No <anna.no@xored.com>

* fix lint issues

Signed-off-by: Anna No <anna.no@xored.com>

* fix lint issues

Signed-off-by: Anna No <anna.no@xored.com>

* fix merge conflicts

Signed-off-by: Anna No <anna.no@xored.com>

* fix merge conflicts

Signed-off-by: Anna No <anna.no@xored.com>
This commit is contained in:
Anna No 2022-05-17 15:36:11 +07:00 committed by GitHub
parent 7b49752288
commit 29058d8c84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 367 additions and 701 deletions

View File

@ -14,9 +14,9 @@
//
// To help typescript locate view plugin properly
import type { Board, Card, CardAction, CardDate, CardLabel, MenuPage, LabelsCompactMode } from '@anticrm/board'
import type { Board, Card, CardDate, CardLabel, MenuPage, LabelsCompactMode } from '@anticrm/board'
import type { Employee } from '@anticrm/contact'
import { DOMAIN_MODEL, IndexKind, Markup, Ref, Timestamp, TxOperations as Client, Type } from '@anticrm/core'
import { DOMAIN_MODEL, IndexKind, Markup, Ref, Timestamp, Type } from '@anticrm/core'
import {
ArrOf,
Builder,
@ -35,9 +35,9 @@ import chunter from '@anticrm/model-chunter'
import contact from '@anticrm/model-contact'
import core, { TAttachedDoc, TDoc, TObj } from '@anticrm/model-core'
import task, { TSpaceWithStates, TTask } from '@anticrm/model-task'
import view from '@anticrm/model-view'
import view, { actionTemplates, createAction } from '@anticrm/model-view'
import workbench from '@anticrm/model-workbench'
import { Asset, IntlString, Resource } from '@anticrm/platform'
import { IntlString } from '@anticrm/platform'
import type { AnyComponent } from '@anticrm/ui'
import preference, { TPreference } from '@anticrm/model-preference'
import board from './plugin'
@ -115,20 +115,6 @@ export class TCard extends TTask implements Card {
members?: Ref<Employee>[]
}
@Model(board.class.CardAction, core.class.Doc, DOMAIN_MODEL)
export class TCardAction extends TDoc implements CardAction {
component?: AnyComponent
hint?: IntlString
icon!: Asset
isInline?: boolean
kind?: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'dangerous'
label!: IntlString
position!: number
type!: string
handler?: Resource<(card: Card, client: Client, e?: Event) => void>
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
@ -137,7 +123,7 @@ export class TMenuPage extends TDoc implements MenuPage {
}
export function createModel (builder: Builder): void {
builder.createModel(TBoard, TCard, TCardLabel, TCardDate, TCardAction, TMenuPage, TLabelsCompactMode)
builder.createModel(TBoard, TCard, TCardLabel, TCardDate, TMenuPage, TLabelsCompactMode)
builder.createDoc(board.class.MenuPage, core.space.Model, {
component: board.component.Archive,
@ -278,207 +264,156 @@ export function createModel (builder: Builder): void {
)
// card actions
builder.createDoc(
board.class.CardAction,
core.space.Model,
createAction(
builder,
{
icon: board.icon.Card,
isInline: false,
label: board.string.Join,
position: 10,
type: board.cardActionType.Suggested,
handler: board.cardActionHandler.Join,
supported: board.cardActionSupportedHandler.Join
},
board.cardAction.Join
)
builder.createDoc(
board.class.CardAction,
core.space.Model,
{
icon: board.icon.Card,
isInline: true,
label: board.string.Members,
position: 20,
type: board.cardActionType.AddToCard,
handler: board.cardActionHandler.Members
},
board.cardAction.Members
)
builder.createDoc(
board.class.CardAction,
core.space.Model,
{
icon: board.icon.Card,
isInline: true,
action: view.actionImpl.ShowPopup,
actionProps: {
component: board.component.LabelsActionPopup,
element: 'top'
},
label: board.string.Labels,
position: 30,
type: board.cardActionType.AddToCard,
handler: board.cardActionHandler.Labels
},
board.cardAction.Labels
)
builder.createDoc(
board.class.CardAction,
core.space.Model,
{
icon: board.icon.Card,
isInline: false,
label: board.string.Checklist,
position: 40,
type: board.cardActionType.AddToCard,
handler: board.cardActionHandler.Checklist
input: 'any',
category: board.category.Card,
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'top' }
},
board.cardAction.Checklist
board.action.Labels
)
builder.createDoc(
board.class.CardAction,
core.space.Model,
createAction(
builder,
{
icon: board.icon.Card,
isInline: true,
action: view.actionImpl.ShowPopup,
actionProps: {
component: board.component.DatesActionPopup,
element: 'top'
},
label: board.string.Dates,
position: 50,
type: board.cardActionType.AddToCard,
handler: board.cardActionHandler.Dates
},
board.cardAction.Dates
)
builder.createDoc(
board.class.CardAction,
core.space.Model,
{
icon: board.icon.Card,
isInline: false,
label: board.string.Attachments,
position: 60,
type: board.cardActionType.AddToCard,
handler: board.cardActionHandler.Attachments
input: 'any',
category: board.category.Card,
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'top' }
},
board.cardAction.Attachments
board.action.Dates
)
builder.createDoc(
board.class.CardAction,
core.space.Model,
createAction(
builder,
{
icon: board.icon.Card,
isInline: true,
action: view.actionImpl.ShowPopup,
actionProps: {
component: board.component.CoverActionPopup,
element: 'top'
},
label: board.string.Cover,
position: 70,
type: board.cardActionType.Cover,
handler: board.cardActionHandler.Cover
},
board.cardAction.Cover
)
builder.createDoc(
board.class.CardAction,
core.space.Model,
{
icon: board.icon.Card,
isInline: false,
label: board.string.CustomFields,
position: 80,
type: board.cardActionType.AddToCard,
handler: board.cardActionHandler.CustomFields
input: 'any',
category: board.category.Card,
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'top' }
},
board.cardAction.CustomFields
board.action.Cover
)
builder.createDoc(
board.class.CardAction,
core.space.Model,
createAction(
builder,
{
icon: board.icon.Card,
isInline: false,
kind: 'transparent',
label: board.string.AddButton,
position: 90,
type: board.cardActionType.Automation,
handler: board.cardActionHandler.AddButton
...actionTemplates.move,
action: view.actionImpl.ShowPopup,
actionProps: {
component: board.component.MoveActionPopup,
element: 'top'
},
input: 'any',
category: board.category.Card,
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'tools' }
},
board.cardAction.AddButton
board.action.Move
)
builder.createDoc(
board.class.CardAction,
core.space.Model,
createAction(
builder,
{
icon: board.icon.Card,
isInline: true,
label: board.string.Move,
position: 100,
type: board.cardActionType.Action,
handler: board.cardActionHandler.Move
},
board.cardAction.Move
)
builder.createDoc(
board.class.CardAction,
core.space.Model,
{
icon: board.icon.Card,
isInline: true,
action: view.actionImpl.ShowPopup,
actionProps: {
component: board.component.CopyActionPopup,
element: 'top'
},
label: board.string.Copy,
position: 110,
type: board.cardActionType.Action,
handler: board.cardActionHandler.Copy
},
board.cardAction.Copy
)
builder.createDoc(
board.class.CardAction,
core.space.Model,
{
icon: board.icon.Card,
isInline: false,
label: board.string.MakeTemplate,
position: 120,
type: board.cardActionType.Action,
handler: board.cardActionHandler.MakeTemplate
input: 'any',
category: board.category.Card,
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'tools' }
},
board.cardAction.MakeTemplate
board.action.Copy
)
builder.createDoc(
board.class.CardAction,
core.space.Model,
createAction(
builder,
{
action: view.actionImpl.UpdateDocument,
actionProps: {
key: 'isArchived',
value: true,
ask: true,
label: task.string.Archive,
message: task.string.ArchiveConfirm
},
query: {
isArchived: { $nin: [true] }
},
label: board.string.Archive,
icon: board.icon.Card,
isInline: true,
label: board.string.ToArchive,
position: 140,
type: board.cardActionType.Action,
handler: board.cardActionHandler.Archive,
supported: board.cardActionSupportedHandler.Archive
input: 'any',
category: board.category.Card,
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'tools' }
},
board.cardAction.Archive
board.action.Archive
)
builder.createDoc(
board.class.CardAction,
core.space.Model,
createAction(
builder,
{
icon: board.icon.Card,
isInline: true,
action: view.actionImpl.UpdateDocument,
actionProps: {
key: 'isArchived',
value: false
},
query: {
isArchived: true
},
label: board.string.SendToBoard,
position: 140,
type: board.cardActionType.Action,
handler: board.cardActionHandler.SendToBoard,
supported: board.cardActionSupportedHandler.SendToBoard
},
board.cardAction.SendToBoard
)
builder.createDoc(
board.class.CardAction,
core.space.Model,
{
icon: board.icon.Card,
isInline: false,
kind: 'dangerous',
label: board.string.Delete,
position: 150,
type: board.cardActionType.Action,
handler: board.cardActionHandler.Delete,
supported: board.cardActionSupportedHandler.Delete
input: 'any',
category: board.category.Card,
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'tools' }
},
board.cardAction.Delete
board.action.SendToBoard
)
createAction(
builder,
{
action: view.actionImpl.Delete,
query: {
isArchived: true
},
label: view.string.Delete,
icon: view.icon.Delete,
keyBinding: ['Meta + Backspace', 'Ctrl + Backspace'],
category: board.category.Card,
input: 'any',
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'tools' }
},
board.action.Delete
)
builder.mixin(board.class.Card, core.class.Class, view.mixin.IgnoreActions, {
actions: [view.action.Delete, task.action.Move]
})
}
export { boardOperation } from './migration'

View File

@ -34,7 +34,12 @@ export default mergeIds(boardId, board, {
TemplatesIcon: '' as AnyComponent,
Cards: '' as AnyComponent,
KanbanView: '' as AnyComponent,
TableView: '' as AnyComponent
TableView: '' as AnyComponent,
LabelsActionPopup: '' as AnyComponent,
DatesActionPopup: '' as AnyComponent,
CoverActionPopup: '' as AnyComponent,
MoveActionPopup: '' as AnyComponent,
CopyActionPopup: '' as AnyComponent
},
space: {
DefaultBoard: '' as Ref<Space>

View File

@ -509,11 +509,15 @@ export function createModel (builder: Builder): void {
task.completion.IssueCategory
)
createAction(builder, {
...viewTemplates.move,
target: task.class.Task,
context: {
mode: ['context', 'browser']
}
})
createAction(
builder,
{
...viewTemplates.move,
target: task.class.Task,
context: {
mode: ['context', 'browser']
}
},
task.action.Move
)
}

View File

@ -27,7 +27,8 @@ export default mergeIds(taskId, task, {
action: {
EditStatuses: '' as Ref<Action>,
ArchiveSpace: '' as Ref<Action>,
UnarchiveSpace: '' as Ref<Action>
UnarchiveSpace: '' as Ref<Action>,
Move: '' as Ref<Action>
},
actionImpl: {
EditStatuses: '' as ViewAction,

View File

@ -1,15 +1,18 @@
<script lang="ts">
import { Button, Label } from '@anticrm/ui'
import { createQuery, getClient } from '@anticrm/presentation'
import { Card } from '@anticrm/board'
import { DocumentQuery, SortingOrder } from '@anticrm/core'
import { getResource } from '@anticrm/platform'
import { createQuery, getClient } from '@anticrm/presentation'
import { Button, Label } from '@anticrm/ui'
import type { Action } from '@anticrm/view'
import { invokeAction } from '@anticrm/view-resources'
import board from '../plugin'
import { getCardActions } from '../utils/CardActionUtils'
import KanbanCard from './KanbanCard.svelte'
export let query: DocumentQuery<Card> = {}
let archivedCards: Card[]
let actions: Action[] = []
const client = getClient()
const cardQuery = createQuery()
$: cardQuery.query(
@ -20,6 +23,9 @@
},
{ sort: { rank: SortingOrder.Descending } }
)
getCardActions(client, { _id: { $in: [board.action.SendToBoard, board.action.Delete] } }).then(async (result) => {
actions = result
})
</script>
{#if archivedCards}
@ -33,16 +39,20 @@
<div class="flex-center flex-gap-2 w-full">
<Button
label={board.string.SendToBoard}
on:click={async (e) => {
const unarchiveAction = await getResource(board.cardActionHandler.SendToBoard)
unarchiveAction(card, client, e)
on:click={(e) => {
const unarchiveAction = actions.find((a) => a._id === board.action.SendToBoard)
if (unarchiveAction) {
invokeAction(card, e, unarchiveAction.action, unarchiveAction.actionProps)
}
}}
/>
<Button
label={board.string.Delete}
on:click={async (e) => {
const deleteAction = await getResource(board.cardActionHandler.Delete)
deleteAction(card, client, e)
const deleteAction = actions.find((a) => a._id === board.action.Delete)
if (deleteAction) {
invokeAction(card, e, deleteAction.action, deleteAction.actionProps)
}
}}
/>
</div>

View File

@ -17,13 +17,12 @@
import type { Card } from '@anticrm/board'
import { Class, Ref } from '@anticrm/core'
import { Panel } from '@anticrm/panel'
import { getResource } from '@anticrm/platform'
import { createQuery, getClient } from '@anticrm/presentation'
import type { State, TodoItem } from '@anticrm/task'
import task from '@anticrm/task'
import { StyledTextBox } from '@anticrm/text-editor'
import { Button, EditBox, Icon, Label } from '@anticrm/ui'
import { UpDownNavigator } from '@anticrm/view-resources'
import { invokeAction, UpDownNavigator } from '@anticrm/view-resources'
import { createEventDispatcher, onMount } from 'svelte'
import board from '../plugin'
import { getCardActions } from '../utils/CardActionUtils'
@ -66,13 +65,13 @@
checklists = result
})
getCardActions(client, { _id: board.cardAction.Move }).then(async (result) => {
if (result[0]?.handler) {
const handler = await getResource(result[0].handler)
getCardActions(client, { _id: board.action.Move }).then(async (result) => {
if (result[0]) {
handleMove = (e) => {
if (object) {
handler(object, client, e)
if (!object) {
return
}
invokeAction(object, e, result[0].action, result[0].actionProps)
}
}
})

View File

@ -22,8 +22,8 @@
import notification from '@anticrm/notification'
import { getClient, UserBoxList } from '@anticrm/presentation'
import { Button, Component, EditBox, IconEdit, Label, numberToHexColor, showPopup } from '@anticrm/ui'
import { ContextMenu } from '@anticrm/view-resources'
import board from '../plugin'
import CardInlineActions from './editor/CardInlineActions.svelte'
import CardLabels from './editor/CardLabels.svelte'
import DatePresenter from './presenters/DatePresenter.svelte'
import { hasDate, openCardPanel, updateCard, updateCardMembers } from '../utils/CardUtils'
@ -44,12 +44,7 @@
function enterEditMode (): void {
isEditMode = true
showPopup(
CardInlineActions,
{ value: object },
getElementPopupAlignment(ref, { h: 'right', v: 'top' }),
exitEditMode
)
showPopup(ContextMenu, { object }, getElementPopupAlignment(ref, { h: 'right', v: 'top' }), exitEditMode)
}
function showCard () {

View File

@ -23,6 +23,7 @@
import { showPopup } from '@anticrm/ui'
import {
ActionContext,
ContextMenu,
focusStore,
ListSelectionProvider,
SelectDirection,
@ -30,7 +31,6 @@
} from '@anticrm/view-resources'
import { onMount } from 'svelte'
import AddCard from './add-card/AddCard.svelte'
import CardInlineActions from './editor/CardInlineActions.svelte'
import KanbanCard from './KanbanCard.svelte'
import AddPanel from './AddPanel.svelte'
import ListHeader from './ListHeader.svelte'
@ -94,8 +94,8 @@
}
showPopup(
CardInlineActions,
{ value: object },
ContextMenu,
{ object },
{
getBoundingClientRect: () => DOMRect.fromRect({ width: 1, height: 1, x: ev.clientX, y: ev.clientY })
}

View File

@ -14,67 +14,26 @@
// limitations under the License.
-->
<script lang="ts">
import board from '@anticrm/board'
import type { Card, CardAction } from '@anticrm/board'
import { IntlString, getResource } from '@anticrm/platform'
import type { Card } from '@anticrm/board'
import { getClient } from '@anticrm/presentation'
import { Button, Component, Label } from '@anticrm/ui'
import { Button, IconAttachment, Label, showPopup } from '@anticrm/ui'
import type { Action } from '@anticrm/view'
import { getActions, invokeAction } from '@anticrm/view-resources'
import AddChecklist from '../popups/AddChecklist.svelte'
import AttachmentPicker from '../popups/AttachmentPicker.svelte'
import plugin from '../../plugin'
import { cardActionSorter, getCardActions } from '../../utils/CardActionUtils'
import { getPopupAlignment } from '../../utils/PopupUtils'
export let value: Card
const client = getClient()
let actionGroups: { label: IntlString; actions: CardAction[] }[] = []
let topActions: Action[] = []
let toolsActions: Action[] = []
async function fetch () {
const suggestedActions: CardAction[] = []
const addToCardActions: CardAction[] = []
const automationActions: CardAction[] = []
const actions: CardAction[] = []
const result = await getCardActions(client)
for (const action of result) {
let supported = true
if (action.supported) {
const supportedHandler = await getResource(action.supported)
supported = supportedHandler(value, client)
}
if (supported) {
if (action.type === board.cardActionType.Suggested) {
suggestedActions.push(action)
} else if (action.type === board.cardActionType.Cover) {
addToCardActions.push(action)
} else if (action.type === board.cardActionType.AddToCard) {
addToCardActions.push(action)
} else if (action.type === board.cardActionType.Automation) {
automationActions.push(action)
} else if (action.type === board.cardActionType.Action) {
actions.push(action)
}
}
}
actionGroups = [
{
label: plugin.string.Suggested,
actions: suggestedActions.sort(cardActionSorter)
},
{
label: plugin.string.AddToCard,
actions: addToCardActions.sort(cardActionSorter)
},
{
label: plugin.string.Automation,
actions: automationActions.sort(cardActionSorter)
},
{
label: plugin.string.Actions,
actions: actions.sort(cardActionSorter)
}
]
const result = await getActions(client, value, value._class)
topActions = result.filter((action) => action.context.group === 'top')
toolsActions = result.filter((action) => action.context.group !== 'top')
}
fetch()
$: value.members && fetch()
$: value.isArchived && fetch()
@ -83,32 +42,51 @@
{#if value}
<div class="flex-col flex-gap-3">
{#each actionGroups as group}
{#if group.actions.length > 0}
<div class="flex-col flex-gap-1">
<Label label={group.label} />
{#each group.actions as action}
{#if action.component}
<Component is={action.component} props={{ object: value }}>
<slot />
</Component>
{:else}
<Button
icon={action.icon}
label={action.label}
kind={action.kind ?? 'no-border'}
justify="left"
on:click={async (e) => {
if (action.handler) {
const handler = await getResource(action.handler)
handler(value, client, e)
}
}}
/>
{/if}
{/each}
</div>
{/if}
{/each}
<div class="flex-col flex-gap-1">
<Label label={plugin.string.AddToCard} />
{#each topActions as action}
<Button
icon={action.icon}
label={action.label}
kind="no-border"
justify="left"
on:click={(e) => {
invokeAction(value, e, action.action, action.actionProps)
}}
/>
{/each}
<Button
icon={plugin.icon.Card}
label={plugin.string.Checklist}
kind="no-border"
justify="left"
on:click={(e) => {
showPopup(AddChecklist, { value }, getPopupAlignment(e))
}}
/>
<Button
icon={IconAttachment}
label={plugin.string.Attachments}
kind="no-border"
justify="left"
on:click={(e) => {
showPopup(AttachmentPicker, { value }, getPopupAlignment(e))
}}
/>
</div>
<div class="flex-col flex-gap-1">
<Label label={plugin.string.Actions} />
{#each toolsActions as action}
<Button
icon={action.icon}
label={action.label}
kind="no-border"
justify="left"
on:click={(e) => {
invokeAction(value, e, action.action, action.actionProps)
}}
/>
{/each}
</div>
</div>
{/if}

View File

@ -16,29 +16,24 @@
<script lang="ts">
import attachment, { Attachment } from '@anticrm/attachment'
import type { Card } from '@anticrm/board'
import { getResource } from '@anticrm/platform'
import { getClient } from '@anticrm/presentation'
import { Button, Icon, IconAttachment, Label } from '@anticrm/ui'
import { Button, Icon, IconAttachment, Label, showPopup } from '@anticrm/ui'
import AttachmentPicker from '../popups/AttachmentPicker.svelte'
import AttachmentPresenter from '../presenters/AttachmentPresenter.svelte'
import board from '../../plugin'
import { getPopupAlignment } from '../../utils/PopupUtils'
export let value: Card
const client = getClient()
let attachments: Attachment[] = []
let addAttachment: (e: Event) => void
async function fetch () {
attachments = await client.findAll(attachment.class.Attachment, { space: value.space, attachedTo: value._id })
}
client.findOne(board.class.CardAction, { _id: board.cardAction.Attachments }).then(async (action) => {
if (action && action.handler) {
const handler = await getResource(action.handler)
addAttachment = (e) => {
handler(value, client, e)
}
}
})
function addAttachment (e: Event) {
showPopup(AttachmentPicker, { object: value }, getPopupAlignment(e))
}
$: value?.attachments && value.attachments > 0 && fetch()
</script>

View File

@ -92,7 +92,7 @@
}
function showItemMenu (item: TodoItem, e?: Event) {
showPopup(ContextMenu, { object: item }, getPopupAlignment(e))
showPopup(ContextMenu, { object: item, baseMenuClass: board.class.Card }, getPopupAlignment(e))
}
$: checklistItemsQuery.query(task.class.TodoItem, { space: value.space, attachedTo: value._id }, (result) => {

View File

@ -16,13 +16,15 @@
import type { Card, CardDate } from '@anticrm/board'
import contact, { Employee } from '@anticrm/contact'
import { getResource } from '@anticrm/platform'
import { createQuery, getClient } from '@anticrm/presentation'
import { Button, IconAdd, Label } from '@anticrm/ui'
import { Ref } from '@anticrm/core'
import { createQuery, getClient, UsersPopup } from '@anticrm/presentation'
import { Button, IconAdd, Label, showPopup } from '@anticrm/ui'
import { invokeAction } from '@anticrm/view-resources'
import board from '../../plugin'
import { getCardActions } from '../../utils/CardActionUtils'
import { hasDate, updateCardMembers } from '../../utils/CardUtils'
import { getPopupAlignment } from '../../utils/PopupUtils'
import DatePresenter from '../presenters/DatePresenter.svelte'
import MemberPresenter from '../presenters/MemberPresenter.svelte'
import CardLabels from './CardLabels.svelte'
@ -30,20 +32,30 @@
export let value: Card
const query = createQuery()
const client = getClient()
let members: Employee[]
let membersHandler: (e: Event) => void
let members: Employee[] = []
const membersHandler = (e?: Event) => {
showPopup(
UsersPopup,
{
_class: contact.class.Employee,
multiSelect: true,
allowDeselect: true,
selectedUsers: members?.map((m) => m._id) ?? [],
placeholder: board.string.SearchMembers
},
getPopupAlignment(e),
undefined,
(result: Array<Ref<Employee>>) => {
updateCardMembers(value, client, result)
}
)
}
let dateHandler: (e: Event) => void
$: membersIds = members?.map((m) => m._id) ?? []
const getMenuItems = (member: Employee) => {
return [
[
{
title: board.string.ViewProfile,
handler: () => console.log('TODO: implement')
}
],
[
{
title: board.string.RemoveFromCard,
@ -56,29 +68,20 @@
]
}
$: if (value.members && value.members.length > 0) {
query.query(contact.class.Employee, { _id: { $in: value.members } }, (result) => {
members = result
})
} else {
members = []
}
$: query.query(contact.class.Employee, { _id: { $in: value.members } }, (result) => {
members = result
})
function updateDate (e: CustomEvent<CardDate>) {
client.update(value, { date: e.detail })
}
getCardActions(client, {
_id: { $in: [board.cardAction.Dates, board.cardAction.Members] }
_id: { $in: [board.action.Dates] }
}).then(async (result) => {
for (const action of result) {
if (action.handler) {
const handler = await getResource(action.handler)
if (action._id === board.cardAction.Dates) {
dateHandler = (e) => handler(value, client, e)
} else if (action._id === board.cardAction.Members) {
membersHandler = (e) => handler(value, client, e)
}
if (action._id === board.action.Dates) {
dateHandler = (e: Event) => invokeAction(value, e, action.action, action.actionProps)
}
}
})

View File

@ -1,80 +0,0 @@
<!--
// Copyright © 2022 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import type { Card, CardAction } from '@anticrm/board'
import { getResource } from '@anticrm/platform'
import { getClient } from '@anticrm/presentation'
import { Button, Component } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte'
import board from '../../plugin'
import { cardActionSorter, getCardActions } from '../../utils/CardActionUtils'
import { openCardPanel } from '../../utils/CardUtils'
export let value: Card
const client = getClient()
const dispatch = createEventDispatcher()
let actions: CardAction[] = []
function openCard () {
openCardPanel(value)
dispatch('close')
}
async function fetch () {
actions = []
const result = await getCardActions(client, { isInline: true })
for (const action of result) {
if (!action.supported) {
actions.push(action)
} else {
const supportedHandler = await getResource(action.supported)
if (supportedHandler(value, client)) {
actions.push(action)
}
}
}
actions = actions.sort(cardActionSorter)
}
fetch()
</script>
{#if value && !value.isArchived}
<div class="flex-col flex-gap-1">
<Button icon={board.icon.Card} label={board.string.OpenCard} kind="no-border" justify="left" on:click={openCard} />
{#each actions as action}
{#if action.component}
<Component is={action.component} props={{ object: value, isInline: true }}>
<slot />
</Component>
{:else}
<Button
icon={action.icon}
label={action.label}
kind="no-border"
justify="left"
on:click={async (e) => {
if (action.handler) {
const handler = await getResource(action.handler)
handler(value, client, e)
}
}}
/>
{/if}
{/each}
</div>
{/if}

View File

@ -15,10 +15,10 @@
<script lang="ts">
import type { Card, CardLabel, LabelsCompactMode } from '@anticrm/board'
import { getResource } from '@anticrm/platform'
import preference from '@anticrm/preference'
import { createQuery, getClient } from '@anticrm/presentation'
import { Button, IconAdd } from '@anticrm/ui'
import { invokeAction } from '@anticrm/view-resources'
import board from '../../plugin'
import { getCardActions } from '../../utils/CardActionUtils'
@ -50,11 +50,10 @@
if (!isInline) {
getCardActions(client, {
_id: board.cardAction.Labels
_id: board.action.Labels
}).then(async (result) => {
if (result?.[0]?.handler) {
const handler = await getResource(result[0].handler)
labelsHandler = (e: Event) => handler(value, client, e)
if (result?.[0]) {
labelsHandler = (e: Event) => invokeAction(value, e, result[0].action, result[0].actionProps)
}
})
}

View File

@ -11,7 +11,7 @@
import board from '../../plugin'
export let object: Card
export let value: Card
const noneListItem: ListItem = {
_id: 'none',
@ -43,9 +43,9 @@
const items: TodoItem[] = template ? await client.findAll(task.class.TodoItem, { attachedTo: template._id }) : []
const checklistRef = await client.addCollection(
task.class.TodoItem,
object.space,
object._id,
object._class,
value.space,
value._id,
value._class,
'todoItems',
{
name,
@ -57,7 +57,7 @@
if (items.length > 0) {
await Promise.all(
items.map((item) =>
client.addCollection(task.class.TodoItem, object.space, checklistRef, task.class.TodoItem, 'items', {
client.addCollection(task.class.TodoItem, value.space, checklistRef, task.class.TodoItem, 'items', {
name: item.name,
dueTo: item.dueTo,
done: item.done,

View File

@ -6,7 +6,7 @@
import board from '../../plugin'
export let object: Card
export let value: Card
let inputFile: HTMLInputElement
let loading: number = 0
@ -36,9 +36,9 @@
<AddAttachment
bind:inputFile
bind:loading
objectClass={object._class}
objectId={object._id}
space={object.space}
objectClass={value._class}
objectId={value._id}
space={value.space}
on:attached={onAttached}
>
<svelte:fragment slot="control" let:click>

View File

@ -6,9 +6,9 @@
import board from '../../plugin'
import { getBoardAvailableColors } from '../../utils/BoardUtils'
import ColorPresenter from '../presenters/ColorPresenter.svelte'
export let object: Card
export let value: Card
let cover = object.cover
let cover = value.cover
const dispatch = createEventDispatcher()
const client = getClient()
@ -19,7 +19,7 @@
function updateCover (newCover?: Partial<CardCover>) {
cover = newCover ? { size: 'small', ...cover, ...newCover } : null
client.update(object, { cover })
client.update(value, { cover })
}
</script>

View File

@ -3,7 +3,7 @@
import CardLabelsEditor from './CardLabelsEditor.svelte'
import CardLabelsPicker from './CardLabelsPicker.svelte'
export let object: Card
export let value: Card
let editMode: {
isEdit?: boolean
@ -19,7 +19,7 @@
{#if editMode.isEdit}
<CardLabelsEditor
on:close
boardRef={object.space}
boardRef={value.space}
object={editMode.object}
onBack={() => setEditMode(false, undefined)}
/>
@ -27,7 +27,7 @@
<CardLabelsPicker
bind:search
on:close
{object}
object={value}
onCreate={() => setEditMode(true, undefined)}
onEdit={(o) => setEditMode(true, o)}
/>

View File

@ -3,30 +3,30 @@
import { Label, Button, Status as StatusControl, TextArea } from '@anticrm/ui'
import { Class, Client, Doc, Ref } from '@anticrm/core'
import { getResource, OK, Resource, Status } from '@anticrm/platform'
import { getClient } from '@anticrm/presentation'
import { Card } from '@anticrm/board'
import view from '@anticrm/view'
import board from '../../plugin'
import SpaceSelect from '../selectors/SpaceSelect.svelte'
import StateSelect from '../selectors/StateSelect.svelte'
import RankSelect from '../selectors/RankSelect.svelte'
import type { TxOperations } from '@anticrm/core'
import { generateId, AttachedData } from '@anticrm/core'
import task from '@anticrm/task'
import { createMissingLabels } from '../../utils/BoardUtils'
export let object: Card
export let client: TxOperations
export let value: Card
const client = getClient()
const hierarchy = client.getHierarchy()
const dispatch = createEventDispatcher()
let inputRef: TextArea
let title = object.title
let title = value.title
let status: Status = OK
const selected = {
space: object.space,
state: object.state,
rank: object.rank
space: value.space,
state: value.state,
rank: value.rank
}
async function copyCard (): Promise<void> {
@ -40,9 +40,9 @@
const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true)
const labels =
object.space !== selected.space ? await createMissingLabels(client, object, selected.space) : object.labels
value.space !== selected.space ? await createMissingLabels(client, value, selected.space) : value.labels
const value: AttachedData<Card> = {
const copy: AttachedData<Card> = {
state: selected.state,
doneState: null,
number: (incResult as any).object.sequence,
@ -61,7 +61,7 @@
selected.space,
board.class.Board,
'cards',
value,
copy,
newCardId
)
dispatch('close')
@ -71,7 +71,7 @@
action: Resource<<T extends Doc>(doc: T, client: Client) => Promise<Status>>
): Promise<Status> {
const impl = await getResource(action)
return await impl(object, client)
return await impl(value, client)
}
async function validate (doc: Doc, _class: Ref<Class<Doc>>): Promise<void> {
@ -86,7 +86,7 @@
}
}
$: validate({ ...object, ...selected }, object._class)
$: validate({ ...value, ...selected }, value._class)
onMount(() => inputRef.focus())
</script>
@ -109,20 +109,20 @@
</div>
<div class="ap-category">
<div class="categoryItem w-full border-radius-2 p-2 background-button-bg-enabled">
<SpaceSelect label={board.string.Board} {object} bind:selected={selected.space} />
<SpaceSelect label={board.string.Board} object={value} bind:selected={selected.space} />
</div>
</div>
<div class="ap-category flex-gap-3">
<div class="categoryItem w-full border-radius-2 p-2 background-button-bg-enabled">
{#key selected.space}
<StateSelect label={board.string.List} {object} space={selected.space} bind:selected={selected.state} />
<StateSelect label={board.string.List} object={value} space={selected.space} bind:selected={selected.state} />
{/key}
</div>
<div class="categoryItem w-full border-radius-2 p-2 background-button-bg-enabled">
{#key selected.state}
<RankSelect
label={board.string.Position}
{object}
object={value}
state={selected.state}
bind:selected={selected.rank}
isCopying={true}

View File

@ -6,29 +6,29 @@
import board from '../../plugin'
import { getClient } from '@anticrm/presentation'
export let object: Card
export let value: Card
const client = getClient()
const dispatch = createEventDispatcher()
let startDate = object.date?.startDate
let savedStartDate = object.date?.startDate ?? Date.now()
let startDate = value.date?.startDate
let savedStartDate = value.date?.startDate ?? Date.now()
let startDateEnabled = startDate !== undefined
$: startDate && (savedStartDate = startDate)
let dueDate = object.date?.dueDate
let savedDueDate = object.date?.dueDate ?? Date.now()
let dueDate = value.date?.dueDate
let savedDueDate = value.date?.dueDate ?? Date.now()
let dueDateEnabled = dueDate !== undefined
$: dueDate && (savedDueDate = dueDate)
function getEmptyDate (): CardDate {
return { _class: object.date?._class ?? board.class.CardDate }
return { _class: value.date?._class ?? board.class.CardDate }
}
function update () {
const date: CardDate = getEmptyDate()
if (startDate !== undefined) date.startDate = startDate
if (dueDate !== undefined) date.dueDate = dueDate
client.update(object, { date })
client.update(value, { date })
}
</script>
@ -82,7 +82,7 @@
label={board.string.Remove}
size={'small'}
on:click={() => {
client.update(object, { date: getEmptyDate() })
client.update(value, { date: getEmptyDate() })
dispatch('close')
}}
/>
@ -96,7 +96,7 @@
}}
/>
<div class="flex-center mr-2">
<Component is={calendar.component.DocReminder} props={{ value: object }} />
<Component is={calendar.component.DocReminder} props={{ value }} />
</div>
</div>
</div>

View File

@ -12,29 +12,29 @@
import RankSelect from '../selectors/RankSelect.svelte'
import { createMissingLabels } from '../../utils/BoardUtils'
export let object: Card
export let value: Card
const client = getClient()
const hierarchy = client.getHierarchy()
const dispatch = createEventDispatcher()
let status: Status = OK
const selected = {
space: object.space,
state: object.state,
rank: object.rank
space: value.space,
state: value.state,
rank: value.rank
}
async function move (): Promise<void> {
const update: DocumentUpdate<Card> = {}
if (selected.space !== object.space) {
update.labels = await createMissingLabels(client, object, selected.space)
if (selected.space !== value.space) {
update.labels = await createMissingLabels(client, value, selected.space)
update.space = selected.space
}
if (selected.state !== object.state) update.state = selected.state
if (selected.rank !== object.rank) update.rank = selected.rank
client.update(object, update)
if (selected.state !== value.state) update.state = selected.state
if (selected.rank !== value.rank) update.rank = selected.rank
client.update(value, update)
dispatch('close')
}
@ -42,7 +42,7 @@
action: Resource<<T extends Doc>(doc: T, client: Client) => Promise<Status>>
): Promise<Status> {
const impl = await getResource(action)
return await impl(object, client)
return await impl(value, client)
}
async function validate (doc: Doc, _class: Ref<Class<Doc>>): Promise<void> {
@ -57,7 +57,7 @@
}
}
$: validate({ ...object, ...selected }, object._class)
$: validate({ ...value, ...selected }, value._class)
</script>
<div class="antiPopup antiPopup-withHeader antiPopup-withTitle antiPopup-withCategory w-85">
@ -72,18 +72,18 @@
</div>
<div class="ap-category">
<div class="categoryItem w-full border-radius-2 p-2 background-button-bg-enabled">
<SpaceSelect label={board.string.Board} {object} bind:selected={selected.space} />
<SpaceSelect label={board.string.Board} object={value} bind:selected={selected.space} />
</div>
</div>
<div class="ap-category flex-gap-3">
<div class="categoryItem w-full border-radius-2 p-2 background-button-bg-enabled">
{#key selected.space}
<StateSelect label={board.string.List} {object} space={selected.space} bind:selected={selected.state} />
<StateSelect label={board.string.List} object={value} space={selected.space} bind:selected={selected.state} />
{/key}
</div>
<div class="categoryItem w-full border-radius-2 p-2 background-button-bg-enabled">
{#key selected.state}
<RankSelect label={board.string.Position} {object} state={selected.state} bind:selected={selected.rank} />
<RankSelect label={board.string.Position} object={value} state={selected.state} bind:selected={selected.rank} />
{/key}
</div>
</div>
@ -98,7 +98,7 @@
<Button
label={board.string.Move}
size={'small'}
disabled={status !== OK || (object.state === selected.state && object.rank === selected.rank)}
disabled={status !== OK || (value.state === selected.state && value.rank === selected.rank)}
kind={'primary'}
on:click={move}
/>

View File

@ -1,46 +0,0 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { Label, Button, ActionIcon, IconClose } from '@anticrm/ui'
import board from '../../plugin'
import { getClient } from '@anticrm/presentation'
import { Card } from '@anticrm/board'
export let object: Card
const client = getClient()
const dispatch = createEventDispatcher()
</script>
<div class="antiPopup antiPopup-withHeader antiPopup-withTitle antiPopup-withCategory w-85">
<div class="ap-space" />
<div class="flex-row-center header">
<div class="flex-center flex-grow">
<Label label={board.string.Delete} />
</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-box ml-4 mr-4 mt-4">
<Label label={board.string.DeleteCard} />
</div>
<div class="ap-footer">
<Button
size={'small'}
width="100%"
label={board.string.Delete}
kind={'dangerous'}
on:click={() => {
client.remove(object)
dispatch('close')
}}
/>
</div>
</div>

View File

@ -13,12 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
import type { Card } from '@anticrm/board'
import contact, { Employee } from '@anticrm/contact'
import type { TxOperations as Client, Ref } from '@anticrm/core'
import { Resources } from '@anticrm/platform'
import { UsersPopup } from '@anticrm/presentation'
import { showPopup } from '@anticrm/ui'
import BoardPresenter from './components/BoardPresenter.svelte'
import CardPresenter from './components/CardPresenter.svelte'
@ -27,11 +22,8 @@ import CreateCard from './components/CreateCard.svelte'
import EditCard from './components/EditCard.svelte'
import KanbanCard from './components/KanbanCard.svelte'
import KanbanView from './components/KanbanView.svelte'
import AddChecklist from './components/popups/AddChecklist.svelte'
import AttachmentPicker from './components/popups/AttachmentPicker.svelte'
import CardLabelsPopup from './components/popups/CardLabelsPopup.svelte'
import MoveCard from './components/popups/MoveCard.svelte'
import DeleteCard from './components/popups/RemoveCard.svelte'
import CopyCard from './components/popups/CopyCard.svelte'
import DateRangePicker from './components/popups/DateRangePicker.svelte'
import CardDatePresenter from './components/presenters/DatePresenter.svelte'
@ -44,69 +36,8 @@ import Archive from './components/Archive.svelte'
import TableView from './components/TableView.svelte'
import UserBoxList from './components/UserBoxList.svelte'
import CardLabels from './components/editor/CardLabels.svelte'
import board from './plugin'
import {
addCurrentUser,
canAddCurrentUser,
isArchived,
isUnarchived,
archiveCard,
unarchiveCard,
updateCardMembers
} from './utils/CardUtils'
import { getPopupAlignment } from './utils/PopupUtils'
import CardCoverEditor from './components/popups/CardCoverEditor.svelte'
async function showMoveCardPopup (object: Card, client: Client, e?: Event): Promise<void> {
showPopup(MoveCard, { object }, getPopupAlignment(e))
}
async function showDeleteCardPopup (object: Card, client: Client, e?: Event): Promise<void> {
showPopup(DeleteCard, { object }, getPopupAlignment(e))
}
async function showCopyCardPopup (object: Card, client: Client, e?: Event): Promise<void> {
showPopup(CopyCard, { object, client }, getPopupAlignment(e))
}
async function showDatePickerPopup (object: Card, client: Client, e?: Event): Promise<void> {
showPopup(DateRangePicker, { object }, getPopupAlignment(e))
}
async function showCardLabelsPopup (object: Card, client: Client, e?: Event): Promise<void> {
showPopup(CardLabelsPopup, { object }, getPopupAlignment(e))
}
async function showChecklistsPopup (object: Card, client: Client, e?: Event): Promise<void> {
showPopup(AddChecklist, { object }, getPopupAlignment(e))
}
async function showEditMembersPopup (object: Card, client: Client, e?: Event): Promise<void> {
showPopup(
UsersPopup,
{
_class: contact.class.Employee,
multiSelect: true,
allowDeselect: true,
selectedUsers: object?.members ?? [],
placeholder: board.string.SearchMembers
},
getPopupAlignment(e),
undefined,
(result: Array<Ref<Employee>>) => {
updateCardMembers(object, client, result)
}
)
}
async function showAttachmentsPopup (object: Card, client: Client, e?: Event): Promise<void> {
showPopup(AttachmentPicker, { object }, getPopupAlignment(e))
}
async function showCoverPopup (object: Card, client: Client, e?: Event): Promise<void> {
showPopup(CardCoverEditor, { object }, getPopupAlignment(e))
}
export default async (): Promise<Resources> => ({
component: {
CreateBoard,
@ -125,26 +56,12 @@ export default async (): Promise<Resources> => ({
MenuMainPage,
TableView,
UserBoxList,
CardLabels
},
cardActionHandler: {
Join: addCurrentUser,
Move: showMoveCardPopup,
Dates: showDatePickerPopup,
Labels: showCardLabelsPopup,
Attachments: showAttachmentsPopup,
Archive: archiveCard,
SendToBoard: unarchiveCard,
Delete: showDeleteCardPopup,
Members: showEditMembersPopup,
Checklist: showChecklistsPopup,
Copy: showCopyCardPopup,
Cover: showCoverPopup
},
cardActionSupportedHandler: {
Join: canAddCurrentUser,
Archive: isUnarchived,
SendToBoard: isArchived,
Delete: isArchived
CardLabels,
// action popups
LabelsActionPopup: CardLabelsPopup,
DatesActionPopup: DateRangePicker,
CoverActionPopup: CardCoverEditor,
MoveActionPopup: MoveCard,
CopyActionPopup: CopyCard
}
})

View File

@ -1,10 +1,7 @@
import { CardAction } from '@anticrm/board'
import type { Action } from '@anticrm/view'
import view from '@anticrm/view'
import { Client, DocumentQuery } from '@anticrm/core'
import board from '../plugin'
export const cardActionSorter = (a1: CardAction, a2: CardAction): number => a1.position - a2.position
export const getCardActions = async (client: Client, query?: DocumentQuery<CardAction>): Promise<CardAction[]> => {
return await client.findAll(board.class.CardAction, query ?? {})
export const getCardActions = async (client: Client, query?: DocumentQuery<Action>): Promise<Action[]> => {
return await client.findAll(view.class.Action, query ?? {})
}

View File

@ -15,12 +15,13 @@
//
import { Employee } from '@anticrm/contact'
import type { AttachedDoc, Class, TxOperations as Client, Doc, Markup, Ref, Timestamp, Obj } from '@anticrm/core'
import type { Asset, IntlString, Plugin, Resource } from '@anticrm/platform'
import type { AttachedDoc, Class, Doc, Markup, Ref, Timestamp, Obj } from '@anticrm/core'
import type { Asset, IntlString, Plugin } from '@anticrm/platform'
import { plugin } from '@anticrm/platform'
import type { Preference } from '@anticrm/preference'
import type { KanbanTemplateSpace, SpaceWithStates, Task } from '@anticrm/task'
import type { AnyComponent } from '@anticrm/ui'
import type { Preference } from '@anticrm/preference'
import { Action, ActionCategory } from '@anticrm/view'
/**
* @public
@ -88,21 +89,7 @@ export interface Card extends Task {
comments?: number
attachments?: number
}
/**
* @public
*/
export interface CardAction extends Doc {
component?: AnyComponent
hint?: IntlString
icon: Asset
isInline?: boolean
kind?: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'dangerous'
label: IntlString
position: number
type: string
handler?: Resource<(card: Card, client: Client, e?: Event) => void>
supported?: Resource<(card: Card, client: Client) => boolean>
}
/**
* @public
*/
@ -133,12 +120,24 @@ const boards = plugin(boardId, {
class: {
Board: '' as Ref<Class<Board>>,
Card: '' as Ref<Class<Card>>,
CardAction: '' as Ref<Class<CardAction>>,
CardDate: '' as Ref<Class<CardDate>>,
CardLabel: '' as Ref<Class<CardLabel>>,
MenuPage: '' as Ref<Class<MenuPage>>,
LabelsCompactMode: '' as Ref<Class<LabelsCompactMode>>
},
category: {
Card: '' as Ref<ActionCategory>
},
action: {
Cover: '' as Ref<Action>,
Dates: '' as Ref<Action>,
Labels: '' as Ref<Action>,
Move: '' as Ref<Action>,
Copy: '' as Ref<Action>,
Archive: '' as Ref<Action>,
SendToBoard: '' as Ref<Action>,
Delete: '' as Ref<Action>
},
icon: {
Board: '' as Asset,
Card: '' as Asset
@ -149,56 +148,6 @@ const boards = plugin(boardId, {
menuPageId: {
Main: 'main',
Archive: 'archive'
},
cardActionType: {
Suggested: 'Suggested',
Editor: 'Editor',
Cover: 'Cover',
AddToCard: 'AddToCard',
Automation: 'Automation',
Action: 'Action'
},
cardAction: {
Cover: '' as Ref<CardAction>,
Join: '' as Ref<CardAction>,
Members: '' as Ref<CardAction>,
Labels: '' as Ref<CardAction>,
Checklist: '' as Ref<CardAction>,
Dates: '' as Ref<CardAction>,
Attachments: '' as Ref<CardAction>,
CustomFields: '' as Ref<CardAction>,
AddButton: '' as Ref<CardAction>,
Move: '' as Ref<CardAction>,
Copy: '' as Ref<CardAction>,
MakeTemplate: '' as Ref<CardAction>,
Watch: '' as Ref<CardAction>,
Archive: '' as Ref<CardAction>,
SendToBoard: '' as Ref<CardAction>,
Delete: '' as Ref<CardAction>
},
cardActionHandler: {
Cover: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
Join: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
Members: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
Labels: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
Checklist: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
Dates: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
Attachments: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
CustomFields: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
AddButton: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
Move: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
Copy: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
MakeTemplate: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
Watch: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
Archive: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
SendToBoard: '' as Resource<(card: Card, client: Client, e?: Event) => void>,
Delete: '' as Resource<(card: Card, client: Client, e?: Event) => void>
},
cardActionSupportedHandler: {
Join: '' as Resource<(card: Card, client: Client) => boolean>,
Archive: '' as Resource<(card: Card, client: Client) => boolean>,
SendToBoard: '' as Resource<(card: Card, client: Client) => boolean>,
Delete: '' as Resource<(card: Card, client: Client) => boolean>
}
})

View File

@ -16,7 +16,8 @@
import type { Doc, WithLookup } from '@anticrm/core'
import core, { Class, Client, matchQuery, Ref } from '@anticrm/core'
import type { Action, ViewActionInput, ViewContextType } from '@anticrm/view'
import { getResource } from '@anticrm/platform'
import type { Action, ViewAction, ViewActionInput, ViewContextType } from '@anticrm/view'
import view from './plugin'
import { FocusSelection } from './selection'
@ -80,6 +81,16 @@ export async function getActions (
return filteredActions
}
export async function invokeAction (
object: Doc | Doc[],
evt: Event,
action: ViewAction,
props?: Record<string, any>
): Promise<void> {
const impl = await getResource(action)
await impl(Array.isArray(object) && object.length === 1 ? object[0] : object, evt, props)
}
export async function getContextActions (
client: Client,
doc: Doc | Doc[],

View File

@ -15,11 +15,9 @@
<script lang="ts">
import type { Class, Doc, Ref } from '@anticrm/core'
import type { Asset } from '@anticrm/platform'
import { getResource } from '@anticrm/platform'
import { getClient } from '@anticrm/presentation'
import { Action, Menu } from '@anticrm/ui'
import { ViewAction } from '@anticrm/view'
import { getActions } from '../actions'
import { getActions, invokeAction } from '../actions'
export let object: Doc | Doc[]
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
@ -27,10 +25,6 @@
const client = getClient()
async function invokeAction (evt: Event, action: ViewAction, props?: Record<string, any>) {
const impl = await getResource(action)
await impl(Array.isArray(object) && object.length === 1 ? object[0] : object, evt, props)
}
let loaded = 0
getActions(client, object, baseMenuClass).then((result) => {
@ -38,7 +32,7 @@
label: a.label,
icon: a.icon as Asset,
action: async (_: any, evt: Event) => {
invokeAction(evt, a.action, a.actionProps)
invokeAction(object, evt, a.action, a.actionProps)
}
}))
loaded = 1

View File

@ -47,7 +47,7 @@ import DocAttributeBar from './components/DocAttributeBar.svelte'
import ViewletSetting from './components/ViewletSetting.svelte'
import TableBrowser from './components/TableBrowser.svelte'
export { getActions } from './actions'
export { getActions, invokeAction } from './actions'
export { default as ActionContext } from './components/ActionContext.svelte'
export { default as ActionHandler } from './components/ActionHandler.svelte'
export { default as ContextMenu } from './components/Menu.svelte'