From 6b05833c8b337fbc6cc08c68eb65446a8407702e Mon Sep 17 00:00:00 2001 From: Denis Bykhov Date: Wed, 1 Feb 2023 17:24:21 +0600 Subject: [PATCH] Multiselect enums (#2573) Signed-off-by: Denis Bykhov --- models/setting/src/index.ts | 4 + models/setting/src/plugin.ts | 3 +- models/view/src/index.ts | 3 + models/view/src/plugin.ts | 1 + .../ui/src/components/DropdownLabels.svelte | 49 +++++++--- .../src/components/DropdownLabelsPopup.svelte | 43 +++++++-- .../components/typeEditors/ArrayEditor.svelte | 89 +++++++++++++++++++ plugins/setting-resources/src/index.ts | 2 + .../src/components/EnumArrayEditor.svelte | 59 ++++++++++++ .../src/components/StringPresenter.svelte | 12 ++- plugins/view-resources/src/index.ts | 4 +- 11 files changed, 246 insertions(+), 23 deletions(-) create mode 100644 plugins/setting-resources/src/components/typeEditors/ArrayEditor.svelte create mode 100644 plugins/view-resources/src/components/EnumArrayEditor.svelte diff --git a/models/setting/src/index.ts b/models/setting/src/index.ts index 606f6b56d0..62356f0a82 100644 --- a/models/setting/src/index.ts +++ b/models/setting/src/index.ts @@ -302,6 +302,10 @@ export function createModel (builder: Builder): void { editor: setting.component.EnumTypeEditor }) + builder.mixin(core.class.ArrOf, core.class.Class, view.mixin.ObjectEditor, { + editor: setting.component.ArrayEditor + }) + builder.mixin(core.class.Class, core.class.Class, view.mixin.IgnoreActions, { actions: [view.action.Delete] }) diff --git a/models/setting/src/plugin.ts b/models/setting/src/plugin.ts index 44945b2373..987572a17b 100644 --- a/models/setting/src/plugin.ts +++ b/models/setting/src/plugin.ts @@ -40,7 +40,8 @@ export default mergeIds(settingId, setting, { RefEditor: '' as AnyComponent, EnumTypeEditor: '' as AnyComponent, Owners: '' as AnyComponent, - CreateMixin: '' as AnyComponent + CreateMixin: '' as AnyComponent, + ArrayEditor: '' as AnyComponent }, category: { Settings: '' as Ref diff --git a/models/view/src/index.ts b/models/view/src/index.ts index 5778485794..eec97f03ff 100644 --- a/models/view/src/index.ts +++ b/models/view/src/index.ts @@ -383,6 +383,9 @@ export function createModel (builder: Builder): void { builder.mixin(core.class.Class, core.class.Class, view.mixin.ObjectPresenter, { presenter: view.component.ClassPresenter }) + builder.mixin(core.class.EnumOf, core.class.Class, view.mixin.ArrayEditor, { + inlineEditor: view.component.EnumArrayEditor + }) classPresenter(builder, core.class.TypeRelatedDocument, view.component.ObjectPresenter) diff --git a/models/view/src/plugin.ts b/models/view/src/plugin.ts index afc7f13e10..ebcc6fc221 100644 --- a/models/view/src/plugin.ts +++ b/models/view/src/plugin.ts @@ -60,6 +60,7 @@ export default mergeIds(viewId, view, { ClassPresenter: '' as AnyComponent, ClassRefPresenter: '' as AnyComponent, EnumEditor: '' as AnyComponent, + EnumArrayEditor: '' as AnyComponent, HTMLEditor: '' as AnyComponent, MarkupEditor: '' as AnyComponent, MarkupEditorPopup: '' as AnyComponent, diff --git a/packages/ui/src/components/DropdownLabels.svelte b/packages/ui/src/components/DropdownLabels.svelte index 976e7a4dd2..aaffcf6055 100644 --- a/packages/ui/src/components/DropdownLabels.svelte +++ b/packages/ui/src/components/DropdownLabels.svelte @@ -27,7 +27,8 @@ export let label: IntlString export let placeholder: IntlString | undefined = ui.string.SearchDots export let items: DropdownTextItem[] - export let selected: DropdownTextItem['id'] | undefined = undefined + export let multiselect = false + export let selected: DropdownTextItem['id'] | DropdownTextItem['id'][] | undefined = multiselect ? [] : undefined export let kind: ButtonKind = 'no-border' export let size: ButtonSize = 'small' @@ -41,13 +42,10 @@ let container: HTMLElement let opened: boolean = false - let isDisabled = false - $: isDisabled = items.length === 0 - let selectedItem = items.find((x) => x.id === selected) - $: selectedItem = items.find((x) => x.id === selected) + $: selectedItem = multiselect ? items.filter((p) => selected?.includes(p.id)) : items.find((x) => x.id === selected) $: if (autoSelect && selected === undefined && items[0] !== undefined) { - selected = items[0].id + selected = multiselect ? [items[0].id] : items[0].id } const dispatch = createEventDispatcher() @@ -66,19 +64,42 @@ on:click={() => { if (!opened) { opened = true - showPopup(DropdownLabelsPopup, { placeholder, items, selected }, container, (result) => { - if (result) { - selected = result - dispatch('selected', result) + showPopup( + DropdownLabelsPopup, + { placeholder, items, multiselect, selected }, + container, + (result) => { + if (result) { + selected = result + dispatch('selected', result) + } + opened = false + mgr?.setFocusPos(focusIndex) + }, + (result) => { + if (result) { + selected = result + dispatch('selected', result) + } } - opened = false - mgr?.setFocusPos(focusIndex) - }) + ) } }} > - {#if selectedItem}{selectedItem.label}{:else} diff --git a/packages/ui/src/components/DropdownLabelsPopup.svelte b/packages/ui/src/components/DropdownLabelsPopup.svelte index 7a1cd2068a..24f63a2007 100644 --- a/packages/ui/src/components/DropdownLabelsPopup.svelte +++ b/packages/ui/src/components/DropdownLabelsPopup.svelte @@ -24,7 +24,8 @@ export let placeholder: IntlString = plugin.string.SearchDots export let items: DropdownTextItem[] - export let selected: DropdownTextItem['id'] | undefined = undefined + export let selected: DropdownTextItem['id'] | DropdownTextItem['id'][] | undefined = undefined + export let multiselect: boolean = false let search: string = '' let phTraslate: string = '' @@ -45,8 +46,18 @@ async function handleSelection (evt: Event | undefined, selection: number): Promise { const item = objects[selection] - - dispatch('close', item.id) + if (multiselect && Array.isArray(selected)) { + const index = selected.indexOf(item.id) + if (index !== -1) { + selected.splice(index, 1) + selected = selected + } else { + selected = selected === undefined ? [item.id] : [...selected, item.id] + } + dispatch('update', selected) + } else { + dispatch('close', item.id) + } } function onKeydown (key: KeyboardEvent): void { @@ -71,6 +82,17 @@ dispatch('close') } } + + function isSelected ( + selected: DropdownTextItem['id'] | DropdownTextItem['id'][] | undefined, + item: DropdownTextItem + ): boolean { + if (Array.isArray(selected)) { + return selected.includes(item.id) + } else { + return item.id === selected + } + }