From 3500847fcc003fb10ad27c868f78f75ced470856 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 27 Sep 2023 09:51:18 +0200 Subject: [PATCH 1/8] us ifDefined --- .../users/components/user-input/user-input.element.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/users/components/user-input/user-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/users/users/components/user-input/user-input.element.ts index 69db7f7311..15d6f274b4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/users/components/user-input/user-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/users/components/user-input/user-input.element.ts @@ -1,5 +1,5 @@ import { UmbUserPickerContext } from './user-input.context.js'; -import { css, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, customElement, property, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import type { UserItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; @@ -76,13 +76,13 @@ export class UmbUserInputElement extends FormControlMixin(UmbLitElement) { this.addValidator( 'rangeUnderflow', () => this.minMessage, - () => !!this.min && this.#pickerContext.getSelection().length < this.min + () => !!this.min && this.#pickerContext.getSelection().length < this.min, ); this.addValidator( 'rangeOverflow', () => this.maxMessage, - () => !!this.max && this.#pickerContext.getSelection().length > this.max + () => !!this.max && this.#pickerContext.getSelection().length > this.max, ); this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); @@ -105,7 +105,7 @@ export class UmbUserInputElement extends FormControlMixin(UmbLitElement) { private _renderItem(item: UserItemResponseModel) { if (!item.id) return; return html` - + this.#pickerContext.requestRemoveItem(item.id!)} label="Remove ${item.name}" >Remove Date: Wed, 27 Sep 2023 09:51:46 +0200 Subject: [PATCH 2/8] remove debuggers --- .../input-document-granular-permission.element.ts | 1 - .../documents/documents/entity-actions/permissions.action.ts | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document-granular-permission/input-document-granular-permission.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document-granular-permission/input-document-granular-permission.element.ts index af6d9ad5d0..1ab33f7290 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document-granular-permission/input-document-granular-permission.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document-granular-permission/input-document-granular-permission.element.ts @@ -66,7 +66,6 @@ export class UmbInputDocumentGranularPermissionElement extends FormControlMixin( }); modalContext?.onSubmit().then(({ selection }: any) => { - debugger; //this.#setSelection(selection); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/permissions.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/permissions.action.ts index bea922e1af..9362d92e4c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/permissions.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/permissions.action.ts @@ -33,8 +33,6 @@ export class UmbDocumentPermissionsEntityAction extends UmbEntityActionBase Date: Wed, 27 Sep 2023 09:52:08 +0200 Subject: [PATCH 3/8] update document input to use document input context --- .../input-document/input-document.context.ts | 10 ++ .../input-document/input-document.element.ts | 128 ++++++------------ 2 files changed, 51 insertions(+), 87 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.context.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.context.ts new file mode 100644 index 0000000000..640649a706 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.context.ts @@ -0,0 +1,10 @@ +import { UmbPickerInputContext } from '@umbraco-cms/backoffice/picker-input'; +import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UMB_DOCUMENT_PICKER_MODAL } from '@umbraco-cms/backoffice/modal'; +import { DocumentItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; + +export class UmbDocumentPickerContext extends UmbPickerInputContext { + constructor(host: UmbControllerHostElement) { + super(host, 'Umb.Repository.Document', UMB_DOCUMENT_PICKER_MODAL); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts index f76c73039e..01df812944 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts @@ -1,16 +1,8 @@ -import { UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN } from '../../repository/document.tree.store.js'; -import type { UmbDocumentTreeStore } from '../../repository/document.tree.store.js'; -import { css, html, nothing, customElement, property, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; +import { UmbDocumentPickerContext } from './input-document.context.js'; +import { css, html, customElement, property, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui'; -import { - UmbModalManagerContext, - UMB_MODAL_MANAGER_CONTEXT_TOKEN, - UMB_CONFIRM_MODAL, - UMB_DOCUMENT_PICKER_MODAL, -} from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import type { DocumentTreeItemResponseModel, EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; -import type { UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; +import type { DocumentItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; @customElement('umb-input-document') export class UmbInputDocumentElement extends FormControlMixin(UmbLitElement) { @@ -18,10 +10,15 @@ export class UmbInputDocumentElement extends FormControlMixin(UmbLitElement) { * This is a minimum amount of selected items in this input. * @type {number} * @attr - * @default undefined + * @default 0 */ @property({ type: Number }) - min?: number; + public get min(): number { + return this.#pickerContext.min; + } + public set min(value: number) { + this.#pickerContext.min = value; + } /** * Min validation message. @@ -36,10 +33,15 @@ export class UmbInputDocumentElement extends FormControlMixin(UmbLitElement) { * This is a maximum amount of selected items in this input. * @type {number} * @attr - * @default undefined + * @default Infinity */ @property({ type: Number }) - max?: number; + public get max(): number { + return this.#pickerContext.max; + } + public set max(value: number) { + this.#pickerContext.max = value; + } /** * Max validation message. @@ -50,30 +52,23 @@ export class UmbInputDocumentElement extends FormControlMixin(UmbLitElement) { @property({ type: String, attribute: 'min-message' }) maxMessage = 'This field exceeds the allowed amount of items'; - // TODO: do we need both selectedIds and value? If we just use value we follow the same pattern as native form controls. - private _selectedIds: Array = []; public get selectedIds(): Array { - return this._selectedIds; + return this.#pickerContext.getSelection(); } public set selectedIds(ids: Array) { - this._selectedIds = ids; - super.value = ids.join(','); - this._observePickedDocuments(); + this.#pickerContext.setSelection(ids); } @property() public set value(idsString: string) { - if (idsString !== this._value) { - this.selectedIds = idsString.split(/[ ,]+/); - } + // Its with full purpose we don't call super.value, as thats being handled by the observation of the context selection. + this.selectedIds = idsString.split(/[ ,]+/); } @state() - private _items?: Array; + private _items?: Array; - private _modalContext?: UmbModalManagerContext; - private _documentStore?: UmbDocumentTreeStore; - private _pickedItemsObserver?: UmbObserverController; + #pickerContext = new UmbDocumentPickerContext(this); constructor() { super(); @@ -81,84 +76,43 @@ export class UmbInputDocumentElement extends FormControlMixin(UmbLitElement) { this.addValidator( 'rangeUnderflow', () => this.minMessage, - () => !!this.min && this._selectedIds.length < this.min, + () => !!this.min && this.#pickerContext.getSelection().length < this.min, ); + this.addValidator( 'rangeOverflow', () => this.maxMessage, - () => !!this.max && this._selectedIds.length > this.max, + () => !!this.max && this.#pickerContext.getSelection().length > this.max, ); - this.consumeContext(UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN, (instance) => { - this._documentStore = instance; - this._observePickedDocuments(); - }); - this.consumeContext(UMB_MODAL_MANAGER_CONTEXT_TOKEN, (instance) => { - this._modalContext = instance; - }); + this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); + this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); } protected getFormElement() { return undefined; } - private _observePickedDocuments() { - this._pickedItemsObserver?.destroy(); - - if (!this._documentStore) return; - - // TODO: consider changing this to the list data endpoint when it is available - this._pickedItemsObserver = this.observe(this._documentStore.items(this._selectedIds), (items) => { - this._items = items; - }); - } - - private _openPicker() { - // We send a shallow copy(good enough as its just an array of ids) of our this._selectedIds, as we don't want the modal to manipulate our data: - const modalContext = this._modalContext?.open(UMB_DOCUMENT_PICKER_MODAL, { - multiple: this.max === 1 ? false : true, - selection: [...this._selectedIds], - }); - - modalContext?.onSubmit().then(({ selection }: any) => { - this._setSelection(selection); - }); - } - - private async _removeItem(item: EntityTreeItemResponseModel) { - const modalContext = this._modalContext?.open(UMB_CONFIRM_MODAL, { - color: 'danger', - headline: `Remove ${item.name}?`, - content: 'Are you sure you want to remove this item', - confirmLabel: 'Remove', - }); - - await modalContext?.onSubmit(); - const newSelection = this._selectedIds.filter((value) => value !== item.id); - this._setSelection(newSelection); - } - - private _setSelection(newSelection: Array) { - this.selectedIds = newSelection; - this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true })); - } - render() { return html` - ${this._items?.map((item) => this._renderItem(item))} - Add + ${this._items?.map((item) => this._renderItem(item))} + this.#pickerContext.openPicker()} label="open" + >Add `; } - private _renderItem(item: EntityTreeItemResponseModel) { - // TODO: remove when we have a way to handle trashed items - const tempItem = item as EntityTreeItemResponseModel & { isTrashed: boolean }; - + private _renderItem(item: DocumentItemResponseModel) { + if (!item.id) return; return html` - - ${tempItem.isTrashed ? html` Trashed ` : nothing} + + - this._removeItem(item)} label="Remove document ${item.name}">Remove + this.#pickerContext.requestRemoveItem(item.id!)} + label="Remove document ${item.name}" + >Remove `; From bba9c1e3e0c95be5bb699e435de35a9abd7e44f9 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 27 Sep 2023 09:52:27 +0200 Subject: [PATCH 4/8] make init wait for promises --- .../documents/documents/repository/document.repository.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/document.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/document.repository.ts index 01331c1443..b2a7216326 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/document.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/document.repository.ts @@ -46,19 +46,19 @@ export class UmbDocumentRepository this.#init = Promise.all([ new UmbContextConsumerController(this.#host, UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN, (instance) => { this.#treeStore = instance; - }), + }).asPromise(), new UmbContextConsumerController(this.#host, UMB_DOCUMENT_STORE_CONTEXT_TOKEN, (instance) => { this.#store = instance; - }), + }).asPromise(), new UmbContextConsumerController(this.#host, UMB_DOCUMENT_ITEM_STORE_CONTEXT_TOKEN, (instance) => { this.#itemStore = instance; - }), + }).asPromise(), new UmbContextConsumerController(this.#host, UMB_NOTIFICATION_CONTEXT_TOKEN, (instance) => { this.#notificationContext = instance; - }), + }).asPromise(), ]); } From df9bb4a1bba513ed7618206f99f1ac88836d1e87 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 27 Sep 2023 09:52:37 +0200 Subject: [PATCH 5/8] register document item store --- .../documents/repository/manifests.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/manifests.ts index a66e493a3a..adcaa16ab0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/manifests.ts @@ -1,7 +1,13 @@ import { UmbDocumentRepository } from '../repository/document.repository.js'; +import { UmbDocumentItemStore } from './document-item.store.js'; import { UmbDocumentStore } from './document.store.js'; import { UmbDocumentTreeStore } from './document.tree.store.js'; -import type { ManifestRepository, ManifestStore, ManifestTreeStore } from '@umbraco-cms/backoffice/extension-registry'; +import type { + ManifestItemStore, + ManifestRepository, + ManifestStore, + ManifestTreeStore, +} from '@umbraco-cms/backoffice/extension-registry'; export const DOCUMENT_REPOSITORY_ALIAS = 'Umb.Repository.Document'; @@ -14,6 +20,7 @@ const repository: ManifestRepository = { export const DOCUMENT_STORE_ALIAS = 'Umb.Store.Document'; export const DOCUMENT_TREE_STORE_ALIAS = 'Umb.Store.DocumentTree'; +export const DOCUMENT_ITEM_STORE_ALIAS = 'Umb.Store.DocumentItem'; const store: ManifestStore = { type: 'store', @@ -29,4 +36,11 @@ const treeStore: ManifestTreeStore = { class: UmbDocumentTreeStore, }; -export const manifests = [repository, store, treeStore]; +const itemStore: ManifestItemStore = { + type: 'itemStore', + alias: DOCUMENT_ITEM_STORE_ALIAS, + name: 'Document Item Store', + class: UmbDocumentItemStore, +}; + +export const manifests = [repository, store, treeStore, itemStore]; From a5dd991f4cb82229cc77a91678d4fe1db0373b7f Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 27 Sep 2023 10:03:37 +0200 Subject: [PATCH 6/8] align naming of element --- .../components/input-section/input-section.element.ts | 8 ++++---- .../components/input-section/input-section.stories.ts | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-section/input-section.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-section/input-section.element.ts index a93649fa3e..73427c9aca 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-section/input-section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-section/input-section.element.ts @@ -1,11 +1,11 @@ import { UmbInputListBaseElement } from '../input-list-base/input-list-base.js'; -import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, nothing, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UMB_SECTION_PICKER_MODAL } from '@umbraco-cms/backoffice/modal'; import { ManifestSection, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; @customElement('umb-input-section') -export class UmbInputPickerSectionElement extends UmbInputListBaseElement { +export class UmbInputSectionElement extends UmbInputListBaseElement { @state() private _sections: Array = []; @@ -47,7 +47,7 @@ export class UmbInputPickerSectionElement extends UmbInputListBaseElement { label="remove" color="danger"> - ` + `, )} `; @@ -85,6 +85,6 @@ export class UmbInputPickerSectionElement extends UmbInputListBaseElement { declare global { interface HTMLElementTagNameMap { - 'umb-input-section': UmbInputPickerSectionElement; + 'umb-input-section': UmbInputSectionElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-section/input-section.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-section/input-section.stories.ts index 30367b89ae..b39e4966d8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-section/input-section.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-section/input-section.stories.ts @@ -1,8 +1,8 @@ import { Meta, StoryObj } from '@storybook/web-components'; import './input-section.element.js'; -import type { UmbInputPickerSectionElement } from './input-section.element.js'; +import type { UmbInputSectionElement } from './input-section.element.js'; -const meta: Meta = { +const meta: Meta = { title: 'Components/Inputs/Section', component: 'umb-input-section', argTypes: { @@ -22,7 +22,7 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Overview: Story = { args: { From 84c7d6cb41f4be92efa1f693ece73b3d025d4799 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 27 Sep 2023 10:03:52 +0200 Subject: [PATCH 7/8] export components from module --- .../src/packages/documents/documents/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/index.ts index c1efa8caac..cce7f7a361 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/index.ts @@ -2,6 +2,7 @@ export * from './repository/index.js'; export * from './workspace/index.js'; export * from './recycle-bin/index.js'; export * from './user-permissions/index.js'; +export * from './components/index.js'; import './components/index.js'; From afd06073923e70fe21eb272615f7926d19dd5bfc Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 27 Sep 2023 10:04:02 +0200 Subject: [PATCH 8/8] update value on model --- .../user-group-workspace-editor.element.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/user-group-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/user-group-workspace-editor.element.ts index cac7e82517..393834be8e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/user-group-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/user-group-workspace-editor.element.ts @@ -9,6 +9,8 @@ import { UmbModalManagerContext, } from '@umbraco-cms/backoffice/modal'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UmbInputDocumentElement } from '@umbraco-cms/backoffice/document'; +import { UmbInputSectionElement } from '@umbraco-cms/backoffice/components'; import './components/user-group-default-permission-list.element.js'; import './components/user-group-granular-permission-list.element.js'; @@ -42,8 +44,14 @@ export class UmbUserGroupWorkspaceEditorElement extends UmbLitElement { this.#workspaceContext?.updateUserKeys(userIds); } - #onSectionsChange(value: string[]) { - this.#workspaceContext?.updateProperty('sections', value); + #onSectionsChange(event: CustomEvent) { + const target = event.target as UmbInputSectionElement; + this.#workspaceContext?.updateProperty('sections', target.value); + } + + #onDocumentStartNodeChange(event: CustomEvent) { + const target = event.target as UmbInputDocumentElement; + this.#workspaceContext?.updateProperty('documentStartNodeId', target.selectedIds[0]); } async #onDelete() { @@ -116,7 +124,7 @@ export class UmbUserGroupWorkspaceEditorElement extends UmbLitElement { this.#onSectionsChange(e.target.value)}> + @change=${this.#onSectionsChange}> this.#onSectionsChange(e.target.value)} + @change=${this.#onDocumentStartNodeChange} multiple>