From 3d3bfa1e161d7c1d8d0631e32c657111e7ece370 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 8 Mar 2023 10:17:10 +0100 Subject: [PATCH] introduce modal tokens --- src/Umbraco.Web.UI.Client/libs/modal/index.ts | 1 + .../libs/modal/modal-handler.ts | 25 +++++++++---- .../libs/modal/modal.context.ts | 14 +++---- .../libs/modal/token/modal-token.ts | 37 +++++++++++++++++++ .../document-picker-modal.element.ts | 8 +--- .../document-picker-modal.stories.ts | 5 ++- .../documents/modals/document-picker/index.ts | 18 +++++++++ .../input-document-picker.element.ts | 11 ++---- 8 files changed, 90 insertions(+), 29 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/libs/modal/token/modal-token.ts create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/index.ts diff --git a/src/Umbraco.Web.UI.Client/libs/modal/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/index.ts index 0065759e9e..4dc9d1d107 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/index.ts @@ -1,3 +1,4 @@ export * from './modal.context'; export * from './modal-handler'; export * from './layouts/modal-layout.element'; +export * from './token/modal-token'; diff --git a/src/Umbraco.Web.UI.Client/libs/modal/modal-handler.ts b/src/Umbraco.Web.UI.Client/libs/modal/modal-handler.ts index 2119157278..8bc20a5cd9 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/modal-handler.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/modal-handler.ts @@ -3,7 +3,8 @@ import type { UUIModalDialogElement } from '@umbraco-ui/uui-modal-dialog'; import type { UUIModalSidebarElement, UUIModalSidebarSize } from '@umbraco-ui/uui-modal-sidebar'; import { v4 as uuidv4 } from 'uuid'; import { BehaviorSubject } from 'rxjs'; -import { UmbModalOptions } from './modal.context'; +import { UmbModalConfig, UmbModalType } from './modal.context'; +import { UmbModalToken } from './token/modal-token'; import { createExtensionElement, umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import { UmbObserverController } from '@umbraco-cms/observable-api'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -21,15 +22,25 @@ export class UmbModalHandler { public readonly element = this.#element.asObservable(); public key: string; - public type: string; - public size: UUIModalSidebarSize; + public type: UmbModalType = 'dialog'; + public size: UUIModalSidebarSize = 'small'; - constructor(host: UmbControllerHostInterface, modalAlias: string, options?: UmbModalOptions) { + constructor( + host: UmbControllerHostInterface, + modalAlias: string | UmbModalToken, + data: unknown, + config?: UmbModalConfig + ) { this.#host = host; this.key = uuidv4(); - this.type = options?.type || 'dialog'; - this.size = options?.size || 'small'; + if (modalAlias instanceof UmbModalToken) { + this.type = modalAlias.getDefaultConfig().type || this.type; + this.size = modalAlias.getDefaultConfig().size || this.size; + } + + this.type = config?.type || this.type; + this.size = config?.size || this.size; // TODO: Consider if its right to use Promises, or use another event based system? Would we need to be able to cancel an event, to then prevent the closing..? this._closePromise = new Promise((resolve) => { @@ -37,7 +48,7 @@ export class UmbModalHandler { }); this.containerElement = this.#createContainerElement(); - this.#observeModal(modalAlias, options?.data); + this.#observeModal(modalAlias.toString(), data); } #createContainerElement() { diff --git a/src/Umbraco.Web.UI.Client/libs/modal/modal.context.ts b/src/Umbraco.Web.UI.Client/libs/modal/modal.context.ts index a2728483f7..38811b1b08 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/modal.context.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/modal.context.ts @@ -13,26 +13,26 @@ import './layouts/search/modal-layout-search.element.ts'; import { UUIModalSidebarSize } from '@umbraco-ui/uui-modal-sidebar'; import { BehaviorSubject } from 'rxjs'; import type { UUIModalDialogElement } from '@umbraco-ui/uui-modal-dialog'; +import type { UmbModalDocumentPickerData } from '../../src/backoffice/documents/documents/modals/document-picker'; import { UmbModalChangePasswordData } from './layouts/modal-layout-change-password.element'; import type { UmbModalIconPickerData } from './layouts/icon-picker/modal-layout-icon-picker.element'; import type { UmbModalConfirmData } from './layouts/confirm/modal-layout-confirm.element'; -import type { UmbModalContentPickerData } from '../../src/backoffice/documents/documents/modals/document-picker/document-picker-modal.element'; import type { UmbModalPropertyEditorUIPickerData } from './layouts/property-editor-ui-picker/modal-layout-property-editor-ui-picker.element'; import type { UmbModalMediaPickerData } from './layouts/media-picker/modal-layout-media-picker.element'; import type { UmbModalLinkPickerData } from './layouts/link-picker/modal-layout-link-picker.element'; import { UmbModalHandler } from './modal-handler'; import type { UmbBasicModalData } from './layouts/basic/modal-layout-basic.element'; import { UmbPickerModalData } from './layouts/modal-layout-picker-base'; +import { UmbModalToken } from './token/modal-token'; import { UmbContextToken } from '@umbraco-cms/context-api'; import { LanguageModel } from '@umbraco-cms/backend-api'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; export type UmbModalType = 'dialog' | 'sidebar'; -export interface UmbModalOptions { +export interface UmbModalConfig { type?: UmbModalType; size?: UUIModalSidebarSize; - data?: UmbModalData; } // TODO: we should find a way to easily open a modal without adding custom methods to this context. It would result in a better separation of concerns. @@ -61,11 +61,11 @@ export class UmbModalContext { /** * Opens a Content Picker sidebar modal * @public - * @param {UmbModalContentPickerData} [data] + * @param {UmbModalDocumentPickerData} [data] * @return {*} {UmbModalHandler} * @memberof UmbModalContext */ - public documentPicker(data?: UmbModalContentPickerData): UmbModalHandler { + public documentPicker(data?: UmbModalDocumentPickerData): UmbModalHandler { return this.open('umb-document-picker-modal', { data, type: 'sidebar', size: 'small' }); } @@ -204,8 +204,8 @@ export class UmbModalContext { * @return {*} {UmbModalHandler} * @memberof UmbModalContext */ - public open(modalAlias: string, options?: UmbModalOptions): UmbModalHandler { - const modalHandler = new UmbModalHandler(this.host, modalAlias, options); + public open(modalAlias: string | UmbModalToken, data: T, config?: UmbModalConfig): UmbModalHandler { + const modalHandler = new UmbModalHandler(this.host, modalAlias, data, config); modalHandler.containerElement.addEventListener('close-end', () => this.#onCloseEnd(modalHandler)); diff --git a/src/Umbraco.Web.UI.Client/libs/modal/token/modal-token.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/modal-token.ts new file mode 100644 index 0000000000..784dc5becb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/modal-token.ts @@ -0,0 +1,37 @@ +import { UmbModalConfig } from '../modal.context'; + +export class UmbModalToken { + /** + * Get the type of the token + * + * @public + * @type {T} + * @memberOf UmbModalToken + * @example `typeof MyToken.TYPE` + * @returns undefined + */ + readonly TYPE: T = undefined as never; + + /** + * @param alias Unique identifier for the token, + * @param defaultConfig Default configuration for the modal, + * @param _desc Description for the token, + * used only for debugging purposes, + * it should but does not need to be unique + */ + constructor(protected alias: string, protected defaultConfig: UmbModalConfig, protected _desc?: string) {} + + /** + * This method must always return the unique alias of the token since that + * will be used to look up the token in the injector. + * + * @returns the unique alias of the token + */ + toString(): string { + return this.alias; + } + + public getDefaultConfig(): UmbModalConfig { + return this.defaultConfig; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/document-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/document-picker-modal.element.ts index 96a54a54b1..73cf047eb8 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/document-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/document-picker-modal.element.ts @@ -2,16 +2,12 @@ import { css, html } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, state } from 'lit/decorators.js'; import { UmbModalLayoutElement } from '../../../../../../libs/modal/layouts/modal-layout.element'; +import { UmbModalDocumentPickerData } from '.'; import type { UmbTreeElement } from 'src/backoffice/shared/components/tree/tree.element'; -export interface UmbModalContentPickerData { - multiple?: boolean; - selection?: Array; -} - // TODO: make use of UmbPickerLayoutBase @customElement('umb-document-picker-modal') -export class UmbDocumentPickerModalElement extends UmbModalLayoutElement { +export class UmbDocumentPickerModalElement extends UmbModalLayoutElement { static styles = [ UUITextStyles, css` diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/document-picker-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/document-picker-modal.stories.ts index 14b86679db..ce00525c89 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/document-picker-modal.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/document-picker-modal.stories.ts @@ -4,7 +4,8 @@ import './document-picker-modal.element'; import { Meta, Story } from '@storybook/web-components'; import { html } from 'lit'; -import type { UmbDocumentPickerModalElement, UmbModalContentPickerData } from './document-picker-modal.element'; +import type { UmbDocumentPickerModalElement } from './document-picker-modal.element'; +import type { UmbModalDocumentPickerData } from './index'; export default { title: 'API/Modals/Layouts/Content Picker', @@ -12,7 +13,7 @@ export default { id: 'umb-document-picker-modal', } as Meta; -const data: UmbModalContentPickerData = { +const data: UmbModalDocumentPickerData = { multiple: true, selection: [], }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/index.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/index.ts new file mode 100644 index 0000000000..6fb9b30151 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/index.ts @@ -0,0 +1,18 @@ +import { UmbModalToken } from 'libs/modal'; + +export interface UmbModalDocumentPickerData { + multiple?: boolean; + selection?: Array; +} + +export interface UmbModalDocumentPickerResponse { + selection: Array; +} + +export const UMB_DOCUMENT_PICKER_MODAL_TOKEN = new UmbModalToken( + 'Umb.Modal.DocumentPicker', + { + type: 'sidebar', + size: 'small', + } +); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-document-picker/input-document-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-document-picker/input-document-picker.element.ts index cb8bedb77a..7cdd428fb4 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-document-picker/input-document-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-document-picker/input-document-picker.element.ts @@ -9,6 +9,7 @@ import type { UmbDocumentTreeStore } from '../../../documents/documents/reposito import { UmbLitElement } from '@umbraco-cms/element'; import type { DocumentTreeItemModel, FolderTreeItemModel } from '@umbraco-cms/backend-api'; import type { UmbObserverController } from '@umbraco-cms/observable-api'; +import { UMB_DOCUMENT_PICKER_MODAL_TOKEN } from 'src/backoffice/documents/documents/modals/document-picker'; @customElement('umb-input-document-picker') export class UmbInputDocumentPickerElement extends FormControlMixin(UmbLitElement) { @@ -121,13 +122,9 @@ export class UmbInputDocumentPickerElement extends FormControlMixin(UmbLitElemen private _openPicker() { // We send a shallow copy(good enough as its just an array of keys) of our this._selectedKeys, as we don't want the modal to manipulate our data: - const modalHandler = this._modalContext?.open('Umb.Modal.DocumentPicker', { - data: { - multiple: this.max === 1 ? false : true, - selection: [...this._selectedKeys], - }, - type: 'sidebar', - size: 'small', + const modalHandler = this._modalContext?.open(UMB_DOCUMENT_PICKER_MODAL_TOKEN, { + multiple: this.max === 1 ? false : true, + selection: [...this._selectedKeys], }); modalHandler?.onClose().then(({ selection }: any) => {