From ae09613d5fbe3329c5a28a253fd6eacc718823df Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Fri, 5 Aug 2022 14:48:31 +0700 Subject: [PATCH] Allow Mixin Add/Edit/Delete operations (#2247) Signed-off-by: Andrey Sobolev --- models/setting/src/index.ts | 74 ++++++++++++++-- models/setting/src/plugin.ts | 15 +++- packages/core/src/hierarchy.ts | 44 ++++++++-- packages/panel/src/components/Panel.svelte | 6 +- packages/ui/src/popups.ts | 5 ++ .../src/components/KanbanView.svelte | 12 +-- .../src/components/ListHeader.svelte | 12 +-- plugins/client-resources/src/connection.ts | 14 ++- .../src/components/DepartmentCard.svelte | 19 ++-- .../src/components/PersonsPresenter.svelte | 8 +- plugins/setting-assets/lang/en.json | 8 +- plugins/setting-assets/lang/ru.json | 5 +- .../src/components/ClassAttributes.svelte | 58 ++++++++---- .../src/components/ClassHierarchy.svelte | 13 ++- .../src/components/ClassSetting.svelte | 15 ++-- .../src/components/CreateMixin.svelte | 63 +++++++++++++ .../src/components/EditClassLabel.svelte | 88 +++++++++++++++++++ plugins/setting-resources/src/index.ts | 66 ++++++++++---- plugins/setting-resources/src/plugin.ts | 8 +- plugins/setting/src/index.ts | 10 ++- .../src/components/kanban/KanbanView.svelte | 15 +--- .../src/components/issues/IssuesList.svelte | 32 +++---- .../src/components/issues/KanbanView.svelte | 28 +++--- .../issues/edit/SubIssueList.svelte | 20 ++--- plugins/view-resources/src/actions.ts | 8 +- .../src/components/ClassAttributeBar.svelte | 4 +- .../src/components/DocAttributeBar.svelte | 8 +- .../src/components/EditDoc.svelte | 39 ++++++-- .../src/components/Table.svelte | 25 +++--- 29 files changed, 552 insertions(+), 170 deletions(-) create mode 100644 plugins/setting-resources/src/components/CreateMixin.svelte create mode 100644 plugins/setting-resources/src/components/EditClassLabel.svelte diff --git a/models/setting/src/index.ts b/models/setting/src/index.ts index a5404ef389..188e9712af 100644 --- a/models/setting/src/index.ts +++ b/models/setting/src/index.ts @@ -13,15 +13,23 @@ // limitations under the License. // -import { Builder, Mixin, Model } from '@anticrm/model' -import { Ref, Domain, DOMAIN_MODEL } from '@anticrm/core' -import core, { TClass, TDoc } from '@anticrm/model-core' -import setting from './plugin' -import { Editable, Integration, IntegrationType, Handler, SettingsCategory, settingId } from '@anticrm/setting' -import type { Asset, IntlString } from '@anticrm/platform' -import task from '@anticrm/task' import activity from '@anticrm/activity' -import view from '@anticrm/view' +import { Domain, DOMAIN_MODEL, Ref } from '@anticrm/core' +import { Builder, Mixin, Model } from '@anticrm/model' +import core, { TClass, TDoc } from '@anticrm/model-core' +import view, { createAction } from '@anticrm/model-view' +import type { Asset, IntlString } from '@anticrm/platform' +import { + Editable, + Handler, + Integration, + IntegrationType, + settingId, + SettingsCategory, + UserMixin +} from '@anticrm/setting' +import task from '@anticrm/task' +import setting from './plugin' import workbench from '@anticrm/model-workbench' import { AnyComponent } from '@anticrm/ui' @@ -65,8 +73,18 @@ export class TIntegrationType extends TDoc implements IntegrationType { @Mixin(setting.mixin.Editable, core.class.Class) export class TEditable extends TClass implements Editable {} +@Mixin(setting.mixin.UserMixin, core.class.Class) +export class TUserMixin extends TClass implements UserMixin {} + export function createModel (builder: Builder): void { - builder.createModel(TIntegration, TIntegrationType, TSettingsCategory, TWorkspaceSettingCategory, TEditable) + builder.createModel( + TIntegration, + TIntegrationType, + TSettingsCategory, + TWorkspaceSettingCategory, + TEditable, + TUserMixin + ) builder.createDoc( setting.class.SettingsCategory, @@ -276,6 +294,44 @@ export function createModel (builder: Builder): void { builder.mixin(core.class.EnumOf, core.class.Class, view.mixin.ObjectEditor, { editor: setting.component.EnumTypeEditor }) + + builder.mixin(core.class.Class, core.class.Class, view.mixin.IgnoreActions, { + actions: [view.action.Delete] + }) + + createAction(builder, { + action: view.actionImpl.ShowPopup, + actionProps: { + component: setting.component.CreateMixin, + fillProps: { + _object: 'value' + } + }, + label: setting.string.CreateMixin, + input: 'focus', + icon: view.icon.Pin, + category: setting.category.Settings, + target: core.class.Class, + context: { + mode: ['context', 'browser'], + group: 'edit' + } + }) + + createAction( + builder, + { + action: setting.actionImpl.DeleteMixin, + label: view.string.Delete, + icon: view.icon.Delete, + keyBinding: ['Meta + Backspace', 'Ctrl + Backspace'], + category: view.category.General, + input: 'any', + target: setting.mixin.UserMixin, + context: { mode: ['context', 'browser'], group: 'tools' } + }, + setting.action.DeleteMixin + ) } export { settingOperation } from './migration' diff --git a/models/setting/src/plugin.ts b/models/setting/src/plugin.ts index c7cdda165c..64ea04d185 100644 --- a/models/setting/src/plugin.ts +++ b/models/setting/src/plugin.ts @@ -13,12 +13,13 @@ // limitations under the License. // +import type { TxViewlet } from '@anticrm/activity' import { Doc, Ref } from '@anticrm/core' import { mergeIds } from '@anticrm/platform' import { settingId } from '@anticrm/setting' import setting from '@anticrm/setting-resources/src/plugin' -import type { TxViewlet } from '@anticrm/activity' import { AnyComponent } from '@anticrm/ui' +import { Action, ActionCategory, ViewAction } from '@anticrm/view' export default mergeIds(settingId, setting, { activity: { @@ -37,6 +38,16 @@ export default mergeIds(settingId, setting, { DateTypeEditor: '' as AnyComponent, RefEditor: '' as AnyComponent, EnumTypeEditor: '' as AnyComponent, - Owners: '' as AnyComponent + Owners: '' as AnyComponent, + CreateMixin: '' as AnyComponent + }, + category: { + Settings: '' as Ref + }, + action: { + DeleteMixin: '' as Ref + }, + actionImpl: { + DeleteMixin: '' as ViewAction> } }) diff --git a/packages/core/src/hierarchy.ts b/packages/core/src/hierarchy.ts index 98253a1922..868c34f255 100644 --- a/packages/core/src/hierarchy.ts +++ b/packages/core/src/hierarchy.ts @@ -144,8 +144,8 @@ export class Hierarchy { ) { const _id = tx.objectId as Ref this.classifiers.set(_id, TxProcessor.createDoc2Doc(tx as TxCreateDoc)) - this.addAncestors(_id) - this.addDescendant(_id) + this.updateAncestors(_id) + this.updateDescendant(_id) } else if (tx.objectClass === core.class.Attribute) { const createTx = tx as TxCreateDoc this.addAttribute(TxProcessor.createDoc2Doc(createTx)) @@ -158,6 +158,11 @@ export class Hierarchy { const doc = this.attributesById.get(updateTx.objectId) if (doc === undefined) return this.addAttribute(TxProcessor.updateDoc2Doc(doc, updateTx)) + } else if (tx.objectClass === core.class.Mixin || tx.objectClass === core.class.Class) { + const updateTx = tx as TxUpdateDoc>> + const doc = this.classifiers.get(updateTx.objectId) + if (doc === undefined) return + TxProcessor.updateDoc2Doc(doc, updateTx) } } @@ -169,6 +174,11 @@ export class Hierarchy { const map = this.attributes.get(doc.attributeOf) map?.delete(doc.name) this.attributesById.delete(removeTx.objectId) + } else if (tx.objectClass === core.class.Mixin) { + const removeTx = tx as TxRemoveDoc>> + this.updateDescendant(removeTx.objectId, false) + this.updateAncestors(removeTx.objectId, false) + this.classifiers.delete(removeTx.objectId) } } @@ -246,19 +256,28 @@ export class Hierarchy { return data } - private addDescendant (_class: Ref): void { + private updateDescendant (_class: Ref, add = true): void { const hierarchy = this.getAncestors(_class) for (const cls of hierarchy) { const list = this.descendants.get(cls) if (list === undefined) { - this.descendants.set(cls, [_class]) + if (add) { + this.descendants.set(cls, [_class]) + } } else { - list.push(_class) + if (add) { + list.push(_class) + } else { + const pos = list.indexOf(_class) + if (pos !== -1) { + list.splice(pos, 1) + } + } } } } - private addAncestors (_class: Ref): void { + private updateAncestors (_class: Ref, add = true): void { const cl: Ref[] = [_class] const visited = new Set>() while (cl.length > 0) { @@ -266,9 +285,18 @@ export class Hierarchy { if (addNew(visited, classifier)) { const list = this.ancestors.get(_class) if (list === undefined) { - this.ancestors.set(_class, [classifier]) + if (add) { + this.ancestors.set(_class, [classifier]) + } } else { - addIf(list, classifier) + if (add) { + addIf(list, classifier) + } else { + const pos = list.indexOf(classifier) + if (pos !== -1) { + list.splice(pos, 1) + } + } } cl.push(...this.ancestorsOf(classifier)) } diff --git a/packages/panel/src/components/Panel.svelte b/packages/panel/src/components/Panel.svelte index 2c07109a48..35e08e9193 100644 --- a/packages/panel/src/components/Panel.svelte +++ b/packages/panel/src/components/Panel.svelte @@ -84,8 +84,10 @@
{#if $$slots.actions}
- -
+ {#if $$slots['actions-label']} + + {/if} +
diff --git a/packages/ui/src/popups.ts b/packages/ui/src/popups.ts index 921297d473..3cdd545800 100644 --- a/packages/ui/src/popups.ts +++ b/packages/ui/src/popups.ts @@ -325,3 +325,8 @@ export function getPopupPositionElement ( return undefined } +export function getEventPositionElement (evt: MouseEvent): PopupAlignment | undefined { + return { + getBoundingClientRect: () => DOMRect.fromRect({ width: 1, height: 1, x: evt.clientX, y: evt.clientY }) + } +} diff --git a/plugins/board-resources/src/components/KanbanView.svelte b/plugins/board-resources/src/components/KanbanView.svelte index df8b1993cc..f3ed099670 100644 --- a/plugins/board-resources/src/components/KanbanView.svelte +++ b/plugins/board-resources/src/components/KanbanView.svelte @@ -20,7 +20,7 @@ import { createQuery, getClient } from '@anticrm/presentation' import type { Kanban, SpaceWithStates, State } from '@anticrm/task' import task, { calcRank } from '@anticrm/task' - import { showPopup } from '@anticrm/ui' + import { getEventPositionElement, showPopup } from '@anticrm/ui' import { ActionContext, ContextMenu, @@ -31,8 +31,8 @@ } from '@anticrm/view-resources' import { onMount } from 'svelte' import AddCard from './add-card/AddCard.svelte' - import KanbanCard from './KanbanCard.svelte' import AddPanel from './AddPanel.svelte' + import KanbanCard from './KanbanCard.svelte' import ListHeader from './ListHeader.svelte' export let _class: Ref> @@ -93,13 +93,7 @@ return } - showPopup( - ContextMenu, - { object }, - { - getBoundingClientRect: () => DOMRect.fromRect({ width: 1, height: 1, x: ev.clientX, y: ev.clientY }) - } - ) + showPopup(ContextMenu, { object }, getEventPositionElement(ev)) } diff --git a/plugins/board-resources/src/components/ListHeader.svelte b/plugins/board-resources/src/components/ListHeader.svelte index 031b2b78da..44e6b7c9c5 100644 --- a/plugins/board-resources/src/components/ListHeader.svelte +++ b/plugins/board-resources/src/components/ListHeader.svelte @@ -1,19 +1,13 @@ diff --git a/plugins/client-resources/src/connection.ts b/plugins/client-resources/src/connection.ts index ef23c8b4c8..ad60c602ac 100644 --- a/plugins/client-resources/src/connection.ts +++ b/plugins/client-resources/src/connection.ts @@ -68,11 +68,23 @@ class Connection implements ClientConnection { this.websocket?.close() } + delay = 1 private async waitOpenConnection (): Promise { while (true) { try { - return await this.openConnection() + const conn = await this.openConnection() + this.delay = 1 + return conn } catch (err: any) { + await new Promise((resolve) => { + setTimeout(() => { + console.log(`delay ${this.delay} second`) + resolve(null) + if (this.delay !== 5) { + this.delay++ + } + }, this.delay * 1000) + }) console.log('failed to connect', err) if (err.code === UNAUTHORIZED.code) { this.onUnauthorized?.() diff --git a/plugins/hr-resources/src/components/DepartmentCard.svelte b/plugins/hr-resources/src/components/DepartmentCard.svelte index a86c864aef..0a1d3896af 100644 --- a/plugins/hr-resources/src/components/DepartmentCard.svelte +++ b/plugins/hr-resources/src/components/DepartmentCard.svelte @@ -18,7 +18,16 @@ import { Ref, WithLookup } from '@anticrm/core' import { Department, Staff } from '@anticrm/hr' import { Avatar, getClient, UsersPopup } from '@anticrm/presentation' - import { Button, closeTooltip, eventToHTMLElement, IconAdd, Label, showPanel, showPopup } from '@anticrm/ui' + import { + Button, + closeTooltip, + eventToHTMLElement, + getEventPositionElement, + IconAdd, + Label, + showPanel, + showPopup + } from '@anticrm/ui' import view from '@anticrm/view' import { Menu } from '@anticrm/view-resources' import hr from '../plugin' @@ -70,13 +79,7 @@ } function showMenu (e: MouseEvent) { - showPopup( - Menu, - { object: value, baseMenuClass: value._class }, - { - getBoundingClientRect: () => DOMRect.fromRect({ width: 1, height: 1, x: e.clientX, y: e.clientY }) - } - ) + showPopup(Menu, { object: value, baseMenuClass: value._class }, getEventPositionElement(e)) } function edit (e: MouseEvent): void { diff --git a/plugins/hr-resources/src/components/PersonsPresenter.svelte b/plugins/hr-resources/src/components/PersonsPresenter.svelte index 5f51a9f994..d11b660323 100644 --- a/plugins/hr-resources/src/components/PersonsPresenter.svelte +++ b/plugins/hr-resources/src/components/PersonsPresenter.svelte @@ -18,7 +18,7 @@ import { EmployeePresenter } from '@anticrm/contact-resources' import { WithLookup } from '@anticrm/core' import { Staff } from '@anticrm/hr' - import { closeTooltip, showPopup } from '@anticrm/ui' + import { closeTooltip, getEventPositionElement, showPopup } from '@anticrm/ui' import { ContextMenu } from '@anticrm/view-resources' import hr from '../plugin' @@ -36,11 +36,7 @@ } function showContextMenu (ev: MouseEvent, object: Employee) { - showPopup( - ContextMenu, - { object }, - { getBoundingClientRect: () => DOMRect.fromRect({ width: 1, height: 1, x: ev.clientX, y: ev.clientY }) } - ) + showPopup(ContextMenu, { object }, getEventPositionElement(ev)) } diff --git a/plugins/setting-assets/lang/en.json b/plugins/setting-assets/lang/en.json index ce7fb4f195..c4c37f3c04 100644 --- a/plugins/setting-assets/lang/en.json +++ b/plugins/setting-assets/lang/en.json @@ -35,6 +35,9 @@ "DeleteAttribute": "Delete attribute", "DeleteAttributeConfirm": "Do you want to delete this attribute?", "DeleteAttributeExistConfirm": "Do you want to delete this attribute? Data will be lost", + "DeleteMixin": "Delete Mixin", + "DeleteMixinConfirm": "Do you want to delete this mixin?", + "DeleteMixinExistConfirm": "Do you wany yo delete this mixin? Data will not be available", "Attribute": "Attribute", "Custom": "Custom", "Type": "Type", @@ -56,6 +59,9 @@ "Role": "Role", "FailedToSave": "Failed to update password", "ImportEnum": "Import enum values", - "ImportEnumCopy": "Copy enum values from clipboard" + "ImportEnumCopy": "Copy enum values from clipboard", + "CreateMixin": "Create Mixin", + "OldNames": "Old values", + "NewClassName": "Type new class name or select from previous values..." } } \ No newline at end of file diff --git a/plugins/setting-assets/lang/ru.json b/plugins/setting-assets/lang/ru.json index 9dbe88e0e5..2ae6bb4667 100644 --- a/plugins/setting-assets/lang/ru.json +++ b/plugins/setting-assets/lang/ru.json @@ -56,6 +56,9 @@ "Role": "Роль", "FailedToSave": "Не удалось обновить пароль", "ImportEnum": "Загрузить значения справочника", - "ImportEnumCopy": "Загрузить значения справочника из буфера обмена" + "ImportEnumCopy": "Загрузить значения справочника из буфера обмена", + "CreateMixin": "Создать Миксин", + "OldNames": "Предыдушие значения", + "NewClassName": "Введите новое имя класса или выберете прошлое значение..." } } \ No newline at end of file diff --git a/plugins/setting-resources/src/components/ClassAttributes.svelte b/plugins/setting-resources/src/components/ClassAttributes.svelte index 0a02afa942..81b936d60c 100644 --- a/plugins/setting-resources/src/components/ClassAttributes.svelte +++ b/plugins/setting-resources/src/components/ClassAttributes.svelte @@ -18,6 +18,7 @@ ArrOf, AttachedDoc, Class, + ClassifierKind, Collection, Doc, EnumOf, @@ -26,11 +27,14 @@ Type } from '@anticrm/core' import { IntlString } from '@anticrm/platform' - import presentation, { getClient, MessageBox } from '@anticrm/presentation' + import presentation, { createQuery, getClient, MessageBox } from '@anticrm/presentation' import { Action, + ActionIcon, CircleButton, Component, + getEventPositionElement, + Icon, IconAdd, IconDelete, IconEdit, @@ -40,14 +44,22 @@ showPopup } from '@anticrm/ui' import view from '@anticrm/view' - import setting from '../plugin' + import settings from '../plugin' import CreateAttribute from './CreateAttribute.svelte' import EditAttribute from './EditAttribute.svelte' + import EditClassLabel from './EditClassLabel.svelte' export let _class: Ref> const client = getClient() const hierarchy = client.getHierarchy() + const classQuery = createQuery() + + let clazz: Class | undefined + + $: classQuery.query(core.class.Class, { _id: _class }, (res) => { + clazz = res.shift() + }) $: attributes = getCustomAttributes(_class) function getCustomAttributes (_class: Ref>): AnyAttribute[] { @@ -73,8 +85,8 @@ showPopup( MessageBox, { - label: setting.string.DeleteAttribute, - message: exist ? setting.string.DeleteAttributeExistConfirm : setting.string.DeleteAttributeConfirm + label: settings.string.DeleteAttribute, + message: exist ? settings.string.DeleteAttributeExistConfirm : settings.string.DeleteAttributeConfirm }, 'top', async (result) => { @@ -105,14 +117,7 @@ } } ] - showPopup( - Menu, - { actions }, - { - getBoundingClientRect: () => DOMRect.fromRect({ width: 1, height: 1, x: ev.clientX, y: ev.clientY }) - }, - () => {} - ) + showPopup(Menu, { actions }, getEventPositionElement(ev), () => {}) } function getAttrType (type: Type): IntlString | undefined { @@ -133,10 +138,31 @@ const res = await client.findOne(core.class.Enum, { _id: ref }) return res?.name } + function editLabel (evt: MouseEvent): void { + showPopup(EditClassLabel, { clazz }, getEventPositionElement(evt), () => {}) + } +
+ {#if clazz?.icon} +
+ + {#if clazz.kind === ClassifierKind.MIXIN && hierarchy.hasMixin(clazz, settings.mixin.UserMixin)} + + {/if} +
+ {/if} + {#if clazz} +
-
@@ -144,17 +170,17 @@ diff --git a/plugins/setting-resources/src/components/ClassHierarchy.svelte b/plugins/setting-resources/src/components/ClassHierarchy.svelte index 7cc6a62732..e42e2ddaf7 100644 --- a/plugins/setting-resources/src/components/ClassHierarchy.svelte +++ b/plugins/setting-resources/src/components/ClassHierarchy.svelte @@ -15,8 +15,10 @@ {#each classes as cl} @@ -52,11 +57,15 @@ on:click={() => { dispatch('select', cl) }} + on:contextmenu|preventDefault|stopPropagation={(evt) => showContextMenu(evt, clazz)} >
{#if clazz.icon} -
+
+ {#if clazz.kind === ClassifierKind.MIXIN && hierarchy.hasMixin(clazz, settings.mixin.UserMixin)} + + {/if}
{/if} diff --git a/plugins/setting-resources/src/components/ClassSetting.svelte b/plugins/setting-resources/src/components/ClassSetting.svelte index 51f50560c9..b4c86ea9b2 100644 --- a/plugins/setting-resources/src/components/ClassSetting.svelte +++ b/plugins/setting-resources/src/components/ClassSetting.svelte @@ -14,7 +14,7 @@ -->
diff --git a/plugins/setting-resources/src/components/CreateMixin.svelte b/plugins/setting-resources/src/components/CreateMixin.svelte new file mode 100644 index 0000000000..deb990d6bb --- /dev/null +++ b/plugins/setting-resources/src/components/CreateMixin.svelte @@ -0,0 +1,63 @@ + + + + { + dispatch('close') + }} +> + +
+ {#if value.icon} + + {/if} +
+
+
+
+ +
+
diff --git a/plugins/setting-resources/src/components/EditClassLabel.svelte b/plugins/setting-resources/src/components/EditClassLabel.svelte new file mode 100644 index 0000000000..9521bce08d --- /dev/null +++ b/plugins/setting-resources/src/components/EditClassLabel.svelte @@ -0,0 +1,88 @@ + + + + save(getEmbeddedLabel(name))} + canSave={!(name === undefined || name.trim().length === 0)} + on:close={() => { + dispatch('close') + }} +> +
+ +
+
diff --git a/plugins/setting-resources/src/index.ts b/plugins/setting-resources/src/index.ts index 443f85523d..8d337c405b 100644 --- a/plugins/setting-resources/src/index.ts +++ b/plugins/setting-resources/src/index.ts @@ -13,28 +13,56 @@ // limitations under the License. // +import { Class, Doc, Mixin } from '@anticrm/core' import { Resources } from '@anticrm/platform' -import Profile from './components/Profile.svelte' -import Password from './components/Password.svelte' -import WorkspaceSettings from './components/WorkspaceSettings.svelte' -import Integrations from './components/Integrations.svelte' -import ManageStatuses from './components/statuses/ManageStatuses.svelte' -import Support from './components/Support.svelte' -import Privacy from './components/Privacy.svelte' -import Terms from './components/Terms.svelte' -import Settings from './components/Settings.svelte' -import ClassSetting from './components/ClassSetting.svelte' +import { getClient, MessageBox } from '@anticrm/presentation' +import { showPopup } from '@anticrm/ui' +import { deleteObject } from '@anticrm/view-resources/src/utils' import TxIntegrationDisable from './components/activity/TxIntegrationDisable.svelte' import TxIntegrationDisableReconnect from './components/activity/TxIntegrationDisableReconnect.svelte' -import StringTypeEditor from './components/typeEditors/StringTypeEditor.svelte' -import BooleanTypeEditor from './components/typeEditors/BooleanTypeEditor.svelte' -import DateTypeEditor from './components/typeEditors/DateTypeEditor.svelte' -import NumberTypeEditor from './components/typeEditors/NumberTypeEditor.svelte' -import RefEditor from './components/typeEditors/RefEditor.svelte' -import EnumTypeEditor from './components/typeEditors/EnumTypeEditor.svelte' +import ClassSetting from './components/ClassSetting.svelte' +import CreateMixin from './components/CreateMixin.svelte' import EditEnum from './components/EditEnum.svelte' import EnumSetting from './components/EnumSetting.svelte' +import Integrations from './components/Integrations.svelte' import Owners from './components/Owners.svelte' +import Password from './components/Password.svelte' +import Privacy from './components/Privacy.svelte' +import Profile from './components/Profile.svelte' +import Settings from './components/Settings.svelte' +import ManageStatuses from './components/statuses/ManageStatuses.svelte' +import Support from './components/Support.svelte' +import Terms from './components/Terms.svelte' +import BooleanTypeEditor from './components/typeEditors/BooleanTypeEditor.svelte' +import DateTypeEditor from './components/typeEditors/DateTypeEditor.svelte' +import EnumTypeEditor from './components/typeEditors/EnumTypeEditor.svelte' +import NumberTypeEditor from './components/typeEditors/NumberTypeEditor.svelte' +import RefEditor from './components/typeEditors/RefEditor.svelte' +import StringTypeEditor from './components/typeEditors/StringTypeEditor.svelte' +import WorkspaceSettings from './components/WorkspaceSettings.svelte' +import setting from './plugin' + +async function DeleteMixin (object: Mixin>): Promise { + const docs = await getClient().findAll(object._id, {}, { limit: 1 }) + + showPopup( + MessageBox, + { + label: setting.string.DeleteMixin, + message: docs.length > 0 ? setting.string.DeleteMixinExistConfirm : setting.string.DeleteMixinConfirm, + params: { count: docs.length } + }, + 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)) + } + } + } + ) +} export default async (): Promise => ({ activity: { @@ -60,6 +88,10 @@ export default async (): Promise => ({ EnumTypeEditor, EditEnum, EnumSetting, - Owners + Owners, + CreateMixin + }, + actionImpl: { + DeleteMixin } }) diff --git a/plugins/setting-resources/src/plugin.ts b/plugins/setting-resources/src/plugin.ts index 8c1198910f..58eca7b89a 100644 --- a/plugins/setting-resources/src/plugin.ts +++ b/plugins/setting-resources/src/plugin.ts @@ -30,6 +30,9 @@ export default mergeIds(settingId, setting, { DeleteAttribute: '' as IntlString, DeleteAttributeConfirm: '' as IntlString, DeleteAttributeExistConfirm: '' as IntlString, + DeleteMixin: '' as IntlString, + DeleteMixinConfirm: '' as IntlString, + DeleteMixinExistConfirm: '' as IntlString, Attribute: '' as IntlString, Attributes: '' as IntlString, Custom: '' as IntlString, @@ -50,6 +53,9 @@ export default mergeIds(settingId, setting, { Role: '' as IntlString, FailedToSave: '' as IntlString, ImportEnum: '' as IntlString, - ImportEnumCopy: '' as IntlString + ImportEnumCopy: '' as IntlString, + CreateMixin: '' as IntlString, + OldNames: '' as IntlString, + NewClassName: '' as IntlString } }) diff --git a/plugins/setting/src/index.ts b/plugins/setting/src/index.ts index ed8bdb9fb5..9f18df13e3 100644 --- a/plugins/setting/src/index.ts +++ b/plugins/setting/src/index.ts @@ -49,6 +49,13 @@ export interface Integration extends Doc { */ export interface Editable extends Class {} +/** + * @public + * + * Mixin to allow delete of Custom classes. + */ +export interface UserMixin extends Class {} + /** * @public */ @@ -84,7 +91,8 @@ export default plugin(settingId, { Owners: '' as Ref }, mixin: { - Editable: '' as Ref> + Editable: '' as Ref>, + UserMixin: '' as Ref> }, class: { SettingsCategory: '' as Ref>, diff --git a/plugins/task-resources/src/components/kanban/KanbanView.svelte b/plugins/task-resources/src/components/kanban/KanbanView.svelte index ff5ae09d4f..c306334953 100644 --- a/plugins/task-resources/src/components/kanban/KanbanView.svelte +++ b/plugins/task-resources/src/components/kanban/KanbanView.svelte @@ -20,7 +20,7 @@ import { createQuery, getClient } from '@anticrm/presentation' import type { Kanban, SpaceWithStates, State, Task } from '@anticrm/task' import task from '@anticrm/task' - import { showPopup } from '@anticrm/ui' + import { getEventPositionElement, showPopup } from '@anticrm/ui' import { ActionContext, FilterBar, @@ -82,16 +82,9 @@ const showMenu = async (ev: MouseEvent, items: Doc[]): Promise => { ev.preventDefault() - showPopup( - Menu, - { object: items, baseMenuClass }, - { - getBoundingClientRect: () => DOMRect.fromRect({ width: 1, height: 1, x: ev.clientX, y: ev.clientY }) - }, - () => { - // selection = undefined - } - ) + showPopup(Menu, { object: items, baseMenuClass }, getEventPositionElement(ev), () => { + // selection = undefined + }) } const onContent = (evt: any) => { listProvider.update(evt.detail) diff --git a/plugins/tracker-resources/src/components/issues/IssuesList.svelte b/plugins/tracker-resources/src/components/issues/IssuesList.svelte index eafd76cf67..79fe941377 100644 --- a/plugins/tracker-resources/src/components/issues/IssuesList.svelte +++ b/plugins/tracker-resources/src/components/issues/IssuesList.svelte @@ -15,18 +15,27 @@ diff --git a/plugins/view-resources/src/components/DocAttributeBar.svelte b/plugins/view-resources/src/components/DocAttributeBar.svelte index aad85a277d..c8c87a37ef 100644 --- a/plugins/view-resources/src/components/DocAttributeBar.svelte +++ b/plugins/view-resources/src/components/DocAttributeBar.svelte @@ -14,15 +14,21 @@ --> {#each mixins as mixin} - + {@const to = !hierarchy.hasMixin(mixin, setting.mixin.UserMixin) ? object._class : mixin.extends} + {/each} diff --git a/plugins/view-resources/src/components/EditDoc.svelte b/plugins/view-resources/src/components/EditDoc.svelte index 9d0d3b1722..6ef29a6a34 100644 --- a/plugins/view-resources/src/components/EditDoc.svelte +++ b/plugins/view-resources/src/components/EditDoc.svelte @@ -26,7 +26,7 @@ getClient, KeyedAttribute } from '@anticrm/presentation' - import { AnyComponent, Component } from '@anticrm/ui' + import { AnyComponent, Button, Component } from '@anticrm/ui' import view from '@anticrm/view' import { createEventDispatcher, onDestroy } from 'svelte' import { collectionsFilter, getCollectionCounter, getFiltredKeys } from '../utils' @@ -77,17 +77,22 @@ let mixins: Mixin[] = [] + let showAllMixins = false + const dispatch = createEventDispatcher() - function getMixins (parentClass: Ref>, object: Doc): void { + function getMixins (parentClass: Ref>, object: Doc, showAllMixins: boolean): void { if (object === undefined || parentClass === undefined) return const descendants = hierarchy.getDescendants(parentClass).map((p) => hierarchy.getClass(p)) mixins = descendants.filter( - (m) => m.kind === ClassifierKind.MIXIN && hierarchy.hasMixin(object, m._id) && !ignoreMixins.has(m._id) + (m) => + m.kind === ClassifierKind.MIXIN && + !ignoreMixins.has(m._id) && + (hierarchy.hasMixin(object, m._id) || showAllMixins) ) } - $: getMixins(parentClass, object) + $: getMixins(parentClass, object, showAllMixins) let ignoreKeys: string[] = [] let allowedCollections: string[] = [] @@ -239,6 +244,30 @@ +
+ +
{#if !headerLoading} {#if headerEditor !== undefined} diff --git a/plugins/view-resources/src/components/Table.svelte b/plugins/view-resources/src/components/Table.svelte index d293d797fa..aefcd7b21c 100644 --- a/plugins/view-resources/src/components/Table.svelte +++ b/plugins/view-resources/src/components/Table.svelte @@ -18,7 +18,17 @@ import { getObjectValue, SortingOrder } from '@anticrm/core' import notification from '@anticrm/notification' import { createQuery, getClient } from '@anticrm/presentation' - import { CheckBox, Component, IconDown, IconUp, Label, Loading, showPopup, Spinner } from '@anticrm/ui' + import { + CheckBox, + Component, + getEventPositionElement, + IconDown, + IconUp, + Label, + Loading, + showPopup, + Spinner + } from '@anticrm/ui' import { BuildModelKey } from '@anticrm/view' import { createEventDispatcher } from 'svelte' import { buildConfigLookup, buildModel, LoadingProps } from '../utils' @@ -103,16 +113,9 @@ checked = [] } const items = checked.length > 0 ? checked : object - showPopup( - Menu, - { object: items, baseMenuClass }, - { - getBoundingClientRect: () => DOMRect.fromRect({ width: 1, height: 1, x: ev.clientX, y: ev.clientY }) - }, - () => { - selection = undefined - } - ) + showPopup(Menu, { object: items, baseMenuClass }, getEventPositionElement(ev), () => { + selection = undefined + }) } function changeSorting (key: string): void {
-
-
-