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 { TAttribute, TClass, TDoc, TMixin, TObj, TType, TTypeString } from './core'
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 './security'
@ -36,6 +36,7 @@ export function createModel (builder: Builder): void {
TTxPutBag,
TTxMixin,
TTxUpdateDoc,
TTxRemoveDoc,
TSpace,
TSpaceWithStates,
TAccount,

View File

@ -13,12 +13,12 @@
// limitations under the License.
//
import type { IntlString } from '@anticrm/platform'
import type { Ref, Class, Space } from '@anticrm/core'
import type { IntlString, Asset, Resource } from '@anticrm/platform'
import type { Ref, Class, Space, Doc } from '@anticrm/core'
import { DOMAIN_MODEL } from '@anticrm/core'
import { Model, Mixin, Builder } from '@anticrm/model'
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'
@ -57,8 +57,21 @@ export class TViewlet extends TDoc implements Viewlet {
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 {
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, {
editor: view.component.StringEditor
@ -83,6 +96,17 @@ export function createModel (builder: Builder): void {
icon: view.icon.Kanban,
component: view.component.KanbanView
}, 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

View File

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

View File

@ -14,34 +14,39 @@
-->
<script lang="ts">
import type { IntlString, Asset } from '@anticrm/platform'
import type { AnySvelteComponent } from '@anticrm/ui'
import type { IntlString, Asset, Resource } from '@anticrm/platform'
import { getResource } from '@anticrm/platform'
import { getClient, createQuery } from '@anticrm/presentation'
import type { Ref, Class, Doc } from '@anticrm/core'
import { createEventDispatcher } from 'svelte'
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 caption: IntlString
export let object: Doc
let actions: Action[] = []
interface PopupItem {
icon: Asset | AnySvelteComponent
label: IntlString
action?: () => Promise<void>
}
const client = getClient()
getActions(client, object._class).then(result => { actions = result })
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>
<div class="flex-col popup">
{#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">
{#if typeof (action.icon) === 'string'}
<Icon icon={action.icon} size={'large'} />
{:else}
<svelte:component this={action.icon} size={'large'} />
{/if}
<Icon icon={action.icon} size={'large'} />
</div>
<div class="label"><Label label={action.label} /></div>
</div>

View File

@ -58,11 +58,12 @@
}
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 elBtn: HTMLElement = findNode(ev.target as HTMLElement, 'menuRow')
elRow.classList.add('fixed')
showPopup(Menu, {}, elBtn, (() => {
showPopup(Menu, { object }, elBtn, (() => {
elRow.classList.remove('fixed')
}))
}
@ -100,7 +101,7 @@
<div class="firstCell">
<div class="control">
<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>
<svelte:component this={attribute.presenter} value={getValue(object, attribute.key)}/>
</div>

View File

@ -13,13 +13,25 @@
// limitations under the License.
//
import type { Doc } from '@anticrm/core'
import StringEditor from './components/StringEditor.svelte'
import StringPresenter from './components/StringPresenter.svelte'
import StatePresenter from './components/StatePresenter.svelte'
import TableView from './components/TableView.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 () => ({
actionImpl: {
Delete
},
component: {
StringEditor,
StringPresenter,

View File

@ -18,6 +18,7 @@ import type { IntlString } from '@anticrm/platform'
import { getResource } from '@anticrm/platform'
import type { Ref, Class, Obj, FindOptions, Doc, Client } from '@anticrm/core'
import type { AnyComponent, AnySvelteComponent } from '@anticrm/ui'
import type { Action, ActionTarget } 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))
console.log(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.
//
import type { Plugin, Asset } from '@anticrm/platform'
import type { Plugin, Asset, Resource } from '@anticrm/platform'
import { plugin } from '@anticrm/platform'
import type { Ref, Mixin, UXObject, Space, FindOptions, Class, Doc } from '@anticrm/core'
@ -66,6 +66,21 @@ export interface Viewlet extends Doc {
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
*/
@ -80,7 +95,9 @@ export default plugin(viewId, {
},
class: {
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: {
Table: '' as Ref<ViewletDescriptor>,

View File

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

File diff suppressed because it is too large Load Diff