Delete action

Signed-off-by: Andrey Platov <andrey@hardcoreeng.com>
This commit is contained in:
Andrey Platov 2021-09-25 18:48:22 +02:00
parent 55ceee2372
commit 048bb9c71f
No known key found for this signature in database
GPG Key ID: C8787EFEB4B64AF0
12 changed files with 1006 additions and 703 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@ import { Builder } from '@anticrm/model'
import core from './component' import core from './component'
import { TAttribute, TClass, TDoc, TMixin, TObj, TType, TTypeString } from './core' import { TAttribute, TClass, TDoc, TMixin, TObj, TType, TTypeString } from './core'
import { TSpace, TAccount, TState, TSpaceWithStates } from './security' import { TSpace, TAccount, TState, TSpaceWithStates } from './security'
import { TTx, TTxCreateDoc, TTxMixin, TTxUpdateDoc, TTxCUD, TTxPutBag } from './tx' import { TTx, TTxCreateDoc, TTxMixin, TTxUpdateDoc, TTxCUD, TTxPutBag, TTxRemoveDoc } from './tx'
export * from './core' export * from './core'
export * from './security' export * from './security'
@ -36,6 +36,7 @@ export function createModel (builder: Builder): void {
TTxPutBag, TTxPutBag,
TTxMixin, TTxMixin,
TTxUpdateDoc, TTxUpdateDoc,
TTxRemoveDoc,
TSpace, TSpace,
TSpaceWithStates, TSpaceWithStates,
TAccount, TAccount,

View File

@ -13,12 +13,12 @@
// limitations under the License. // limitations under the License.
// //
import type { IntlString } from '@anticrm/platform' import type { IntlString, Asset, Resource } from '@anticrm/platform'
import type { Ref, Class, Space } from '@anticrm/core' import type { Ref, Class, Space, Doc } from '@anticrm/core'
import { DOMAIN_MODEL } from '@anticrm/core' import { DOMAIN_MODEL } from '@anticrm/core'
import { Model, Mixin, Builder } from '@anticrm/model' import { Model, Mixin, Builder } from '@anticrm/model'
import type { AnyComponent } from '@anticrm/ui' import type { AnyComponent } from '@anticrm/ui'
import type { ViewletDescriptor, Viewlet, AttributeEditor, AttributePresenter, KanbanCard, ObjectEditor } from '@anticrm/view' import type { ViewletDescriptor, Viewlet, AttributeEditor, AttributePresenter, KanbanCard, ObjectEditor, Action, ActionTarget } from '@anticrm/view'
import core, { TDoc, TClass } from '@anticrm/model-core' import core, { TDoc, TClass } from '@anticrm/model-core'
@ -57,8 +57,21 @@ export class TViewlet extends TDoc implements Viewlet {
config: any config: any
} }
@Model(view.class.Action, core.class.Doc, DOMAIN_MODEL)
export class TAction extends TDoc implements Action {
label!: IntlString
icon?: Asset
action!: Resource<(doc: Doc) => Promise<void>>
}
@Model(view.class.ActionTarget, core.class.Doc, DOMAIN_MODEL)
export class TActionTarget extends TDoc implements ActionTarget {
target!: Ref<Class<Doc>>
action!: Ref<Action>
}
export function createModel (builder: Builder): void { export function createModel (builder: Builder): void {
builder.createModel(TAttributeEditor, TAttributePresenter, TKanbanCard, TObjectEditor, TViewletDescriptor, TViewlet) builder.createModel(TAttributeEditor, TAttributePresenter, TKanbanCard, TObjectEditor, TViewletDescriptor, TViewlet, TAction, TActionTarget)
builder.mixin(core.class.TypeString, core.class.Class, view.mixin.AttributeEditor, { builder.mixin(core.class.TypeString, core.class.Class, view.mixin.AttributeEditor, {
editor: view.component.StringEditor editor: view.component.StringEditor
@ -83,6 +96,17 @@ export function createModel (builder: Builder): void {
icon: view.icon.Kanban, icon: view.icon.Kanban,
component: view.component.KanbanView component: view.component.KanbanView
}, view.viewlet.Kanban) }, view.viewlet.Kanban)
builder.createDoc(view.class.Action, core.space.Model, {
label: 'Delete' as IntlString,
icon: view.icon.Kanban,
action: view.actionImpl.Delete
}, view.action.Delete)
builder.createDoc(view.class.ActionTarget, core.space.Model, {
target: core.class.Doc,
action: view.action.Delete
})
} }
export default view export default view

View File

@ -14,10 +14,18 @@
// //
import { mergeIds } from '@anticrm/platform' import { mergeIds } from '@anticrm/platform'
import type { Ref, Doc } from '@anticrm/core'
import type { Resource } from '@anticrm/platform'
import type { AnyComponent } from '@anticrm/ui' import type { AnyComponent } from '@anticrm/ui'
import view, { viewId } from '@anticrm/view' import view, { viewId, Action } from '@anticrm/view'
export default mergeIds(viewId, view, { export default mergeIds(viewId, view, {
action: {
Delete: '' as Ref<Action>
},
actionImpl: {
Delete: '' as Resource<(doc: Doc) => Promise<void>>
},
component: { component: {
StringEditor: '' as AnyComponent, StringEditor: '' as AnyComponent,
StringPresenter: '' as AnyComponent, StringPresenter: '' as AnyComponent,

View File

@ -14,34 +14,39 @@
--> -->
<script lang="ts"> <script lang="ts">
import type { IntlString, Asset } from '@anticrm/platform' import type { IntlString, Asset, Resource } from '@anticrm/platform'
import type { AnySvelteComponent } from '@anticrm/ui' import { getResource } from '@anticrm/platform'
import { getClient, createQuery } from '@anticrm/presentation'
import type { Ref, Class, Doc } from '@anticrm/core'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import { Icon, Label, IconMoreH, IconFile } from '@anticrm/ui' import { Icon, Label, IconMoreH, IconFile } from '@anticrm/ui'
import type { Action, ActionTarget } from '@anticrm/view'
import { getActions } from '../utils'
import view from '@anticrm/view'
export let title: IntlString export let object: Doc
export let caption: IntlString
let actions: Action[] = []
interface PopupItem { const client = getClient()
icon: Asset | AnySvelteComponent
label: IntlString getActions(client, object._class).then(result => { actions = result })
action?: () => Promise<void>
}
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const actions: Array<PopupItem> = [{ icon: IconFile, label: 'Application', action: async () => {} },
{ icon: IconMoreH, label: 'Options', action: async () => {} }] async function invokeAction(action: Resource<(object: Doc) => Promise<void>>) {
dispatch('close')
const impl = await getResource(action)
await impl(object)
}
</script> </script>
<div class="flex-col popup"> <div class="flex-col popup">
{#each actions as action} {#each actions as action}
<div class="flex-row-center menu-item" on:click={() => { dispatch('close') }}> <div class="flex-row-center menu-item" on:click={() => { invokeAction(action.action) }}>
<div class="icon"> <div class="icon">
{#if typeof (action.icon) === 'string'} <Icon icon={action.icon} size={'large'} />
<Icon icon={action.icon} size={'large'} />
{:else}
<svelte:component this={action.icon} size={'large'} />
{/if}
</div> </div>
<div class="label"><Label label={action.label} /></div> <div class="label"><Label label={action.label} /></div>
</div> </div>

View File

@ -58,11 +58,12 @@
} }
return false return false
} }
const showMenu = (ev: MouseEvent): void => {
const showMenu = (ev: MouseEvent, object: Doc): void => {
const elRow: HTMLElement = findNode(ev.target as HTMLElement, 'tr-body') const elRow: HTMLElement = findNode(ev.target as HTMLElement, 'tr-body')
const elBtn: HTMLElement = findNode(ev.target as HTMLElement, 'menuRow') const elBtn: HTMLElement = findNode(ev.target as HTMLElement, 'menuRow')
elRow.classList.add('fixed') elRow.classList.add('fixed')
showPopup(Menu, {}, elBtn, (() => { showPopup(Menu, { object }, elBtn, (() => {
elRow.classList.remove('fixed') elRow.classList.remove('fixed')
})) }))
} }
@ -100,7 +101,7 @@
<div class="firstCell"> <div class="firstCell">
<div class="control"> <div class="control">
<CheckBox bind:checked={checking} /> <CheckBox bind:checked={checking} />
<div class="menuRow" on:click={(ev) => showMenu(ev)}><MoreV size={'small'} /></div> <div class="menuRow" on:click={(ev) => showMenu(ev, object)}><MoreV size={'small'} /></div>
</div> </div>
<svelte:component this={attribute.presenter} value={getValue(object, attribute.key)}/> <svelte:component this={attribute.presenter} value={getValue(object, attribute.key)}/>
</div> </div>

View File

@ -13,13 +13,25 @@
// limitations under the License. // limitations under the License.
// //
import type { Doc } from '@anticrm/core'
import StringEditor from './components/StringEditor.svelte' import StringEditor from './components/StringEditor.svelte'
import StringPresenter from './components/StringPresenter.svelte' import StringPresenter from './components/StringPresenter.svelte'
import StatePresenter from './components/StatePresenter.svelte' import StatePresenter from './components/StatePresenter.svelte'
import TableView from './components/TableView.svelte' import TableView from './components/TableView.svelte'
import KanbanView from './components/KanbanView.svelte' import KanbanView from './components/KanbanView.svelte'
import { getClient } from '@anticrm/presentation'
async function Delete(object: Doc): Promise<void> {
const client = getClient()
await client.removeDoc(object._class, object.space, object._id)
}
export default async () => ({ export default async () => ({
actionImpl: {
Delete
},
component: { component: {
StringEditor, StringEditor,
StringPresenter, StringPresenter,

View File

@ -18,6 +18,7 @@ import type { IntlString } from '@anticrm/platform'
import { getResource } from '@anticrm/platform' import { getResource } from '@anticrm/platform'
import type { Ref, Class, Obj, FindOptions, Doc, Client } from '@anticrm/core' import type { Ref, Class, Obj, FindOptions, Doc, Client } from '@anticrm/core'
import type { AnyComponent, AnySvelteComponent } from '@anticrm/ui' import type { AnyComponent, AnySvelteComponent } from '@anticrm/ui'
import type { Action, ActionTarget } from '@anticrm/view'
import view from '@anticrm/view' import view from '@anticrm/view'
@ -98,4 +99,19 @@ export async function buildModel(client: Client, _class: Ref<Class<Obj>>, keys:
const model = keys.map(key => getPresenter(client, _class, key, key, options)) const model = keys.map(key => getPresenter(client, _class, key, key, options))
console.log(model) console.log(model)
return await Promise.all(model) return await Promise.all(model)
} }
function filterActions(client: Client, _class: Ref<Class<Obj>>, targets: ActionTarget[]): Ref<Action>[] {
const result: Ref<Action>[] = []
for (const target of targets) {
if (client.getHierarchy().isDerived(_class, target.target)) {
result.push(target.action)
}
}
return result
}
export async function getActions(client: Client, _class: Ref<Class<Obj>>) {
const targets = await client.findAll(view.class.ActionTarget, {})
return await client.findAll(view.class.Action, { _id: { $in: filterActions(client, _class, targets) }})
}

View File

@ -14,7 +14,7 @@
// limitations under the License. // limitations under the License.
// //
import type { Plugin, Asset } from '@anticrm/platform' import type { Plugin, Asset, Resource } from '@anticrm/platform'
import { plugin } from '@anticrm/platform' import { plugin } from '@anticrm/platform'
import type { Ref, Mixin, UXObject, Space, FindOptions, Class, Doc } from '@anticrm/core' import type { Ref, Mixin, UXObject, Space, FindOptions, Class, Doc } from '@anticrm/core'
@ -66,6 +66,21 @@ export interface Viewlet extends Doc {
config: any config: any
} }
/**
* @public
*/
export interface Action extends Doc, UXObject {
action: Resource<(doc: Doc) => Promise<void>>
}
/**
* @public
*/
export interface ActionTarget extends Doc {
target: Ref<Class<Doc>>
action: Ref<Action>
}
/** /**
* @public * @public
*/ */
@ -80,7 +95,9 @@ export default plugin(viewId, {
}, },
class: { class: {
ViewletDescriptor: '' as Ref<Class<ViewletDescriptor>>, ViewletDescriptor: '' as Ref<Class<ViewletDescriptor>>,
Viewlet: '' as Ref<Class<Viewlet>> Viewlet: '' as Ref<Class<Viewlet>>,
Action: '' as Ref<Class<Action>>,
ActionTarget: '' as Ref<Class<ActionTarget>>
}, },
viewlet: { viewlet: {
Table: '' as Ref<ViewletDescriptor>, Table: '' as Ref<ViewletDescriptor>,

View File

@ -37,8 +37,8 @@ export class FullTextIndex extends TxProcessor implements Storage {
console.log('FullTextIndex.txPutBag: Method not implemented.') console.log('FullTextIndex.txPutBag: Method not implemented.')
} }
protected txRemoveDoc (tx: TxRemoveDoc<Doc>): Promise<void> { protected override async txRemoveDoc (tx: TxRemoveDoc<Doc>): Promise<void> {
throw new Error('Method not implemented.') console.log('FullTextIndex.txRemoveDoc: Method not implemented.')
} }
protected txMixin (tx: TxMixin<Doc, Doc>): Promise<void> { protected txMixin (tx: TxMixin<Doc, Doc>): Promise<void> {

File diff suppressed because it is too large Load Diff