Board: Update actions (#1859)

Signed-off-by: Anna No <anna.no@xored.com>
This commit is contained in:
Anna No 2022-05-25 13:08:26 +07:00 committed by GitHub
parent c11ece2a64
commit 8d051a1aab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 172 additions and 62 deletions

View File

@ -280,17 +280,34 @@ export function createModel (builder: Builder): void {
)
// card actions
createAction(
builder,
{
action: view.actionImpl.ShowPanel,
actionProps: {
component: board.component.EditCard
},
label: view.string.Open,
icon: board.icon.Card,
input: 'any',
category: board.category.Card,
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'top' }
},
board.action.Open
)
createAction(
builder,
{
action: view.actionImpl.ShowPopup,
actionProps: {
component: board.component.LabelsActionPopup,
element: 'top'
element: view.popup.PositionElementAlignment
},
label: board.string.Labels,
icon: board.icon.Card,
input: 'any',
inline: true,
category: board.category.Card,
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'top' }
@ -303,11 +320,12 @@ export function createModel (builder: Builder): void {
action: view.actionImpl.ShowPopup,
actionProps: {
component: board.component.DatesActionPopup,
element: 'top'
element: view.popup.PositionElementAlignment
},
label: board.string.Dates,
icon: board.icon.Card,
input: 'any',
inline: true,
category: board.category.Card,
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'top' }
@ -320,12 +338,13 @@ export function createModel (builder: Builder): void {
action: view.actionImpl.ShowPopup,
actionProps: {
component: board.component.CoverActionPopup,
element: 'top',
element: view.popup.PositionElementAlignment,
value: 'object'
},
label: board.string.Cover,
icon: board.icon.Card,
input: 'any',
inline: true,
category: board.category.Card,
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'top' }
@ -339,12 +358,13 @@ export function createModel (builder: Builder): void {
action: view.actionImpl.ShowPopup,
actionProps: {
component: board.component.MoveActionPopup,
element: 'top'
element: view.popup.PositionElementAlignment
},
input: 'any',
inline: true,
category: board.category.Card,
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'tools' }
context: { mode: ['context', 'editor'], application: board.app.Board, group: 'tools' }
},
board.action.Move
)
@ -354,14 +374,15 @@ export function createModel (builder: Builder): void {
action: view.actionImpl.ShowPopup,
actionProps: {
component: board.component.CopyActionPopup,
element: 'top'
element: view.popup.PositionElementAlignment
},
label: board.string.Copy,
icon: board.icon.Card,
input: 'any',
inline: true,
category: board.category.Card,
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'tools' }
context: { mode: ['context', 'editor'], application: board.app.Board, group: 'tools' }
},
board.action.Copy
)
@ -381,11 +402,11 @@ export function createModel (builder: Builder): void {
isArchived: { $nin: [true] }
},
label: board.string.Archive,
icon: board.icon.Card,
icon: view.icon.Archive,
input: 'any',
category: board.category.Card,
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'tools' }
context: { mode: ['context', 'editor'], application: board.app.Board, group: 'tools' }
},
board.action.Archive
)
@ -405,7 +426,7 @@ export function createModel (builder: Builder): void {
input: 'any',
category: board.category.Card,
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'tools' }
context: { mode: ['context', 'editor'], application: board.app.Board, group: 'tools' }
},
board.action.SendToBoard
)
@ -423,7 +444,7 @@ export function createModel (builder: Builder): void {
category: board.category.Card,
input: 'any',
target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'tools' }
context: { mode: ['context', 'editor'], application: board.app.Board, group: 'tools' }
},
board.action.Delete
)

View File

@ -532,7 +532,7 @@ export function createModel (builder: Builder): void {
isArchived: { $nin: [true] }
},
label: task.string.Archive,
icon: task.icon.TaskState,
icon: view.icon.Archive,
input: 'any',
category: task.category.Task,
target: task.class.State,

View File

@ -68,7 +68,9 @@
evt.currentTarget.focus()
}}
on:click={(evt) => {
dispatch('close')
if (!action.inline) {
dispatch('close')
}
action.action(ctx, evt)
}}
>

View File

@ -1,4 +1,12 @@
import type { AnySvelteComponent, AnyComponent, PopupAlignment, PopupPositionElement, PopupOptions } from './types'
import type {
AnySvelteComponent,
AnyComponent,
HorizontalAlignment,
PopupAlignment,
PopupPositionElement,
PopupOptions,
VerticalAlignment
} from './types'
import { getResource } from '@anticrm/platform'
import { writable } from 'svelte/store'
@ -262,3 +270,29 @@ export function fitPopupElement (
export function eventToHTMLElement (evt: MouseEvent): HTMLElement {
return evt.target as HTMLElement
}
export function getEventPopupPositionElement (
e?: Event,
position?: { v: VerticalAlignment, h: HorizontalAlignment }
): PopupAlignment | undefined {
if (e == null || e.target == null) {
return undefined
}
const target = e.target as HTMLElement
return getPopupPositionElement(target, position)
}
export function getPopupPositionElement (
el: HTMLElement | undefined,
position?: { v: VerticalAlignment, h: HorizontalAlignment }
): PopupAlignment | undefined {
if (el?.getBoundingClientRect != null) {
const result = el.getBoundingClientRect()
return {
getBoundingClientRect: () => result,
position
}
}
return undefined
}

View File

@ -43,6 +43,7 @@ export interface Action {
label: IntlString
icon: Asset | AnySvelteComponent
action: (props: any, ev: Event) => Promise<void>
inline?: boolean
}
export interface IPopupItem {
@ -74,7 +75,22 @@ export interface PopupPositionElement {
h: HorizontalAlignment
}
}
export type PopupPosAlignment = 'right' | 'top' | 'float' | 'account' | 'full' | 'content' | 'middle'
export function isPopupPosAlignment (x: any): x is PopupPosAlignment {
return (
typeof x === 'string' &&
(x === 'right' ||
x === 'top' ||
x === 'float' ||
x === 'account' ||
x === 'full' ||
x === 'content' ||
x === 'middle')
)
}
export type PopupAlignment = PopupPosAlignment | PopupPositionElement | null
export type TooltipAlignment = 'top' | 'bottom' | 'left' | 'right'

View File

@ -22,13 +22,21 @@
import type { State, TodoItem } from '@anticrm/task'
import task from '@anticrm/task'
import { StyledTextBox } from '@anticrm/text-editor'
import { Button, CircleButton, EditBox, IconAdd, IconMoreH, Label, showPopup } from '@anticrm/ui'
import {
Button,
CircleButton,
EditBox,
getEventPopupPositionElement,
IconAdd,
IconMoreH,
Label,
showPopup
} from '@anticrm/ui'
import { ContextMenu, DocAttributeBar, invokeAction, UpDownNavigator } from '@anticrm/view-resources'
import { createEventDispatcher, onMount } from 'svelte'
import board from '../plugin'
import { getCardActions } from '../utils/CardActionUtils'
import { updateCard } from '../utils/CardUtils'
import { getPopupAlignment } from '../utils/PopupUtils'
import CardActions from './editor/CardActions.svelte'
import CardChecklist from './editor/CardChecklist.svelte'
import AddChecklist from './popups/AddChecklist.svelte'
@ -57,7 +65,7 @@
}
function addChecklist (e: Event) {
showPopup(AddChecklist, { value: object }, getPopupAlignment(e))
showPopup(AddChecklist, { value: object }, getEventPopupPositionElement(e))
}
$: cardQuery.query(_class, { _id }, (result) => {
@ -124,7 +132,11 @@
kind="transparent"
size="medium"
on:click={(e) => {
showPopup(ContextMenu, { object }, getPopupAlignment(e))
showPopup(
ContextMenu,
{ object, baseMenuClass: board.class.Card, mode: 'editor' },
getEventPopupPositionElement(e)
)
}}
/>
</svelte:fragment>

View File

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

View File

@ -1,5 +1,5 @@
<script lang="ts">
import { Button, Component, getPlatformColor, IconEdit, showPopup } from '@anticrm/ui'
import { Button, Component, getPlatformColor, IconMoreV, showPopup } from '@anticrm/ui'
import { State } from '@anticrm/task'
import notification from '@anticrm/notification'
import { ContextMenu } from '@anticrm/view-resources'
@ -23,7 +23,7 @@
<span class="lines-limit-2">{state.title}</span>
<div class="flex">
<Component is={notification.component.LastViewEditor} props={{ value: state }} />
<Button icon={IconEdit} kind="transparent" on:click={showMenu} />
<Button icon={IconMoreV} kind="transparent" on:click={showMenu} />
</div>
</div>
</div>

View File

@ -21,6 +21,7 @@
Button,
CheckBox,
TextAreaEditor,
getEventPopupPositionElement,
IconAdd,
IconDelete,
IconMoreH,
@ -32,7 +33,6 @@
import contact, { Employee } from '@anticrm/contact'
import board from '../../plugin'
import { getPopupAlignment } from '../../utils/PopupUtils'
import { getDateIcon } from '../../utils/BoardUtils'
export let value: TodoItem
@ -118,7 +118,7 @@
}
function showItemMenu (item: TodoItem, e?: Event) {
showPopup(ContextMenu, { object: item }, getPopupAlignment(e))
showPopup(ContextMenu, { object: item }, getEventPopupPositionElement(e))
}
$: checklistItemsQuery.query(task.class.TodoItem, { space: value.space, attachedTo: value._id }, (result) => {

View File

@ -1,28 +0,0 @@
import { PopupAlignment } from '@anticrm/ui'
import { HorizontalAlignment, VerticalAlignment } from '@anticrm/ui/src/types'
export function getPopupAlignment (
e?: Event,
position?: { v: VerticalAlignment, h: HorizontalAlignment }
): PopupAlignment | undefined {
if (e == null || e.target == null) {
return undefined
}
const target = e.target as HTMLElement
return getElementPopupAlignment(target, position)
}
export function getElementPopupAlignment (
el: HTMLElement | undefined,
position?: { v: VerticalAlignment, h: HorizontalAlignment }
): PopupAlignment | undefined {
if (el?.getBoundingClientRect != null) {
const result = el.getBoundingClientRect()
return {
getBoundingClientRect: () => result,
position
}
}
return undefined
}

View File

@ -120,6 +120,7 @@ const boards = plugin(boardId, {
Completed: '' as Ref<DoneState>
},
action: {
Open: '' as Ref<Action>,
Cover: '' as Ref<Action>,
Dates: '' as Ref<Action>,
Labels: '' as Ref<Action>,

View File

@ -1,6 +1,15 @@
import { Doc, Hierarchy } from '@anticrm/core'
import { getResource, Resource } from '@anticrm/platform'
import { getClient, MessageBox } from '@anticrm/presentation'
import { AnyComponent, closeTooltip, PopupPositionElement, showPanel, showPopup } from '@anticrm/ui'
import {
AnyComponent,
closeTooltip,
isPopupPosAlignment,
PopupAlignment,
PopupPosAlignment,
showPanel,
showPopup
} from '@anticrm/ui'
import { ViewContext } from '@anticrm/view'
import MoveView from './components/Move.svelte'
import { contextStore } from './context'
@ -119,7 +128,7 @@ function ShowPanel (
evt: Event,
props: {
component?: AnyComponent
element: PopupPositionElement
element: PopupPosAlignment
rightSection?: AnyComponent
}
): void {
@ -146,12 +155,12 @@ function ShowPanel (
* - values - all docs will be placed into
* - props - some basic props, will be merged with key, _class, value, values
*/
function ShowPopup (
async function ShowPopup (
doc: Doc | Doc[],
evt: Event,
props: {
component: AnyComponent
element: PopupPositionElement
element?: PopupPosAlignment | Resource<(e?: Event) => PopupAlignment | undefined>
_id?: string
_class?: string
_space?: string
@ -159,8 +168,9 @@ function ShowPopup (
values?: string
props?: Record<string, any>
}
): void {
): Promise<void> {
const docs = Array.isArray(doc) ? doc : doc !== undefined ? [doc] : []
const element = await getPopupAlignment(props.element, evt)
evt.preventDefault()
let cprops = {
...(props?.props ?? {})
@ -178,7 +188,7 @@ function ShowPopup (
}
}
showPopup(props.component, cprops, props.element)
showPopup(props.component, cprops, element)
}
function UpdateDocument (doc: Doc | Doc[], evt: Event, props: Record<string, any>): void {
@ -212,6 +222,24 @@ function UpdateDocument (doc: Doc | Doc[], evt: Event, props: Record<string, any
}
}
async function getPopupAlignment (
element?: PopupPosAlignment | Resource<(e?: Event) => PopupAlignment | undefined>,
evt?: Event
): Promise<PopupAlignment | undefined> {
if (element === undefined) {
return undefined
}
if (isPopupPosAlignment(element)) {
return element
}
try {
const alignmentGetter: (e?: Event) => PopupAlignment | undefined = await getResource(element)
return alignmentGetter(evt)
} catch (e) {
return element as PopupAlignment
}
}
/**
* @public
*/

View File

@ -17,20 +17,23 @@
import type { Asset } from '@anticrm/platform'
import { getClient } from '@anticrm/presentation'
import { Action, Menu } from '@anticrm/ui'
import type { ViewContextType } from '@anticrm/view'
import { getActions, invokeAction } from '../actions'
export let object: Doc | Doc[]
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
export let actions: Action[] = []
export let mode: ViewContextType | undefined = undefined
const client = getClient()
let loaded = 0
getActions(client, object, baseMenuClass).then((result) => {
getActions(client, object, baseMenuClass, mode).then((result) => {
actions = result.map((a) => ({
label: a.label,
icon: a.icon as Asset,
inline: a.inline,
action: async (_: any, evt: Event) => {
invokeAction(object, evt, a.action, a.actionProps)
}

View File

@ -14,6 +14,7 @@
//
import { Resources } from '@anticrm/platform'
import { getEventPopupPositionElement, PopupAlignment } from '@anticrm/ui'
import { actionImpl } from './actionImpl'
import BooleanEditor from './components/BooleanEditor.svelte'
import BooleanPresenter from './components/BooleanPresenter.svelte'
@ -51,6 +52,10 @@ import ObjectFilter from './components/filter/ObjectFilter.svelte'
import TimestampFilter from './components/filter/TimestampFilter.svelte'
import ClassPresenter from './components/ClassPresenter.svelte'
function PositionElementAlignment (e?: Event): PopupAlignment | undefined {
return getEventPopupPositionElement(e)
}
export { getActions, invokeAction } from './actions'
export { default as ActionContext } from './components/ActionContext.svelte'
export { default as ActionHandler } from './components/ActionHandler.svelte'
@ -107,5 +112,8 @@ export default async (): Promise<Resources> => ({
GithubPresenter,
YoutubePresenter,
ActionsPopup
},
popup: {
PositionElementAlignment
}
})

View File

@ -33,7 +33,7 @@ import type {
import type { Asset, IntlString, Plugin, Resource, Status } from '@anticrm/platform'
import { plugin } from '@anticrm/platform'
import type { AnyComponent, AnySvelteComponent } from '@anticrm/ui'
import { PopupPosAlignment } from '@anticrm/ui/src/types'
import { PopupAlignment, PopupPosAlignment } from '@anticrm/ui/src/types'
import type { Preference } from '@anticrm/preference'
/**
@ -212,6 +212,7 @@ export interface Action<T extends Doc = Doc, P = Record<string, any>> extends Do
// any - one or multiple objects are required
// any - any input is suitable.
input: ViewActionInput
inline?: boolean
// Focus and/or all selection document should match target class.
target: Ref<Class<Doc>>
@ -415,6 +416,9 @@ const view = plugin(viewId, {
Editor: '' as Ref<ActionCategory>,
MarkdownFormatting: '' as Ref<ActionCategory>
},
popup: {
PositionElementAlignment: '' as Resource<(e?: Event) => PopupAlignment | undefined>
},
actionImpl: {
UpdateDocument: '' as ViewAction<{
key: string
@ -430,7 +434,7 @@ const view = plugin(viewId, {
}>,
ShowPopup: '' as ViewAction<{
component: AnyComponent
element?: PopupPosAlignment
element?: PopupPosAlignment | Resource<(e?: Event) => PopupAlignment | undefined>
_id?: string
_class?: string
_space?: string