From a1bd2c4b17116644227f5c348b0968f3e14fa6f9 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 14 Nov 2023 19:03:53 +0100 Subject: [PATCH] clean up --- .../data-type-input.element.ts | 4 +- .../document-types/components/index.ts | 2 +- .../document-type-input.context.ts | 10 ++ .../document-type-input.element.ts | 143 ++++++++++++++++++ .../input-document-type.element.ts | 138 ----------------- .../repository/document-type.repository.ts | 77 +--------- .../repository/document-type.tree.store.ts | 25 --- .../document-types/repository/index.ts | 1 - .../document-types/repository/manifests.ts | 18 +-- .../tree/document-type-tree.repository.ts | 2 +- ...t-type-workspace-view-structure.element.ts | 6 +- 11 files changed, 164 insertions(+), 262 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/document-type-input.context.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/document-type-input.element.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/input-document-type.element.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/document-type.tree.store.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/data-type/components/data-type-input/data-type-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/data-type/components/data-type-input/data-type-input.element.ts index 0d330326c8..a67e9445b4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/data-type/components/data-type-input/data-type-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/data-type/components/data-type-input/data-type-input.element.ts @@ -84,13 +84,13 @@ export class UmbDataTypeInputElement 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(','))); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/index.ts index be24cd45e5..0c5efc4532 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/index.ts @@ -1 +1 @@ -import './input-document-type/input-document-type.element.js'; +import './input-document-type/document-type-input.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/document-type-input.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/document-type-input.context.ts new file mode 100644 index 0000000000..2d2d79989d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/document-type-input.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_TYPE_PICKER_MODAL } from '@umbraco-cms/backoffice/modal'; +import { DocumentTypeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; + +export class UmbDocumentTypePickerContext extends UmbPickerInputContext { + constructor(host: UmbControllerHostElement) { + super(host, 'Umb.Repository.DocumentType', UMB_DOCUMENT_TYPE_PICKER_MODAL); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/document-type-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/document-type-input.element.ts new file mode 100644 index 0000000000..440723d165 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/document-type-input.element.ts @@ -0,0 +1,143 @@ +import { UmbDocumentTypePickerContext } from './document-type-input.context.js'; +import { css, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui'; +import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; +import type { DocumentTypeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; + +@customElement('umb-document-type-input') +export class UmbDocumentTypeInputElement extends FormControlMixin(UmbLitElement) { + /** + * This is a minimum amount of selected items in this input. + * @type {number} + * @attr + * @default 0 + */ + @property({ type: Number }) + public get min(): number { + return this.#pickerContext.min; + } + public set min(value: number) { + this.#pickerContext.min = value; + } + + /** + * Min validation message. + * @type {boolean} + * @attr + * @default + */ + @property({ type: String, attribute: 'min-message' }) + minMessage = 'This field need more items'; + + /** + * This is a maximum amount of selected items in this input. + * @type {number} + * @attr + * @default Infinity + */ + @property({ type: Number }) + public get max(): number { + return this.#pickerContext.max; + } + public set max(value: number) { + this.#pickerContext.max = value; + } + + /** + * Max validation message. + * @type {boolean} + * @attr + * @default + */ + @property({ type: String, attribute: 'min-message' }) + maxMessage = 'This field exceeds the allowed amount of items'; + + public get selectedIds(): Array { + return this.#pickerContext.getSelection(); + } + public set selectedIds(ids: Array) { + this.#pickerContext.setSelection(ids); + } + + @property() + public set value(idsString: string) { + // 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(/[ ,]+/); + } + + @property() + get pickableFilter() { + return this.#pickerContext.pickableFilter; + } + set pickableFilter(newVal) { + this.#pickerContext.pickableFilter = newVal; + } + + @state() + private _items?: Array; + + #pickerContext = new UmbDocumentTypePickerContext(this); + + constructor() { + super(); + + this.addValidator( + 'rangeUnderflow', + () => this.minMessage, + () => !!this.min && this.#pickerContext.getSelection().length < this.min, + ); + + this.addValidator( + 'rangeOverflow', + () => this.maxMessage, + () => !!this.max && this.#pickerContext.getSelection().length > this.max, + ); + + this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); + this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); + } + + protected getFormElement() { + return undefined; + } + + render() { + return html` + ${this._items?.map((item) => this._renderItem(item))} + this.#pickerContext.openPicker()} label="open" + >Add + `; + } + + private _renderItem(item: DocumentTypeItemResponseModel) { + if (!item.id) return; + return html` + + + this.#pickerContext.requestRemoveItem(item.id!)} + label="Remove Document Type ${item.name}" + >Remove + + + `; + } + + static styles = [ + css` + #add-button { + width: 100%; + } + `, + ]; +} + +export default UmbDocumentTypeInputElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-document-type-input': UmbDocumentTypeInputElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/input-document-type.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/input-document-type.element.ts deleted file mode 100644 index a577d7a9a8..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/components/input-document-type/input-document-type.element.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { - UmbDocumentTypeTreeStore, - UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN, -} from '../../repository/document-type.tree.store.js'; -import { css, html, nothing, ifDefined, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; -import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui'; -import { DocumentTypeResponseModel, EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; -import { - UmbModalManagerContext, - UMB_MODAL_MANAGER_CONTEXT_TOKEN, - UMB_CONFIRM_MODAL, - UMB_DOCUMENT_TYPE_PICKER_MODAL, -} from '@umbraco-cms/backoffice/modal'; -import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; - -@customElement('umb-input-document-type') -export class UmbInputDocumentTypeElement extends FormControlMixin(UmbLitElement) { - // 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 = []; - @property({ type: Array }) - public get selectedIds(): Array { - return this._selectedIds; - } - public set selectedIds(ids: Array) { - this._selectedIds = ids ?? []; - super.value = this._selectedIds.join(','); - this._observePickedDocuments(); - } - - @property() - public set value(idsString: string) { - if (idsString !== this._value) { - this.selectedIds = idsString.split(/[ ,]+/); - } - } - - @state() - private _items?: Array; - - private _modalContext?: UmbModalManagerContext; - private _documentTypeStore?: UmbDocumentTypeTreeStore; - private _pickedItemsObserver?: UmbObserverController; - - constructor() { - super(); - this.consumeContext(UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN, (instance) => { - this._documentTypeStore = instance; - this._observePickedDocuments(); - }); - this.consumeContext(UMB_MODAL_MANAGER_CONTEXT_TOKEN, (instance) => { - this._modalContext = instance; - }); - } - - protected getFormElement() { - return undefined; - } - - private _observePickedDocuments() { - this._pickedItemsObserver?.destroy(); - - if (!this._documentTypeStore) return; - - // TODO: consider changing this to the list data endpoint when it is available - this._pickedItemsObserver = this.observe(this._documentTypeStore.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_TYPE_PICKER_MODAL, { - multiple: true, - selection: [...this._selectedIds], - }); - - modalContext?.onSubmit().then(({ selection }: any) => { - this._setSelection(selection); - }); - } - - private async _removeItem(item: DocumentTypeResponseModel) { - 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 - `; - } - - private _renderItem(item: DocumentTypeResponseModel) { - // TODO: remove when we have a way to handle trashed items - const tempItem = item as DocumentTypeResponseModel & { isTrashed: boolean }; - - return html` - - - ${tempItem.isTrashed ? html` Trashed ` : nothing} - - this._removeItem(item)} label="Remove document ${item.name}">Remove - - - `; - } - - static styles = [ - css` - #add-button { - width: 100%; - } - `, - ]; -} - -export default UmbInputDocumentTypeElement; - -declare global { - interface HTMLElementTagNameMap { - 'umb-input-document-type': UmbInputDocumentTypeElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/document-type.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/document-type.repository.ts index a3a32f2ccb..21ce4e3bb7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/document-type.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/document-type.repository.ts @@ -1,16 +1,13 @@ -import { UmbDocumentTypeTreeServerDataSource } from '../tree/document-type.tree.server.data-source.js'; +import { UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT, UmbDocumentTypeTreeStore } from '../tree/document-type.tree.store.js'; import { UmbDocumentTypeServerDataSource } from './sources/document-type.server.data.js'; -import { UmbDocumentTypeTreeStore, UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN } from './document-type.tree.store.js'; import { UmbDocumentTypeStore, UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN } from './document-type.store.js'; import { UMB_DOCUMENT_TYPE_ITEM_STORE_CONTEXT_TOKEN, UmbDocumentTypeItemStore } from './document-type-item.store.js'; import { UmbDocumentTypeItemServerDataSource } from './sources/document-type-item.server.data.js'; import { type UmbDetailRepository } from '@umbraco-cms/backoffice/repository'; -import type { UmbTreeRepository, UmbTreeDataSource } from '@umbraco-cms/backoffice/tree'; import { UmbBaseController, type UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { CreateDocumentTypeRequestModel, DocumentTypeResponseModel, - EntityTreeItemResponseModel, FolderTreeItemResponseModel, UpdateDocumentTypeRequestModel, } from '@umbraco-cms/backoffice/backend-api'; @@ -22,13 +19,11 @@ type ItemType = DocumentTypeResponseModel; export class UmbDocumentTypeRepository extends UmbBaseController implements - UmbTreeRepository, UmbDetailRepository, UmbApi { #init!: Promise; - #treeSource: UmbTreeDataSource; #treeStore?: UmbDocumentTypeTreeStore; #detailDataSource: UmbDocumentTypeServerDataSource; @@ -43,12 +38,11 @@ export class UmbDocumentTypeRepository super(host); // TODO: figure out how spin up get the correct data source - this.#treeSource = new UmbDocumentTypeTreeServerDataSource(this); this.#detailDataSource = new UmbDocumentTypeServerDataSource(this); this.#itemSource = new UmbDocumentTypeItemServerDataSource(this); this.#init = Promise.all([ - this.consumeContext(UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN, (instance) => { + this.consumeContext(UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT, (instance) => { this.#treeStore = instance; }), @@ -66,46 +60,6 @@ export class UmbDocumentTypeRepository ]); } - // TODO: Move - async requestTreeRoot() { - await this.#init; - - const data = { - id: null, - type: 'document-type-root', - name: 'Document Types', - icon: 'icon-folder', - hasChildren: true, - }; - - return { data }; - } - - async requestRootTreeItems() { - await this.#init; - - const { data, error } = await this.#treeSource.getRootItems(); - - if (data) { - this.#treeStore?.appendItems(data.items); - } - - return { data, error, asObservable: () => this.#treeStore!.rootItems }; - } - - async requestTreeItemsOf(parentId: string | null) { - await this.#init; - if (parentId === undefined) throw new Error('Parent id is missing'); - - const { data, error } = await this.#treeSource.getChildrenOf(parentId); - - if (data) { - this.#treeStore?.appendItems(data.items); - } - - return { data, error, asObservable: () => this.#treeStore!.childrenOf(parentId) }; - } - async requestItems(ids: Array) { if (!ids) throw new Error('Document Type Ids are missing'); await this.#init; @@ -119,33 +73,6 @@ export class UmbDocumentTypeRepository return { data, error, asObservable: () => this.#itemStore!.items(ids) }; } - async requestItemsLegacy(ids: Array) { - await this.#init; - - if (!ids) { - throw new Error('Ids are missing'); - } - - const { data, error } = await this.#treeSource.getItems(ids); - - return { data, error, asObservable: () => this.#treeStore!.items(ids) }; - } - - async rootTreeItems() { - await this.#init; - return this.#treeStore!.rootItems; - } - - async treeItemsOf(parentId: string | null) { - await this.#init; - return this.#treeStore!.childrenOf(parentId); - } - - async itemsLegacy(ids: Array) { - await this.#init; - return this.#treeStore!.items(ids); - } - // DETAILS: async createScaffold(parentId: string | null) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/document-type.tree.store.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/document-type.tree.store.ts deleted file mode 100644 index 420f62d9f1..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/document-type.tree.store.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import { UmbEntityTreeStore } from '@umbraco-cms/backoffice/tree'; -import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; - -/** - * @export - * @class UmbDocumentTypeTreeStore - * @extends {UmbStoreBase} - * @description - Tree Data Store for Document-Types - */ -// TODO: consider if tree store could be turned into a general EntityTreeStore class? -export class UmbDocumentTypeTreeStore extends UmbEntityTreeStore { - /** - * Creates an instance of UmbDocumentTypeTreeStore. - * @param {UmbControllerHostElement} host - * @memberof UmbDocumentTypeTreeStore - */ - constructor(host: UmbControllerHostElement) { - super(host, UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN.toString()); - } -} - -export const UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken( - 'UmbDocumentTypeTreeStore', -); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/index.ts index b6bcde4a59..fb160edd76 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/index.ts @@ -1,3 +1,2 @@ export * from './document-type.repository.js'; export * from './document-type.store.js'; -export * from './document-type.tree.store.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/manifests.ts index 34645f525f..80acc88aa4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/manifests.ts @@ -1,13 +1,7 @@ import { UmbDocumentTypeItemStore } from './document-type-item.store.js'; import { UmbDocumentTypeRepository } from './document-type.repository.js'; import { UmbDocumentTypeStore } from './document-type.store.js'; -import { UmbDocumentTypeTreeStore } from './document-type.tree.store.js'; -import { - ManifestItemStore, - ManifestRepository, - ManifestStore, - ManifestTreeStore, -} from '@umbraco-cms/backoffice/extension-registry'; +import { ManifestItemStore, ManifestRepository, ManifestStore } from '@umbraco-cms/backoffice/extension-registry'; export const DOCUMENT_TYPE_REPOSITORY_ALIAS = 'Umb.Repository.DocumentType'; @@ -19,7 +13,6 @@ const repository: ManifestRepository = { }; export const DOCUMENT_TYPE_STORE_ALIAS = 'Umb.Store.DocumentType'; -export const DOCUMENT_TYPE_TREE_STORE_ALIAS = 'Umb.Store.DocumentTypeTree'; export const DOCUMENT_TYPE_ITEM_STORE_ALIAS = 'Umb.Store.DocumentTypeItem'; const store: ManifestStore = { @@ -29,13 +22,6 @@ const store: ManifestStore = { api: UmbDocumentTypeStore, }; -const treeStore: ManifestTreeStore = { - type: 'treeStore', - alias: DOCUMENT_TYPE_TREE_STORE_ALIAS, - name: 'Document Type Tree Store', - api: UmbDocumentTypeTreeStore, -}; - const itemStore: ManifestItemStore = { type: 'itemStore', alias: DOCUMENT_TYPE_ITEM_STORE_ALIAS, @@ -43,4 +29,4 @@ const itemStore: ManifestItemStore = { api: UmbDocumentTypeItemStore, }; -export const manifests = [repository, store, treeStore, itemStore]; +export const manifests = [repository, store, itemStore]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/document-type-tree.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/document-type-tree.repository.ts index c656104d22..cc29803cbc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/document-type-tree.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/document-type-tree.repository.ts @@ -18,7 +18,7 @@ export class UmbDocumentTypeTreeRepository const data = { id: null, type: DOCUMENT_TYPE_ROOT_ENTITY_TYPE, - name: 'BLa Bla', + name: 'Document Types', icon: 'icon-folder', hasChildren: true, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/structure/document-type-workspace-view-structure.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/structure/document-type-workspace-view-structure.element.ts index c59d51d8ba..b664c98812 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/structure/document-type-workspace-view-structure.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/structure/document-type-workspace-view-structure.element.ts @@ -1,7 +1,7 @@ import { UmbDocumentTypeWorkspaceContext } from '../../document-type-workspace.context.js'; -import type { UmbInputDocumentTypeElement } from '../../../components/input-document-type/input-document-type.element.js'; +import type { UmbDocumentTypeInputElement } from '../../../components/input-document-type/document-type-input.element.js'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; -import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UUIToggleElement } from '@umbraco-cms/backoffice/external/uui'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; @@ -65,7 +65,7 @@ export class UmbDocumentTypeWorkspaceViewStructureElement