mirror of
https://github.com/hcengineering/platform.git
synced 2025-02-02 08:51:11 +00:00
5b6ef778f3
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
355 lines
9.3 KiB
TypeScript
355 lines
9.3 KiB
TypeScript
import { Class, Doc, DocumentQuery, Hierarchy, Ref } from '@hcengineering/core'
|
|
import { Asset, getResource, IntlString, Resource } from '@hcengineering/platform'
|
|
import { getClient, MessageBox, updateAttribute } from '@hcengineering/presentation'
|
|
import {
|
|
AnyComponent,
|
|
AnySvelteComponent,
|
|
closeTooltip,
|
|
isPopupPosAlignment,
|
|
PopupAlignment,
|
|
PopupPosAlignment,
|
|
showPanel,
|
|
showPopup
|
|
} from '@hcengineering/ui'
|
|
import { Action, ViewContext } from '@hcengineering/view'
|
|
import MoveView from './components/Move.svelte'
|
|
import { contextStore } from './context'
|
|
import view from './plugin'
|
|
import { FocusSelection, focusStore, previewDocument, SelectDirection, selectionStore } from './selection'
|
|
import { deleteObject } from './utils'
|
|
|
|
function Delete (object: Doc): void {
|
|
showPopup(
|
|
MessageBox,
|
|
{
|
|
label: view.string.DeleteObject,
|
|
message: view.string.DeleteObjectConfirm,
|
|
params: { count: Array.isArray(object) ? object.length : 1 }
|
|
},
|
|
undefined,
|
|
(result?: boolean) => {
|
|
if (result === true) {
|
|
const objs = Array.isArray(object) ? object : [object]
|
|
for (const o of objs) {
|
|
deleteObject(getClient(), o).catch((err) => console.error(err))
|
|
}
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
async function Move (object: Doc): Promise<void> {
|
|
showPopup(MoveView, { object })
|
|
}
|
|
|
|
let $focusStore: FocusSelection
|
|
focusStore.subscribe((it) => {
|
|
$focusStore = it
|
|
})
|
|
|
|
let $contextStore: ViewContext[]
|
|
contextStore.subscribe((it) => {
|
|
$contextStore = it
|
|
})
|
|
|
|
export function select (evt: Event | undefined, offset: 1 | -1 | 0, of?: Doc, direction?: SelectDirection): void {
|
|
closeTooltip()
|
|
if ($focusStore.provider?.select !== undefined) {
|
|
$focusStore.provider?.select(offset, of, direction)
|
|
evt?.preventDefault()
|
|
previewDocument.update((old) => {
|
|
if (old !== undefined) {
|
|
return $focusStore.focus
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
function SelectItem (doc: Doc | undefined, evt: Event): void {
|
|
if (doc !== undefined) {
|
|
selectionStore.update((selection) => {
|
|
const ind = selection.findIndex((it) => it._id === doc._id)
|
|
if (ind === -1) {
|
|
selection.push(doc)
|
|
} else {
|
|
selection.splice(ind, 1)
|
|
}
|
|
return selection
|
|
})
|
|
}
|
|
evt.preventDefault()
|
|
}
|
|
function SelectItemNone (doc: Doc | undefined, evt: Event): void {
|
|
selectionStore.set([])
|
|
previewDocument.set(undefined)
|
|
evt.preventDefault()
|
|
}
|
|
function SelectItemAll (doc: Doc | undefined, evt: Event): void {
|
|
const docs = $focusStore.provider?.docs() ?? []
|
|
selectionStore.set(docs)
|
|
previewDocument.set(undefined)
|
|
evt.preventDefault()
|
|
}
|
|
|
|
const MoveUp = (doc: Doc | undefined, evt: Event): void => select(evt, -1, doc, 'vertical')
|
|
const MoveDown = (doc: Doc | undefined, evt: Event): void => select(evt, 1, doc, 'vertical')
|
|
const MoveLeft = (doc: Doc | undefined, evt: Event): void => select(evt, -1, doc, 'horizontal')
|
|
const MoveRight = (doc: Doc | undefined, evt: Event): void => select(evt, 1, doc, 'horizontal')
|
|
|
|
function ShowActions (doc: Doc | Doc[] | undefined, evt: Event): void {
|
|
evt.preventDefault()
|
|
|
|
showPopup(view.component.ActionsPopup, { viewContext: $contextStore[$contextStore.length - 1] }, 'top')
|
|
}
|
|
|
|
function ShowPreview (doc: Doc | undefined, evt: Event): void {
|
|
previewDocument.update((old) => {
|
|
if (old?._id === doc?._id) {
|
|
return undefined
|
|
}
|
|
return doc
|
|
})
|
|
evt.preventDefault()
|
|
}
|
|
|
|
function Open (doc: Doc, evt: Event): void {
|
|
evt.preventDefault()
|
|
showPanel(view.component.EditDoc, doc._id, Hierarchy.mixinOrClass(doc), 'content')
|
|
}
|
|
|
|
/**
|
|
* Quick action for show panel
|
|
* Require props:
|
|
* - component - view.component.EditDoc or another component
|
|
* - element - position
|
|
* - right - some right component
|
|
*/
|
|
function ShowPanel (
|
|
doc: Doc | Doc[],
|
|
evt: Event,
|
|
props: {
|
|
component?: AnyComponent
|
|
element: PopupPosAlignment
|
|
rightSection?: AnyComponent
|
|
}
|
|
): void {
|
|
if (Array.isArray(doc)) {
|
|
console.error('Wrong show Panel parameters')
|
|
return
|
|
}
|
|
evt.preventDefault()
|
|
showPanel(
|
|
props.component ?? view.component.EditDoc,
|
|
doc._id,
|
|
Hierarchy.mixinOrClass(doc),
|
|
props.element ?? 'content',
|
|
props.rightSection
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Quick action for show popup
|
|
* Props:
|
|
* - _id - object id will be placed into
|
|
* - _class - object _class will be placed into
|
|
* - value - object itself will be placed into
|
|
* - values - all docs will be placed into
|
|
* - props - some basic props, will be merged with key, _class, value, values
|
|
*/
|
|
async function ShowPopup (
|
|
doc: Doc | Doc[],
|
|
evt: Event,
|
|
props: {
|
|
component: AnyComponent
|
|
element?: PopupPosAlignment | Resource<(e?: Event) => PopupAlignment | undefined>
|
|
_id?: string
|
|
_class?: string
|
|
_space?: string
|
|
value?: string
|
|
values?: string
|
|
props?: Record<string, any>
|
|
fillProps?: Record<string, string>
|
|
}
|
|
): Promise<void> {
|
|
const docs = Array.isArray(doc) ? doc : doc !== undefined ? [doc] : []
|
|
const element = await getPopupAlignment(props.element, evt)
|
|
evt.preventDefault()
|
|
const cprops = {
|
|
...(props?.props ?? {})
|
|
}
|
|
for (const [docKey, propKey] of Object.entries(props.fillProps ?? {})) {
|
|
for (const dv of docs) {
|
|
const dvv = (dv as any)[docKey]
|
|
if (dvv !== undefined) {
|
|
;(cprops as any)[propKey] = dvv
|
|
}
|
|
}
|
|
if (docKey === '_object') {
|
|
;(cprops as any)[propKey] = docs[0]
|
|
}
|
|
}
|
|
|
|
showPopup(props.component, cprops, element)
|
|
}
|
|
|
|
/**
|
|
* Quick action for show popup
|
|
* Props:
|
|
* - attribute - to show editor for specific attribute
|
|
* - props - some basic props, will be merged with key, _class, value, values
|
|
*/
|
|
async function ShowEditor (
|
|
doc: Doc | Doc[],
|
|
evt: Event,
|
|
props: {
|
|
element?: PopupPosAlignment | Resource<(e?: Event) => PopupAlignment | undefined>
|
|
attribute: string
|
|
props?: Record<string, any>
|
|
}
|
|
): Promise<void> {
|
|
const docs = Array.isArray(doc) ? doc : doc !== undefined ? [doc] : []
|
|
evt.preventDefault()
|
|
let cprops = {
|
|
...(props?.props ?? {})
|
|
}
|
|
if (docs.length === 1) {
|
|
const client = getClient()
|
|
const hierarchy = client.getHierarchy()
|
|
const doc: Doc = docs[0]
|
|
const attribute = hierarchy.getAttribute(doc._class, props.attribute)
|
|
|
|
const typeClass = hierarchy.getClass(attribute.type._class)
|
|
const attributeEditorMixin = hierarchy.as(typeClass, view.mixin.AttributeEditor)
|
|
|
|
if (attributeEditorMixin === undefined || attributeEditorMixin.popup === undefined) {
|
|
throw new Error(`failed to find editor popup for ${typeClass._id}`)
|
|
}
|
|
|
|
const editor: AnySvelteComponent = await getResource(attributeEditorMixin.popup)
|
|
|
|
cprops = {
|
|
...cprops,
|
|
...{
|
|
value: (doc as any)[props.attribute]
|
|
}
|
|
}
|
|
if (editor !== undefined) {
|
|
console.log('EVT', evt)
|
|
showPopup(
|
|
editor,
|
|
cprops,
|
|
{
|
|
getBoundingClientRect: () => new DOMRect((evt as MouseEvent).clientX, (evt as MouseEvent).clientY)
|
|
},
|
|
(result) => {
|
|
if (result != null) {
|
|
void updateAttribute(client, doc, doc._class, { key: props.attribute, attr: attribute }, result)
|
|
}
|
|
}
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
function UpdateDocument (doc: Doc | Doc[], evt: Event, props: Record<string, any>): void {
|
|
async function update (): Promise<void> {
|
|
if (props?.key !== undefined && props?.value !== undefined) {
|
|
if (Array.isArray(doc)) {
|
|
for (const d of doc) {
|
|
await getClient().update(d, { [props.key]: props.value })
|
|
}
|
|
} else {
|
|
await getClient().update(doc, { [props.key]: props.value })
|
|
}
|
|
}
|
|
}
|
|
if (props?.ask === true) {
|
|
showPopup(
|
|
MessageBox,
|
|
{
|
|
label: props.label ?? view.string.LabelYes,
|
|
message: props.message ?? view.string.LabelYes
|
|
},
|
|
undefined,
|
|
(result: boolean) => {
|
|
if (result) {
|
|
void update()
|
|
}
|
|
}
|
|
)
|
|
} else {
|
|
void update()
|
|
}
|
|
}
|
|
|
|
function ValueSelector (
|
|
doc: Doc | Doc[],
|
|
evt: Event,
|
|
props: {
|
|
action: Action
|
|
|
|
attribute: string
|
|
|
|
// Class object finder
|
|
_class?: Ref<Class<Doc>>
|
|
query?: DocumentQuery<Doc>
|
|
// Will copy values from selection document to query
|
|
// If set of docs passed, will do $in for values.
|
|
fillQuery?: Record<string, string>
|
|
|
|
// A list of fields with matched values to perform action.
|
|
docMatches?: string[]
|
|
searchField?: string
|
|
|
|
// Or list of values to select from
|
|
values?: Array<{ icon?: Asset, label: IntlString, id: number | string }>
|
|
|
|
placeholder?: IntlString
|
|
}
|
|
): void {
|
|
if (props.action.actionPopup !== undefined) {
|
|
showPopup(props.action.actionPopup, { ...props, ...props.action.actionProps, value: doc, width: 'large' }, 'top')
|
|
}
|
|
}
|
|
|
|
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
|
|
*/
|
|
export const actionImpl = {
|
|
Delete,
|
|
Move,
|
|
MoveUp,
|
|
MoveDown,
|
|
MoveLeft,
|
|
MoveRight,
|
|
SelectItem,
|
|
SelectItemNone,
|
|
SelectItemAll,
|
|
ShowActions,
|
|
ShowPreview,
|
|
Open,
|
|
UpdateDocument,
|
|
ShowPanel,
|
|
ShowPopup,
|
|
ShowEditor,
|
|
ValueSelector
|
|
}
|