diff --git a/src/Umbraco.Web.UI.Client/.eslintrc.json b/src/Umbraco.Web.UI.Client/.eslintrc.json index 7725f81d2c..5fd970bda6 100644 --- a/src/Umbraco.Web.UI.Client/.eslintrc.json +++ b/src/Umbraco.Web.UI.Client/.eslintrc.json @@ -35,6 +35,7 @@ "local-rules/bad-type-import": "error", "local-rules/no-direct-api-import": "warn", "local-rules/prefer-import-aliases": "error", + "local-rules/prefer-umbraco-cms-imports": "error", "@typescript-eslint/no-non-null-assertion": "off" }, "settings": { diff --git a/src/Umbraco.Web.UI.Client/eslint-local-rules.cjs b/src/Umbraco.Web.UI.Client/eslint-local-rules.cjs index 1ec3bed82c..2a50f6a70a 100644 --- a/src/Umbraco.Web.UI.Client/eslint-local-rules.cjs +++ b/src/Umbraco.Web.UI.Client/eslint-local-rules.cjs @@ -20,18 +20,21 @@ module.exports = { create: function (context) { return { ImportDeclaration: function (node) { - if (node.source.parent.importKind !== 'type' && (node.source.value.endsWith('/models') || node.source.value === 'router-slot/model')) { + if ( + node.source.parent.importKind !== 'type' && + (node.source.value.endsWith('/models') || node.source.value === 'router-slot/model') + ) { const sourceCode = context.getSourceCode(); const nodeSource = sourceCode.getText(node); context.report({ node, message: 'Use `import type` instead of `import`.', - fix: fixer => fixer.replaceText(node, nodeSource.replace('import', 'import type')), + fix: (fixer) => fixer.replaceText(node, nodeSource.replace('import', 'import type')), }); } }, }; - } + }, }, /** @type {import('eslint').Rule.RuleModule} */ @@ -39,9 +42,10 @@ module.exports = { meta: { type: 'suggestion', docs: { - description: 'Ensures that any API resources from the `@umbraco-cms/backend-api` module are not used directly. Instead you should use the `tryExecuteAndNotify` function from the `@umbraco-cms/resources` module.', + description: + 'Ensures that any API resources from the `@umbraco-cms/backend-api` module are not used directly. Instead you should use the `tryExecuteAndNotify` function from the `@umbraco-cms/resources` module.', category: 'Best Practices', - recommended: true + recommended: true, }, fixable: 'code', schema: [], @@ -50,19 +54,30 @@ module.exports = { return { // If methods called on *Resource classes are not already wrapped with `await tryExecuteAndNotify()`, then we should suggest to wrap them. CallExpression: function (node) { - if (node.callee.type === 'MemberExpression' && node.callee.object.type === 'Identifier' && node.callee.object.name.endsWith('Resource') && node.callee.property.type === 'Identifier' && node.callee.property.name !== 'constructor') { - const hasTryExecuteAndNotify = node.parent && node.parent.callee && (node.parent.callee.name === 'tryExecute' || node.parent.callee.name === 'tryExecuteAndNotify'); + if ( + node.callee.type === 'MemberExpression' && + node.callee.object.type === 'Identifier' && + node.callee.object.name.endsWith('Resource') && + node.callee.property.type === 'Identifier' && + node.callee.property.name !== 'constructor' + ) { + const hasTryExecuteAndNotify = + node.parent && + node.parent.callee && + (node.parent.callee.name === 'tryExecute' || node.parent.callee.name === 'tryExecuteAndNotify'); if (!hasTryExecuteAndNotify) { context.report({ node, message: 'Wrap this call with `tryExecuteAndNotify()`. Make sure to `await` the result.', - fix: fixer => [fixer.insertTextBefore(node, 'tryExecuteAndNotify(this, '), fixer.insertTextAfter(node, ')')], + fix: (fixer) => [ + fixer.insertTextBefore(node, 'tryExecuteAndNotify(this, '), + fixer.insertTextAfter(node, ')'), + ], }); } } - } + }, }; - }, }, @@ -71,9 +86,10 @@ module.exports = { meta: { type: 'suggestion', docs: { - description: 'Ensures that the application does not rely on file system paths for imports. Instead, use import aliases or relative imports. This also solves a problem where GitHub fails on the test runner step.', + description: + 'Ensures that the application does not rely on file system paths for imports. Instead, use import aliases or relative imports. This also solves a problem where GitHub fails on the test runner step.', category: 'Best Practices', - recommended: true + recommended: true, }, schema: [], }, @@ -83,11 +99,47 @@ module.exports = { if (node.source.value.startsWith('src/')) { context.report({ node, - message: 'Prefer using import aliases or relative imports instead of absolute imports. Example: `import { MyComponent } from "src/components/MyComponent";` should be `import { MyComponent } from "@components/MyComponent";`' + message: + 'Prefer using import aliases or relative imports instead of absolute imports. Example: `import { MyComponent } from "src/components/MyComponent";` should be `import { MyComponent } from "@components/MyComponent";`', }); } }, }; - } + }, + }, + + // TODO: Its not bullet proof, but it will catch most/some cases. + /** @type {import('eslint').Rule.RuleModule} */ + 'prefer-umbraco-cms-imports': { + meta: { + type: 'suggestion', + docs: { + description: 'Replace relative imports to libs/... with @umbraco-cms/backoffice/...', + category: 'Best Practices', + recommended: true, + }, + fixable: 'code', + schema: [], + }, + create: function (context) { + const libsRegex = /(\.\.\/)*libs\/(.*)/; + return { + ImportDeclaration: function (node) { + const sourceValue = node.source.value; + if (sourceValue.startsWith('libs/') || libsRegex.test(sourceValue)) { + const importPath = sourceValue.replace(libsRegex, (match, p1, p2) => { + return `@umbraco-cms/backoffice/${p2}`; + }); + context.report({ + node, + message: `Use import alias @umbraco-cms/backoffice instead of relative path "${sourceValue}".`, + fix: function (fixer) { + return fixer.replaceTextRange(node.source.range, `'${importPath}'`); + }, + }); + } + }, + }; + }, }, }; diff --git a/src/Umbraco.Web.UI.Client/libs/context-api/consume/context-consumer.ts b/src/Umbraco.Web.UI.Client/libs/context-api/consume/context-consumer.ts index 5432b1dc0d..32bc1781a8 100644 --- a/src/Umbraco.Web.UI.Client/libs/context-api/consume/context-consumer.ts +++ b/src/Umbraco.Web.UI.Client/libs/context-api/consume/context-consumer.ts @@ -36,6 +36,10 @@ export class UmbContextConsumer { + // TODO: check that this check is not giving us any problems: + if (this._instance === instance) { + return; + } this._instance = instance; this._callback?.(instance); this._promiseResolver?.(instance); diff --git a/src/Umbraco.Web.UI.Client/libs/entity-action/actions/delete/delete.action.ts b/src/Umbraco.Web.UI.Client/libs/entity-action/actions/delete/delete.action.ts index 91b855ca74..d45acc3534 100644 --- a/src/Umbraco.Web.UI.Client/libs/entity-action/actions/delete/delete.action.ts +++ b/src/Umbraco.Web.UI.Client/libs/entity-action/actions/delete/delete.action.ts @@ -1,8 +1,7 @@ -import { UMB_CONFIRM_MODAL_TOKEN } from '../../../../src/backoffice/shared/modals/confirm'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_CONFIRM_MODAL } from '@umbraco-cms/backoffice/modal'; export class UmbDeleteEntityAction< T extends { delete(unique: string): Promise; requestItems(uniques: Array): any } @@ -25,7 +24,7 @@ export class UmbDeleteEntityAction< if (data) { const item = data[0]; - const modalHandler = this.#modalContext.open(UMB_CONFIRM_MODAL_TOKEN, { + const modalHandler = this.#modalContext.open(UMB_CONFIRM_MODAL, { headline: `Delete ${item.name}`, content: 'Are you sure you want to delete this item?', color: 'danger', diff --git a/src/Umbraco.Web.UI.Client/libs/entity-action/actions/trash/trash.action.ts b/src/Umbraco.Web.UI.Client/libs/entity-action/actions/trash/trash.action.ts index 95888d0ec9..6a3eb3b745 100644 --- a/src/Umbraco.Web.UI.Client/libs/entity-action/actions/trash/trash.action.ts +++ b/src/Umbraco.Web.UI.Client/libs/entity-action/actions/trash/trash.action.ts @@ -1,8 +1,7 @@ -import { UMB_CONFIRM_MODAL_TOKEN } from '../../../../src/backoffice/shared/modals/confirm'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_CONFIRM_MODAL } from '@umbraco-cms/backoffice/modal'; export class UmbTrashEntityAction< T extends { trash(unique: Array): Promise; requestTreeItems(uniques: Array): any } @@ -25,7 +24,7 @@ export class UmbTrashEntityAction< if (data) { const item = data[0]; - const modalHandler = this.#modalContext?.open(UMB_CONFIRM_MODAL_TOKEN, { + const modalHandler = this.#modalContext?.open(UMB_CONFIRM_MODAL, { headline: `Trash ${item.name}`, content: 'Are you sure you want to move this item to the recycle bin?', color: 'danger', diff --git a/src/Umbraco.Web.UI.Client/libs/modal/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/index.ts index 3c252e27f2..3e2caa5f16 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/index.ts @@ -1,4 +1,6 @@ export * from './modal.context'; export * from './modal-handler'; -export * from './token/modal-token'; +export * from './modal-route-registration'; +export * from './modal-route-registration.controller'; +export * from './token'; export * from './modal.interfaces'; 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 9d70723a1d..627b039642 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/modal-handler.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/modal-handler.ts @@ -16,7 +16,7 @@ import type { ManifestModal } from '@umbraco-cms/backoffice/extensions-registry' /** * Type which omits the real submit method, and replaces it with a submit method which accepts an optional argument depending on the generic type. */ -export type UmbModalHandler = Omit< +export type UmbModalHandler = Omit< UmbModalHandlerClass, 'submit' > & @@ -38,7 +38,7 @@ type OptionalSubmitArgumentIfUndefined = T extends undefined }; //TODO consider splitting this into two separate handlers -export class UmbModalHandlerClass { +export class UmbModalHandlerClass { private _submitPromise: Promise; private _submitResolver?: (value: ModalResult) => void; private _submitRejecter?: () => void; @@ -46,7 +46,7 @@ export class UmbModalHandlerClass { public modalElement: UUIModalDialogElement | UUIModalSidebarElement; - #innerElement = new BehaviorSubject(undefined); + #innerElement = new BehaviorSubject(undefined); public readonly innerElement = this.#innerElement.asObservable(); #modalElement?: UUIModalSidebarElement | UUIDialogElement; @@ -106,7 +106,7 @@ export class UmbModalHandlerClass { const innerElement = (await createExtensionElement(manifest)) as any; if (innerElement) { - innerElement.data = data; // + innerElement.data = data; //innerElement.observable = this.#dataObservable; innerElement.modalHandler = this; } @@ -114,7 +114,7 @@ export class UmbModalHandlerClass { return innerElement; } - // note, this methods argument is not defined correctly here, but requires to be fix by appending the OptionalSubmitArgumentIfUndefined type when newing up this class. + // note, this methods is private argument is not defined correctly here, but requires to be fix by appending the OptionalSubmitArgumentIfUndefined type when newing up this class. private submit(result?: ModalResult) { this._submitResolver?.(result as ModalResult); this.modalElement.close(); @@ -140,22 +140,25 @@ export class UmbModalHandlerClass { async (manifest) => { if (manifest) { const innerElement = await this.#createInnerElement(manifest, data); - this.#appendInnerElement(innerElement); - } else { - this.#removeInnerElement(); + if (innerElement) { + this.#appendInnerElement(innerElement); + return; + } } + this.#removeInnerElement(); } ); } - #appendInnerElement(element: any) { + #appendInnerElement(element: HTMLElement) { this.#modalElement?.appendChild(element); this.#innerElement.next(element); } #removeInnerElement() { - if (this.#innerElement.getValue()) { - this.#modalElement?.removeChild(this.#innerElement.getValue()); + const innerElement = this.#innerElement.getValue(); + if (innerElement) { + this.#modalElement?.removeChild(innerElement); this.#innerElement.next(undefined); } } diff --git a/src/Umbraco.Web.UI.Client/libs/modal/modal-route-registration.controller.ts b/src/Umbraco.Web.UI.Client/libs/modal/modal-route-registration.controller.ts new file mode 100644 index 0000000000..8d3ffae513 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/modal/modal-route-registration.controller.ts @@ -0,0 +1,88 @@ +// TODO: Be aware here we import a class from src! +import { UMB_ROUTE_CONTEXT_TOKEN } from '../router/route.context'; +import type { UmbControllerHostInterface, UmbControllerInterface } from '../controller'; +import { UmbModalRouteRegistration } from './modal-route-registration'; +import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; +import { UmbModalConfig, UmbModalToken } from '@umbraco-cms/backoffice/modal'; + +export class UmbModalRouteRegistrationController + extends UmbModalRouteRegistration + implements UmbControllerInterface +{ + //#host: UmbControllerHostInterface; + + #configuredPath: string; + #uniqueParts: Map; + + #routeContext?: typeof UMB_ROUTE_CONTEXT_TOKEN.TYPE; + #modalRegistration?: UmbModalRouteRegistration; + + public get unique() { + return undefined; + } + + constructor( + host: UmbControllerHostInterface, + alias: UmbModalToken | string, + path: string, + uniqueParts?: Map | null, + modalConfig?: UmbModalConfig + ) { + super(alias, path, modalConfig); + //this.#host = host; + this.#configuredPath = path; + this.#uniqueParts = uniqueParts || new Map(); + + new UmbContextConsumerController(host, UMB_ROUTE_CONTEXT_TOKEN, (_routeContext) => { + this.#routeContext = _routeContext; + this._registererModal(); + }); + } + + setUniqueIdentifier(identifier: string, value: string | undefined) { + if (!this.#uniqueParts.has(identifier)) { + throw new Error( + `Identifier ${identifier} was not registered at the construction of the modal registration controller, it has to be.` + ); + } + this.#uniqueParts.set(identifier, value); + this._registererModal(); + } + + private _registererModal() { + if (!this.#routeContext) return; + if (this.#modalRegistration) { + this.#routeContext.unregisterModal(this.#modalRegistration); + this.#modalRegistration = undefined; + } + + const pathParts = Array.from(this.#uniqueParts.values()); + + // Check if there is any undefined values of unique map: + if (pathParts.some((value) => value === undefined)) return; + + // Add the configured part of the path: + pathParts.push(this.#configuredPath); + + // Make this the path of the modal registration: + this._setPath(pathParts.join('/')); + + this.#modalRegistration = this.#routeContext.registerModal(this); + } + + hostConnected() { + if (!this.#modalRegistration) { + this._registererModal(); + } + } + hostDisconnected(): void { + if (this.#modalRegistration) { + this.#routeContext?.unregisterModal(this.#modalRegistration); + this.#modalRegistration = undefined; + } + } + + public destroy(): void { + this.hostDisconnected(); + } +} diff --git a/src/Umbraco.Web.UI.Client/libs/modal/modal-route-registration.ts b/src/Umbraco.Web.UI.Client/libs/modal/modal-route-registration.ts new file mode 100644 index 0000000000..095da5f546 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/modal/modal-route-registration.ts @@ -0,0 +1,115 @@ +import type { Params } from 'router-slot'; +import { v4 as uuidv4 } from 'uuid'; +import { UmbModalHandler } from './modal-handler'; +import { UmbModalConfig, UmbModalContext } from './modal.context'; +import { UmbModalToken } from './token/modal-token'; + +export type UmbModalRouteBuilder = (params: { [key: string]: string | number }) => string; + +export class UmbModalRouteRegistration { + #key: string; + #path: string; + #modalAlias: UmbModalToken | string; + #modalConfig?: UmbModalConfig; + + #onSetupCallback?: (routingInfo: Params) => UmbModalTokenData | false; + #onSubmitCallback?: (data: UmbModalTokenResult) => void; + #onRejectCallback?: () => void; + + #modalHandler: UmbModalHandler | undefined; + #routeBuilder?: UmbModalRouteBuilder; + #urlBuilderCallback: ((urlBuilder: UmbModalRouteBuilder) => void) | undefined; + + // Notice i removed the key in the transferring to this class. + constructor( + modalAlias: UmbModalToken | string, + path: string, + modalConfig?: UmbModalConfig + ) { + this.#key = modalConfig?.key || uuidv4(); + this.#modalAlias = modalAlias; + this.#path = path; + this.#modalConfig = { ...modalConfig, key: this.#key }; + } + + public get key() { + return this.#key; + } + + public get alias() { + return this.#modalAlias; + } + + public get path() { + return this.#path; + } + + protected _setPath(path: string) { + this.#path = path; + } + + public get modalConfig() { + return this.#modalConfig; + } + + /** + * Returns true if the modal is currently active. + */ + public get active() { + return !!this.#modalHandler; + } + + public open(params: { [key: string]: string | number }) { + if (this.active) return; + + window.history.pushState({}, '', this.#routeBuilder?.(params)); + } + + /** + * Returns the modal handler if the modal is currently active. Otherwise its undefined. + */ + public get modalHandler() { + return this.#modalHandler; + } + + public observeRouteBuilder(callback: (urlBuilder: UmbModalRouteBuilder) => void) { + this.#urlBuilderCallback = callback; + return this; + } + public _internal_setRouteBuilder(urlBuilder: UmbModalRouteBuilder) { + this.#routeBuilder = urlBuilder; + this.#urlBuilderCallback?.(urlBuilder); + } + + public onSetup(callback: (routingInfo: Params) => UmbModalTokenData | false) { + this.#onSetupCallback = callback; + return this; + } + public onSubmit(callback: (data: UmbModalTokenResult) => void) { + this.#onSubmitCallback = callback; + return this; + } + public onReject(callback: () => void) { + this.#onRejectCallback = callback; + return this; + } + + #onSubmit = (data: UmbModalTokenResult) => { + this.#onSubmitCallback?.(data); + this.#modalHandler = undefined; + }; + #onReject = () => { + this.#onRejectCallback?.(); + this.#modalHandler = undefined; + }; + + routeSetup(modalContext: UmbModalContext, params: Params) { + const modalData = this.#onSetupCallback ? this.#onSetupCallback(params) : undefined; + if (modalData !== false) { + this.#modalHandler = modalContext.open(this.#modalAlias, modalData, this.modalConfig); + this.#modalHandler.onSubmit().then(this.#onSubmit, this.#onReject); + return this.#modalHandler; + } + return null; + } +} 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 3bce518c35..7cfabb1968 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/modal.context.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/modal.context.ts @@ -21,7 +21,7 @@ export interface UmbModalConfig { export class UmbModalContext { host: UmbControllerHostInterface; // TODO: Investigate if we can get rid of HTML elements in our store, so we can use one of our states. - #modals = new BehaviorSubject(>>[]); + #modals = new BehaviorSubject(>[]); public readonly modals = this.#modals.asObservable(); constructor(host: UmbControllerHostInterface) { @@ -75,7 +75,7 @@ export class UmbModalContext { * @return {*} {UmbModalHandler} * @memberof UmbModalContext */ - public open( + public open( modalAlias: string | UmbModalToken, data?: ModalData, config?: UmbModalConfig diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/modals/allowed-document-types/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/allowed-document-types-modal.token.ts similarity index 83% rename from src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/modals/allowed-document-types/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/allowed-document-types-modal.token.ts index f4c5820dd4..74e37ca564 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/modals/allowed-document-types/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/allowed-document-types-modal.token.ts @@ -8,7 +8,7 @@ export interface UmbAllowedDocumentTypesModalResult { documentTypeKey: string; } -export const UMB_ALLOWED_DOCUMENT_TYPES_MODAL_TOKEN = new UmbModalToken< +export const UMB_ALLOWED_DOCUMENT_TYPES_MODAL = new UmbModalToken< UmbAllowedDocumentTypesModalData, UmbAllowedDocumentTypesModalResult >('Umb.Modal.AllowedDocumentTypes', { diff --git a/src/Umbraco.Web.UI.Client/libs/modal/token/change-password-modal.token.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/change-password-modal.token.ts new file mode 100644 index 0000000000..b2db240cd6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/change-password-modal.token.ts @@ -0,0 +1,9 @@ +import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; + +export interface UmbChangePasswordModalData { + requireOldPassword: boolean; +} + +export const UMB_CHANGE_PASSWORD_MODAL = new UmbModalToken('Umb.Modal.ChangePassword', { + type: 'dialog', +}); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/confirm/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/confirm-modal.token.ts similarity index 67% rename from src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/confirm/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/confirm-modal.token.ts index 17a03869d0..612881bc26 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/confirm/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/confirm-modal.token.ts @@ -10,9 +10,6 @@ export interface UmbConfirmModalData { export type UmbConfirmModalResult = undefined; -export const UMB_CONFIRM_MODAL_TOKEN = new UmbModalToken( - 'Umb.Modal.Confirm', - { - type: 'dialog', - } -); +export const UMB_CONFIRM_MODAL = new UmbModalToken('Umb.Modal.Confirm', { + type: 'dialog', +}); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/create/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/create-dictionary-modal.token.ts similarity index 87% rename from src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/create/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/create-dictionary-modal.token.ts index 16ab4fd003..3e635f2cc4 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/create/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/create-dictionary-modal.token.ts @@ -12,7 +12,7 @@ export interface UmbCreateDictionaryModalResult { name?: string; } -export const UMB_CREATE_DICTIONARY_MODAL_TOKEN = new UmbModalToken< +export const UMB_CREATE_DICTIONARY_MODAL = new UmbModalToken< UmbCreateDictionaryModalData, UmbCreateDictionaryModalResult >('Umb.Modal.CreateDictionary', { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/modals/invite-user/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/create-user-modal.token.ts similarity index 53% rename from src/Umbraco.Web.UI.Client/src/backoffice/users/users/modals/invite-user/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/create-user-modal.token.ts index 713bb7ae62..5ac7a0a780 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/modals/invite-user/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/create-user-modal.token.ts @@ -1,6 +1,6 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; -export const UMB_INVITE_USER_MODAL_TOKEN = new UmbModalToken('Umb.Modal.InviteUser', { +export const UMB_CREATE_USER_MODAL = new UmbModalToken('Umb.Modal.CreateUser', { type: 'dialog', size: 'small', }); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/modals/current-user/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/current-user-modal.token.ts similarity index 53% rename from src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/modals/current-user/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/current-user-modal.token.ts index 382b450ae7..5a78080a7a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/modals/current-user/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/current-user-modal.token.ts @@ -1,6 +1,6 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; -export const UMB_CURRENT_USER_MODAL_TOKEN = new UmbModalToken('Umb.Modal.CurrentUser', { +export const UMB_CURRENT_USER_MODAL = new UmbModalToken('Umb.Modal.CurrentUser', { type: 'sidebar', size: 'small', }); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/debug/modals/debug/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/debug-modal.token.ts similarity index 52% rename from src/Umbraco.Web.UI.Client/src/backoffice/shared/components/debug/modals/debug/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/debug-modal.token.ts index 760efebb80..7e3f797a38 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/debug/modals/debug/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/debug-modal.token.ts @@ -5,10 +5,7 @@ export interface UmbContextDebuggerModalData { content: TemplateResult | string; } -export const UMB_CONTEXT_DEBUGGER_MODAL_TOKEN = new UmbModalToken( - 'Umb.Modal.ContextDebugger', - { - type: 'sidebar', - size: 'small', - } -); +export const UMB_CONTEXT_DEBUGGER_MODAL = new UmbModalToken('Umb.Modal.ContextDebugger', { + type: 'sidebar', + size: 'small', +}); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/document-picker-modal.token.ts similarity index 55% rename from src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/document-picker-modal.token.ts index 4452cf228d..133318c692 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/document-picker-modal.token.ts @@ -9,10 +9,10 @@ export interface UmbDocumentPickerModalResult { selection: Array; } -export const UMB_DOCUMENT_PICKER_MODAL_TOKEN = new UmbModalToken< - UmbDocumentPickerModalData, - UmbDocumentPickerModalResult ->('Umb.Modal.DocumentPicker', { - type: 'sidebar', - size: 'small', -}); +export const UMB_DOCUMENT_PICKER_MODAL = new UmbModalToken( + 'Umb.Modal.DocumentPicker', + { + type: 'sidebar', + size: 'small', + } +); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/embedded-media/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/embedded-media-modal.token.ts similarity index 83% rename from src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/embedded-media/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/embedded-media-modal.token.ts index 94cde2a310..610b935d80 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/embedded-media/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/embedded-media-modal.token.ts @@ -26,7 +26,7 @@ export type UmbEmbeddedMediaModalResult = { selection: OEmbedResult; }; -export const UMB_EMBEDDED_MEDIA_MODAL_TOKEN = new UmbModalToken( +export const UMB_EMBEDDED_MEDIA_MODAL = new UmbModalToken( 'Umb.Modal.EmbeddedMedia', { type: 'sidebar', diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/modal-views/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/examine-fields-settings-modal.token.ts similarity index 84% rename from src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/modal-views/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/examine-fields-settings-modal.token.ts index 1e1e727379..d6f6aec2d7 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/modal-views/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/examine-fields-settings-modal.token.ts @@ -9,7 +9,7 @@ export interface UmbCreateDocumentModalResultData { fields?: UmbExamineFieldsSettingsModalData; } -export const UMB_EXAMINE_FIELDS_SETTINGS_MODAL_TOKEN = new UmbModalToken< +export const UMB_EXAMINE_FIELDS_SETTINGS_MODAL = new UmbModalToken< UmbExamineFieldsSettingsModalData, UmbCreateDocumentModalResultData >('Umb.Modal.ExamineFieldsSettings', { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/export/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/export-dictionary-modal.token.ts similarity index 83% rename from src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/export/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/export-dictionary-modal.token.ts index 003aae1dfc..4ae285e25f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/export/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/export-dictionary-modal.token.ts @@ -8,7 +8,7 @@ export interface UmbExportDictionaryModalResult { includeChildren?: boolean; } -export const UMB_EXPORT_DICTIONARY_MODAL_TOKEN = new UmbModalToken< +export const UMB_EXPORT_DICTIONARY_MODAL = new UmbModalToken< UmbExportDictionaryModalData, UmbExportDictionaryModalResult >('Umb.Modal.ExportDictionary', { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/icon-picker/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/icon-picker-modal.token.ts similarity index 74% rename from src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/icon-picker/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/icon-picker-modal.token.ts index 50badac9ec..0ae41a9fbb 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/icon-picker/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/icon-picker-modal.token.ts @@ -10,7 +10,7 @@ export interface UmbIconPickerModalResult { icon: string | undefined; } -export const UMB_ICON_PICKER_MODAL_TOKEN = new UmbModalToken( +export const UMB_ICON_PICKER_MODAL = new UmbModalToken( 'Umb.Modal.IconPicker', { type: 'sidebar', diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/import/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/import-dictionary-modal.token.ts similarity index 86% rename from src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/import/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/import-dictionary-modal.token.ts index 6172aaf811..745279c619 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/import/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/import-dictionary-modal.token.ts @@ -11,7 +11,7 @@ export interface UmbImportDictionaryModalResult { parentKey?: string; } -export const UMB_IMPORT_DICTIONARY_MODAL_TOKEN = new UmbModalToken< +export const UMB_IMPORT_DICTIONARY_MODAL = new UmbModalToken< UmbImportDictionaryModalData, UmbImportDictionaryModalResult >('Umb.Modal.ImportDictionary', { diff --git a/src/Umbraco.Web.UI.Client/libs/modal/token/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/index.ts new file mode 100644 index 0000000000..5840ca1bee --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/index.ts @@ -0,0 +1,24 @@ +export * from './modal-token'; +export * from './allowed-document-types-modal.token'; +export * from './change-password-modal.token'; +export * from './confirm-modal.token'; +export * from './create-dictionary-modal.token'; +export * from './create-user-modal.token'; +export * from './current-user-modal.token'; +export * from './debug-modal.token'; +export * from './document-picker-modal.token'; +export * from './embedded-media-modal.token'; +export * from './examine-fields-settings-modal.token'; +export * from './export-dictionary-modal.token'; +export * from './icon-picker-modal.token'; +export * from './import-dictionary-modal.token'; +export * from './invite-user-modal.token'; +export * from './language-picker-modal.token'; +export * from './link-picker-modal.token'; +export * from './media-picker-modal.token'; +export * from './property-editor-ui-picker-modal.token'; +export * from './property-settings-modal.token'; +export * from './search-modal.token'; +export * from './section-picker-modal.token'; +export * from './user-group-picker-modal.token'; +export * from './user-picker-modal.token'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/modals/create-user/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/invite-user-modal.token.ts similarity index 53% rename from src/Umbraco.Web.UI.Client/src/backoffice/users/users/modals/create-user/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/invite-user-modal.token.ts index 8a5868f272..20bb006c54 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/modals/create-user/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/invite-user-modal.token.ts @@ -1,6 +1,6 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; -export const UMB_CREATE_USER_MODAL_TOKEN = new UmbModalToken('Umb.Modal.CreateUser', { +export const UMB_INVITE_USER_MODAL = new UmbModalToken('Umb.Modal.InviteUser', { type: 'dialog', size: 'small', }); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/modals/language-picker/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/language-picker-modal.token.ts similarity index 65% rename from src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/modals/language-picker/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/language-picker-modal.token.ts index 477754bc5d..fb47287bd9 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/modals/language-picker/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/language-picker-modal.token.ts @@ -11,10 +11,10 @@ export interface UmbLanguagePickerModalResult { selection: Array; } -export const UMB_LANGUAGE_PICKER_MODAL_TOKEN = new UmbModalToken< - UmbLanguagePickerModalData, - UmbLanguagePickerModalResult ->('Umb.Modal.LanguagePicker', { - type: 'sidebar', - size: 'small', -}); +export const UMB_LANGUAGE_PICKER_MODAL = new UmbModalToken( + 'Umb.Modal.LanguagePicker', + { + type: 'sidebar', + size: 'small', + } +); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/link-picker-modal.token.ts similarity index 78% rename from src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/link-picker-modal.token.ts index f83d03dc4c..025b5582cb 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/link-picker-modal.token.ts @@ -2,11 +2,12 @@ import type { UUIModalSidebarSize } from '@umbraco-ui/uui'; import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; export interface UmbLinkPickerModalData { + index: number | null; link: UmbLinkPickerLink; config: UmbLinkPickerConfig; } -export type UmbLinkPickerModalResult = UmbLinkPickerLink; +export type UmbLinkPickerModalResult = { index: number | null; link: UmbLinkPickerLink }; export interface UmbLinkPickerLink { icon?: string | null; @@ -26,7 +27,7 @@ export interface UmbLinkPickerConfig { overlaySize?: UUIModalSidebarSize; } -export const UMB_LINK_PICKER_MODAL_TOKEN = new UmbModalToken( +export const UMB_LINK_PICKER_MODAL = new UmbModalToken( 'Umb.Modal.LinkPicker', { type: 'sidebar', diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/modals/media-picker/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/media-picker-modal.token.ts similarity index 72% rename from src/Umbraco.Web.UI.Client/src/backoffice/media/media/modals/media-picker/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/media-picker-modal.token.ts index 4cb0657504..94607575c4 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/modals/media-picker/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/media-picker-modal.token.ts @@ -9,7 +9,7 @@ export interface UmbMediaPickerModalResult { selection: Array; } -export const UMB_MEDIA_PICKER_MODAL_TOKEN = new UmbModalToken( +export const UMB_MEDIA_PICKER_MODAL = new UmbModalToken( 'Umb.Modal.MediaPicker', { type: 'sidebar', 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 index ab466d34d7..99510c47a8 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/token/modal-token.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/modal-token.ts @@ -1,6 +1,6 @@ import { UmbModalConfig } from '../modal.context'; -export class UmbModalToken { +export class UmbModalToken { /** * Get the data type of the token's data. * diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/modals/property-editor-ui-picker/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/property-editor-ui-picker-modal.token.ts similarity index 84% rename from src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/modals/property-editor-ui-picker/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/property-editor-ui-picker-modal.token.ts index 4c3f86509f..9bff24f2fe 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/modals/property-editor-ui-picker/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/property-editor-ui-picker-modal.token.ts @@ -9,7 +9,7 @@ export type UmbPropertyEditorUIPickerModalResult = { selection: Array; }; -export const UMB_PROPERTY_EDITOR_UI_PICKER_MODAL_TOKEN = new UmbModalToken< +export const UMB_PROPERTY_EDITOR_UI_PICKER_MODAL = new UmbModalToken< UmbPropertyEditorUIPickerModalData, UmbPropertyEditorUIPickerModalResult >('Umb.Modal.PropertyEditorUIPicker', { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/property-settings/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/property-settings-modal.token.ts similarity index 81% rename from src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/property-settings/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/property-settings-modal.token.ts index be4f7659f5..80d28d4578 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/property-settings/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/property-settings-modal.token.ts @@ -17,7 +17,7 @@ export interface UmbPropertySettingsModalResult { }; } -export const UMB_PROPERTY_SETTINGS_MODAL_TOKEN = new UmbModalToken( +export const UMB_PROPERTY_SETTINGS_MODAL = new UmbModalToken( 'Umb.Modal.PropertySettings', { type: 'sidebar', diff --git a/src/Umbraco.Web.UI.Client/libs/modal/token/search-modal.token.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/search-modal.token.ts new file mode 100644 index 0000000000..e3a99aab38 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/search-modal.token.ts @@ -0,0 +1,3 @@ +import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; + +export const UMB_SEARCH_MODAL = new UmbModalToken('Umb.Modal.Search'); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/section-picker/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/section-picker-modal.token.ts similarity index 61% rename from src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/section-picker/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/section-picker-modal.token.ts index 95edf154c6..c74346cc70 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/section-picker/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/section-picker-modal.token.ts @@ -5,7 +5,7 @@ export interface UmbSectionPickerModalData { selection: string[]; } -export const UMB_SECTION_PICKER_MODAL_TOKEN = new UmbModalToken('Umb.Modal.SectionPicker', { +export const UMB_SECTION_PICKER_MODAL = new UmbModalToken('Umb.Modal.SectionPicker', { type: 'sidebar', size: 'small', }); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/modals/user-group-picker/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/user-group-picker-modal.token.ts similarity index 69% rename from src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/modals/user-group-picker/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/user-group-picker-modal.token.ts index 5c67576638..4264345e24 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/modals/user-group-picker/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/user-group-picker-modal.token.ts @@ -1,7 +1,7 @@ import type { UserDetails } from '@umbraco-cms/backoffice/models'; import { UmbModalToken, UmbPickerModalData } from '@umbraco-cms/backoffice/modal'; -export const UMB_USER_GROUP_PICKER_MODAL_TOKEN = new UmbModalToken>( +export const UMB_USER_GROUP_PICKER_MODAL = new UmbModalToken>( 'Umb.Modal.UserGroupPicker', { type: 'sidebar', diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/modals/user-picker/index.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/user-picker-modal.token.ts similarity index 61% rename from src/Umbraco.Web.UI.Client/src/backoffice/users/users/modals/user-picker/index.ts rename to src/Umbraco.Web.UI.Client/libs/modal/token/user-picker-modal.token.ts index 4e247bef86..c1bf2c975a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/modals/user-picker/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/user-picker-modal.token.ts @@ -1,7 +1,7 @@ import type { UserDetails } from '@umbraco-cms/backoffice/models'; import { UmbModalToken, UmbPickerModalData } from '@umbraco-cms/backoffice/modal'; -export const UMB_USER_PICKER_MODAL_TOKEN = new UmbModalToken>('Umb.Modal.UserPicker', { +export const UMB_USER_PICKER_MODAL = new UmbModalToken>('Umb.Modal.UserPicker', { type: 'sidebar', size: 'small', }); diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts index b0e189c0c1..c18d4495b7 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts @@ -1,5 +1,6 @@ export * from './observer.controller'; export * from './observer'; +export * from './basic-state'; export * from './boolean-state'; export * from './number-state'; export * from './string-state'; diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/observer.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/observer.ts index 0ce9ce6fef..20eb7a0a34 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/observer.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/observer.ts @@ -17,7 +17,8 @@ export class UmbObserver { } hostDisconnected() { - this.#subscription.unsubscribe(); + // No cause then it cant re-connect, if the same element just was moved in DOM. + //this.#subscription.unsubscribe(); } destroy(): void { diff --git a/src/Umbraco.Web.UI.Client/libs/router/index.ts b/src/Umbraco.Web.UI.Client/libs/router/index.ts new file mode 100644 index 0000000000..5a49b1769d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/router/index.ts @@ -0,0 +1,3 @@ +export * from './route-location.interface'; +export * from './route.context'; +export * from './route.interface'; diff --git a/src/Umbraco.Web.UI.Client/src/core/router/route-location.interface.ts b/src/Umbraco.Web.UI.Client/libs/router/route-location.interface.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/core/router/route-location.interface.ts rename to src/Umbraco.Web.UI.Client/libs/router/route-location.interface.ts diff --git a/src/Umbraco.Web.UI.Client/libs/router/route.context.ts b/src/Umbraco.Web.UI.Client/libs/router/route.context.ts new file mode 100644 index 0000000000..021495954d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/router/route.context.ts @@ -0,0 +1,143 @@ +import type { IRoutingInfo, ISlashOptions } from 'router-slot'; +import { UmbRoute } from './route.interface'; +import { + UmbContextConsumerController, + UmbContextProviderController, + UmbContextToken, +} from '@umbraco-cms/backoffice/context-api'; +import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; +import { UMB_MODAL_CONTEXT_TOKEN, UmbModalRouteRegistration } from '@umbraco-cms/backoffice/modal'; + +const EmptyDiv = document.createElement('div'); + +const PARAM_IDENTIFIER = /:([^\\/]+)/g; + +function stripSlash(path: string): string { + return slashify(path, { start: false, end: false }); +} + +function slashify(path: string, { start = true, end = true }: Partial = {}): string { + path = start && !path.startsWith('/') ? `/${path}` : !start && path.startsWith('/') ? path.slice(1) : path; + return end && !path.endsWith('/') ? `${path}/` : !end && path.endsWith('/') ? path.slice(0, path.length - 1) : path; +} + +export class UmbRouteContext { + #modalRegistrations: UmbModalRouteRegistration[] = []; + #modalContext?: typeof UMB_MODAL_CONTEXT_TOKEN.TYPE; + #contextRoutes: UmbRoute[] = []; + #routerBasePath?: string; + #activeModalPath?: string; + + constructor(host: UmbControllerHostInterface, private _onGotModals: (contextRoutes: any) => void) { + new UmbContextProviderController(host, UMB_ROUTE_CONTEXT_TOKEN, this); + new UmbContextConsumerController(host, UMB_MODAL_CONTEXT_TOKEN, (context) => { + this.#modalContext = context; + this.#generateContextRoutes(); + }); + } + + public registerModal(registration: UmbModalRouteRegistration) { + this.#modalRegistrations.push(registration); + this.#generateNewUrlBuilder(registration); + this.#generateContextRoutes(); + return registration; + } + + public unregisterModal(registrationToken: ReturnType) { + const index = this.#modalRegistrations.indexOf(registrationToken); + if (index === -1) return; + this.#modalRegistrations.splice(index, 1); + this.#generateContextRoutes(); + } + + #getModalRoutePath(modalRegistration: UmbModalRouteRegistration) { + return `/modal/${modalRegistration.alias.toString()}/${modalRegistration.path}`; + } + + #generateRoute(modalRegistration: UmbModalRouteRegistration): UmbRoute { + return { + path: this.#getModalRoutePath(modalRegistration), + component: EmptyDiv, + setup: (component: HTMLElement, info: IRoutingInfo) => { + if (!this.#modalContext) return; + const modalHandler = modalRegistration.routeSetup(this.#modalContext, info.match.params); + if (modalHandler) { + modalHandler.onSubmit().then( + () => { + this.#removeModalPath(info); + }, + () => { + this.#removeModalPath(info); + } + ); + } + }, + }; + } + + #removeModalPath(info: IRoutingInfo) { + if (window.location.href.includes(info.match.fragments.consumed)) { + window.history.pushState({}, '', window.location.href.split(info.match.fragments.consumed)[0]); + } + } + + #generateContextRoutes() { + this.#contextRoutes = this.#modalRegistrations.map((modalRegistration) => { + return this.#generateRoute(modalRegistration); + }); + + // Add an empty route, so there is a route for the router to react on when no modals are open. + this.#contextRoutes.push({ + path: '', + component: EmptyDiv, + }); + + // TODO: Should we await one frame, to ensure we don't call back too much?. + this._onGotModals(this.#contextRoutes); + } + + public _internal_routerGotBasePath(routerBasePath: string) { + if (this.#routerBasePath === routerBasePath) return; + this.#routerBasePath = routerBasePath; + this.#generateNewUrlBuilders(); + } + + // Also notice each registration should now hold its handler when its active. + public _internal_modalRouterChanged(activeModalPath: string | undefined) { + if (this.#activeModalPath === activeModalPath) return; + if (this.#activeModalPath) { + // If if there is a modal using the old path. + const activeModal = this.#modalRegistrations.find( + (registration) => this.#getModalRoutePath(registration) === this.#activeModalPath + ); + if (activeModal) { + this.#modalContext?.close(activeModal.key); + } + } + this.#activeModalPath = activeModalPath; + } + + #generateNewUrlBuilders() { + this.#modalRegistrations.forEach(this.#generateNewUrlBuilder); + } + + #generateNewUrlBuilder = (modalRegistration: UmbModalRouteRegistration) => { + if (!this.#routerBasePath) return; + + const routeBasePath = this.#routerBasePath.endsWith('/') ? this.#routerBasePath : this.#routerBasePath + '/'; + const localPath = `modal/${modalRegistration.alias.toString()}/${modalRegistration.path}`; + + const urlBuilder = (params: { [key: string]: string | number }) => { + const localRoutePath = stripSlash( + localPath.replace(PARAM_IDENTIFIER, (substring: string, ...args: string[]) => { + return params[args[0]].toString(); + }) + ); + return routeBasePath + localRoutePath; + }; + + modalRegistration._internal_setRouteBuilder(urlBuilder); + }; +} + +export const UMB_ROUTE_CONTEXT_TOKEN = new UmbContextToken('UmbRouterContext'); diff --git a/src/Umbraco.Web.UI.Client/libs/router/route.interface.ts b/src/Umbraco.Web.UI.Client/libs/router/route.interface.ts new file mode 100644 index 0000000000..b76705bd13 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/router/route.interface.ts @@ -0,0 +1,3 @@ +import type { IRoute } from 'router-slot/model'; + +export type UmbRoute = IRoute; diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index b09fa44cd1..2d00798d91 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -30,8 +30,8 @@ "build": "tsc && vite build --mode staging", "build:libs": "npm run wc-analyze && npm run wc-analyze:vscode && rollup -c rollup-libs.config.js && node utils/move-libs.js", "build:for:static": "tsc && vite build", - "build:for:cms": "tsc && vite build -c vite.cms.config.ts && npm run build:libs", - "build:for:cms:watch": "vite build -c vite.cms.config.ts --watch", + "build:for:cms": "tsc && npm run build:libs && vite build -c vite.cms.config.ts", + "build:for:cms:watch": "tsc && npm run build:libs && vite build -c vite.cms.config.ts --watch", "preview": "vite preview --open", "test": "web-test-runner --coverage", "test:watch": "web-test-runner --watch", diff --git a/src/Umbraco.Web.UI.Client/src/app.ts b/src/Umbraco.Web.UI.Client/src/app.ts index c6176bd363..0ea2418090 100644 --- a/src/Umbraco.Web.UI.Client/src/app.ts +++ b/src/Umbraco.Web.UI.Client/src/app.ts @@ -4,6 +4,7 @@ import './core/css/custom-properties.css'; import 'element-internals-polyfill'; import './core/router/router-slot.element'; +import './core/router/variant-router-slot.element'; import './core/notification/layouts/default'; import './core/modal/modal-element.element'; @@ -12,7 +13,6 @@ import { css, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { UmbIconStore } from './core/stores/icon/icon.store'; - import type { Guard, IRoute } from '@umbraco-cms/internal/router'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/dashboards/redirect-management/dashboard-redirect-management.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/dashboards/redirect-management/dashboard-redirect-management.element.ts index 174905edee..42fa9ae950 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/dashboards/redirect-management/dashboard-redirect-management.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/dashboards/redirect-management/dashboard-redirect-management.element.ts @@ -2,8 +2,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html, nothing } from 'lit'; import { customElement, state, query, property } from 'lit/decorators.js'; import { UUIButtonState, UUIPaginationElement, UUIPaginationEvent } from '@umbraco-ui/uui'; -import { UMB_CONFIRM_MODAL_TOKEN } from '../../../shared/modals/confirm'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_CONFIRM_MODAL } from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { RedirectManagementResource, @@ -21,7 +20,7 @@ export class UmbDashboardRedirectManagementElement extends UmbLitElement { display: block; margin: var(--uui-size-layout-1); } - + .actions { display: flex; gap: var(--uui-size-space-1); @@ -136,7 +135,7 @@ export class UmbDashboardRedirectManagementElement extends UmbLitElement { } private _removeRedirectHandler(data: RedirectUrlResponseModel) { - const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL, { headline: 'Delete', content: html`
@@ -167,7 +166,7 @@ export class UmbDashboardRedirectManagementElement extends UmbLitElement { } private _disableRedirectHandler() { - const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL, { headline: 'Disable URL tracker', content: html`Are you sure you want to disable the URL tracker?`, color: 'danger', diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/modals/allowed-document-types/allowed-document-types-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/modals/allowed-document-types/allowed-document-types-modal.element.ts index 85a14bb33c..ff55e6dfc3 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/modals/allowed-document-types/allowed-document-types-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/modals/allowed-document-types/allowed-document-types-modal.element.ts @@ -3,7 +3,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, state } from 'lit/decorators.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { UmbDocumentTypeRepository } from '../../repository/document-type.repository'; -import { UmbAllowedDocumentTypesModalData, UmbAllowedDocumentTypesModalResult } from '.'; +import { UmbAllowedDocumentTypesModalData, UmbAllowedDocumentTypesModalResult } from '@umbraco-cms/backoffice/modal'; import { UmbModalBaseElement } from '@umbraco-cms/internal/modal'; import { DocumentTypeTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace-edit.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace-edit.element.ts index 099a206e97..cf68341d44 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace-edit.element.ts @@ -2,11 +2,10 @@ import { UUIInputElement, UUIInputEvent } from '@umbraco-ui/uui'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; -import { UMB_ICON_PICKER_MODAL_TOKEN } from '../../../shared/modals/icon-picker'; import { UmbWorkspaceDocumentTypeContext } from './document-type-workspace.context'; import type { DocumentTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_ICON_PICKER_MODAL } from '@umbraco-cms/backoffice/modal'; @customElement('umb-document-type-workspace-edit') export class UmbDocumentTypeWorkspaceEditElement extends UmbLitElement { @@ -83,7 +82,7 @@ export class UmbDocumentTypeWorkspaceEditElement extends UmbLitElement { } private async _handleIconClick() { - const modalHandler = this._modalContext?.open(UMB_ICON_PICKER_MODAL_TOKEN); + const modalHandler = this._modalContext?.open(UMB_ICON_PICKER_MODAL); modalHandler?.onSubmit().then((saved) => { if (saved.icon) this.#workspaceContext?.setIcon(saved.icon); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/entity-actions/create/create.action.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/entity-actions/create/create.action.ts index 2c0bdfbcc1..de691cf39c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/entity-actions/create/create.action.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/entity-actions/create/create.action.ts @@ -1,8 +1,11 @@ -import { UMB_ALLOWED_DOCUMENT_TYPES_MODAL_TOKEN } from '../../../document-types/modals/allowed-document-types'; import type { UmbDocumentRepository } from '../../repository/document.repository'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { + UmbModalContext, + UMB_MODAL_CONTEXT_TOKEN, + UMB_ALLOWED_DOCUMENT_TYPES_MODAL, +} from '@umbraco-cms/backoffice/modal'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; export class UmbCreateDocumentEntityAction extends UmbEntityActionBase { @@ -24,7 +27,7 @@ export class UmbCreateDocumentEntityAction extends UmbEntityActionBase { routes.push({ path: new UmbVariantId(variantA).toString() + '_&_' + new UmbVariantId(variantB).toString(), - //component: () => import('./document-workspace-split-view.element'), component: this.splitViewElement, setup: (component: HTMLElement | Promise, info: IRoutingInfo) => { // Set split view/active info.. @@ -102,7 +101,6 @@ export class UmbDocumentWorkspaceEditorElement extends UmbLitElement { this._availableVariants.forEach((variant) => { routes.push({ path: new UmbVariantId(variant).toString(), - //component: () => import('./document-workspace-split-view.element'), component: this.splitViewElement, setup: (component: HTMLElement | Promise, info: IRoutingInfo) => { // cause we might come from a split-view, we need to reset index 1. diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts index f872488f91..d7e6ecfc5a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts @@ -45,6 +45,18 @@ export class UmbDocumentWorkspaceContext this.splitView = new UmbWorkspaceSplitViewManager(this.host); new UmbObserverController(this.host, this.documentTypeKey, (key) => this.structure.loadType(key)); + + /* + TODO: Concept for ensure variant values: + new UmbObserverController(this.host, this.variants, (variants) => { + if (!variants) return; + const draft = this.#draft.getValue(); + if (!draft) return; + + // Gather all properties from all document types. + // Loop through all properties for each variant and insert missing value objects. + } + */ } async load(entityKey: string) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/views/edit/document-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/views/edit/document-workspace-view-edit.element.ts index 1b117564bf..21301a1396 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/views/edit/document-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/views/edit/document-workspace-view-edit.element.ts @@ -68,7 +68,6 @@ export class UmbDocumentWorkspaceViewEditElement extends UmbLitElement { this.observe( this._workspaceContext.rootPropertyStructures(), (rootPropertyStructure) => { - console.log('rootPropertyStructure', rootPropertyStructure); this._hasRootProperties = rootPropertyStructure.length > 0; this._createRoutes(); }, diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/manifests.ts index 23906ac74e..57dd021694 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/manifests.ts @@ -1,8 +1,7 @@ import { UmbMediaTypeRepository } from './media-type.repository'; import { UmbMediaTypeStore } from './media-type.detail.store'; import { UmbMediaTypeTreeStore } from './media-type.tree.store'; -import { ManifestRepository } from 'libs/extensions-registry/repository.models'; -import { ManifestStore, ManifestTreeStore } from '@umbraco-cms/backoffice/extensions-registry'; +import type { ManifestStore, ManifestTreeStore, ManifestRepository } from '@umbraco-cms/backoffice/extensions-registry'; export const MEDIA_TYPE_REPOSITORY_ALIAS = 'Umb.Repository.MediaType'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/entity-actions/manifests.ts index eb7b4c6ce0..c8b4e44629 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/entity-actions/manifests.ts @@ -1,6 +1,6 @@ import { MEDIA_REPOSITORY_ALIAS } from '../repository/manifests'; +import type { ManifestEntityAction } from '@umbraco-cms/backoffice/extensions-registry'; import { UmbTrashEntityAction } from '@umbraco-cms/backoffice/entity-action'; -import { ManifestEntityAction } from 'libs/extensions-registry/entity-action.models'; const entityActions: Array = [ { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/entity-bulk-actions/move/move.action.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/entity-bulk-actions/move/move.action.ts index 2e2496632c..9903168c0d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/entity-bulk-actions/move/move.action.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/entity-bulk-actions/move/move.action.ts @@ -1,9 +1,8 @@ import type { UmbMediaRepository } from '../../repository/media.repository'; -import { UMB_MEDIA_PICKER_MODAL_TOKEN } from '../../modals/media-picker'; import { UmbEntityBulkActionBase } from '@umbraco-cms/backoffice/entity-action'; import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_MEDIA_PICKER_MODAL } from '@umbraco-cms/backoffice/modal'; export class UmbMediaMoveEntityBulkAction extends UmbEntityBulkActionBase { #modalContext?: UmbModalContext; @@ -18,7 +17,7 @@ export class UmbMediaMoveEntityBulkAction extends UmbEntityBulkActionBase { #modalContext?: UmbModalContext; @@ -26,7 +25,7 @@ export class UmbMediaTrashEntityBulkAction extends UmbEntityBulkActionBase { - #init!: Promise; - #host: UmbControllerHostInterface; #treeSource: UmbTreeDataSource; @@ -26,6 +24,9 @@ export class UmbMediaRepository implements UmbTreeRepository, UmbDetailRepositor #notificationContext?: UmbNotificationContext; + #initResolver?: () => void; + #initialized = false; + constructor(host: UmbControllerHostInterface) { this.#host = host; @@ -33,19 +34,32 @@ export class UmbMediaRepository implements UmbTreeRepository, UmbDetailRepositor this.#treeSource = new MediaTreeServerDataSource(this.#host); this.#detailDataSource = new UmbMediaDetailServerDataSource(this.#host); - this.#init = Promise.all([ - new UmbContextConsumerController(this.#host, UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN, (instance) => { - this.#treeStore = instance; - }), + new UmbContextConsumerController(this.#host, UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN, (instance) => { + this.#treeStore = instance; + this.#checkIfInitialized(); + }); - new UmbContextConsumerController(this.#host, UMB_MEDIA_STORE_CONTEXT_TOKEN, (instance) => { - this.#store = instance; - }), + new UmbContextConsumerController(this.#host, UMB_MEDIA_STORE_CONTEXT_TOKEN, (instance) => { + this.#store = instance; + this.#checkIfInitialized(); + }); - new UmbContextConsumerController(this.#host, UMB_NOTIFICATION_CONTEXT_TOKEN, (instance) => { - this.#notificationContext = instance; - }), - ]); + new UmbContextConsumerController(this.#host, UMB_NOTIFICATION_CONTEXT_TOKEN, (instance) => { + this.#notificationContext = instance; + this.#checkIfInitialized(); + }); + } + + // TODO: make a generic way to wait for initialization + #init = new Promise((resolve) => { + this.#initialized ? resolve() : (this.#initResolver = resolve); + }); + + #checkIfInitialized() { + if (this.#treeStore && this.#store && this.#notificationContext) { + this.#initialized = true; + this.#initResolver?.(); + } } async requestRootTreeItems() { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/entity-actions/manifests.ts index 174945b696..90bd62d4f2 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/entity-actions/manifests.ts @@ -1,6 +1,6 @@ import { MEMBER_GROUP_REPOSITORY_ALIAS } from '../repository/manifests'; +import type { ManifestEntityAction } from '@umbraco-cms/backoffice/extensions-registry'; import { UmbDeleteEntityAction } from '@umbraco-cms/backoffice/entity-action'; -import { ManifestEntityAction } from 'libs/extensions-registry/entity-action.models'; const entityActions: Array = [ { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/manifests.ts index c9efa0f4be..038a11fe8e 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/manifests.ts @@ -1,8 +1,7 @@ import { UmbMemberGroupRepository } from './member-group.repository'; import { UmbMemberGroupStore } from './member-group.store'; import { UmbMemberGroupTreeStore } from './member-group.tree.store'; -import { ManifestRepository } from 'libs/extensions-registry/repository.models'; -import { ManifestStore, ManifestTreeStore } from '@umbraco-cms/backoffice/extensions-registry'; +import type { ManifestStore, ManifestTreeStore, ManifestRepository } from '@umbraco-cms/backoffice/extensions-registry'; export const MEMBER_GROUP_REPOSITORY_ALIAS = 'Umb.Repository.MemberGroup'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/members/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/members/entity-actions/manifests.ts index b9a055bbd6..9f2f9a28b2 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/members/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/members/entity-actions/manifests.ts @@ -1,6 +1,6 @@ import { MEMBER_REPOSITORY_ALIAS } from '../repository/manifests'; +import type { ManifestEntityAction } from '@umbraco-cms/backoffice/extensions-registry'; import { UmbDeleteEntityAction } from '@umbraco-cms/backoffice/entity-action'; -import { ManifestEntityAction } from 'libs/extensions-registry/entity-action.models'; const entityActions: Array = [ { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/members/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/members/repository/manifests.ts index e5cd53441d..15c055c885 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/members/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/members/repository/manifests.ts @@ -1,8 +1,7 @@ import { UmbMemberRepository } from './member.repository'; import { UmbMemberStore } from './member.store'; import { UmbMemberTreeStore } from './member.tree.store'; -import { ManifestRepository } from 'libs/extensions-registry/repository.models'; -import { ManifestStore, ManifestTreeStore } from '@umbraco-cms/backoffice/extensions-registry'; +import type { ManifestStore, ManifestTreeStore, ManifestRepository } from '@umbraco-cms/backoffice/extensions-registry'; export const MEMBER_REPOSITORY_ALIAS = 'Umb.Repository.Member'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/created/packages-created-overview.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/created/packages-created-overview.element.ts index c51f1dbb9a..11cd97a2fc 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/created/packages-created-overview.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/created/packages-created-overview.element.ts @@ -3,11 +3,10 @@ import { ifDefined } from 'lit/directives/if-defined.js'; import { customElement, state } from 'lit/decorators.js'; import { repeat } from 'lit/directives/repeat.js'; import { UUIPaginationEvent } from '@umbraco-ui/uui'; -import { UMB_CONFIRM_MODAL_TOKEN } from '../../../../shared/modals/confirm'; import { PackageDefinitionResponseModel, PackageResource } from '@umbraco-cms/backoffice/backend-api'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_CONFIRM_MODAL } from '@umbraco-cms/backoffice/modal'; @customElement('umb-packages-created-overview') export class UmbPackagesCreatedOverviewElement extends UmbLitElement { @@ -135,7 +134,7 @@ export class UmbPackagesCreatedOverviewElement extends UmbLitElement { async #deletePackage(p: PackageDefinitionResponseModel) { if (!p.key) return; - const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL, { color: 'danger', headline: `Remove ${p.name}?`, content: 'Are you sure you want to delete this package', diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/installed/installed-packages-section-view-item.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/installed/installed-packages-section-view-item.element.ts index bce3d35726..e4f9cd4b4c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/installed/installed-packages-section-view-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/installed/installed-packages-section-view-item.element.ts @@ -3,9 +3,7 @@ import { ifDefined } from 'lit/directives/if-defined.js'; import { customElement, property, state } from 'lit/decorators.js'; import { firstValueFrom, map } from 'rxjs'; import { UUIButtonState } from '@umbraco-ui/uui'; - -import { UMB_CONFIRM_MODAL_TOKEN } from '../../../../shared/modals/confirm'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_CONFIRM_MODAL } from '@umbraco-cms/backoffice/modal'; import { createExtensionElement, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; import type { ManifestPackageView } from '@umbraco-cms/backoffice/extensions-registry'; @@ -82,7 +80,7 @@ export class UmbInstalledPackagesSectionViewItem extends UmbLitElement { async _onMigration() { if (!this.name) return; - const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL, { color: 'positive', headline: `Run migrations for ${this.name}?`, content: `Do you want to start run migrations for ${this.name}`, diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/manifests.ts index f59cbfc4c5..b2a0069d5b 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/manifests.ts @@ -1,7 +1,6 @@ import { UmbPackageRepository } from './package.repository'; import { UmbPackageStore } from './package.store'; -import { ManifestRepository } from 'libs/extensions-registry/repository.models'; -import { ManifestStore } from '@umbraco-cms/backoffice/extensions-registry'; +import type { ManifestStore, ManifestRepository } from '@umbraco-cms/backoffice/extensions-registry'; export const PACKAGE_REPOSITORY_ALIAS = 'Umb.Repository.Package'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/search/modals/search/index.ts b/src/Umbraco.Web.UI.Client/src/backoffice/search/modals/search/index.ts deleted file mode 100644 index 8044cac830..0000000000 --- a/src/Umbraco.Web.UI.Client/src/backoffice/search/modals/search/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; - -export const UMB_SEARCH_MODAL_TOKEN = new UmbModalToken('Umb.Modal.Search'); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/cultures/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/cultures/repository/manifests.ts index 7502a562d3..0496f4acff 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/cultures/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/cultures/repository/manifests.ts @@ -1,5 +1,5 @@ import { UmbCultureRepository } from '../repository/culture.repository'; -import { ManifestRepository } from 'libs/extensions-registry/repository.models'; +import { ManifestRepository } from '@umbraco-cms/backoffice/extensions-registry'; export const CULTURE_REPOSITORY_ALIAS = 'Umb.Repository.Culture'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/modal-views/fields-settings.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/modal-views/fields-settings.element.ts index 60a1acfb8e..337623cfcc 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/modal-views/fields-settings.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/modal-views/fields-settings.element.ts @@ -1,7 +1,7 @@ import { html, css } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, state } from 'lit/decorators.js'; -import { UmbCreateDocumentModalResultData, UmbExamineFieldsSettingsModalData } from '.'; +import { UmbCreateDocumentModalResultData, UmbExamineFieldsSettingsModalData } from '@umbraco-cms/backoffice/modal'; import { UmbModalBaseElement } from '@umbraco-cms/internal/modal'; @customElement('umb-examine-fields-settings-modal') diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/section-view-examine-indexers.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/section-view-examine-indexers.ts index 6920abc579..9dcfc67150 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/section-view-examine-indexers.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/section-view-examine-indexers.ts @@ -2,8 +2,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html, nothing } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { UUIButtonState } from '@umbraco-ui/uui-button'; -import { UMB_CONFIRM_MODAL_TOKEN } from '../../../../shared/modals/confirm'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_CONFIRM_MODAL } from '@umbraco-cms/backoffice/modal'; import { HealthStatusModel, IndexResponseModel, IndexerResource } from '@umbraco-cms/backoffice/backend-api'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; @@ -118,7 +117,7 @@ export class UmbDashboardExamineIndexElement extends UmbLitElement { } private async _onRebuildHandler() { - const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL, { headline: `Rebuild ${this.indexName}`, content: html` This will cause the index to be rebuilt.
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/section-view-examine-searchers.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/section-view-examine-searchers.ts index 10439bad30..18cad41a37 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/section-view-examine-searchers.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/section-view-examine-searchers.ts @@ -1,14 +1,21 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html, nothing } from 'lit'; import { customElement, state, query, property } from 'lit/decorators.js'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; -import { SearchResultResponseModel, SearcherResource, FieldPresentationModel } from '@umbraco-cms/backoffice/backend-api'; +import { + UmbModalContext, + UMB_MODAL_CONTEXT_TOKEN, + UMB_EXAMINE_FIELDS_SETTINGS_MODAL, +} from '@umbraco-cms/backoffice/modal'; +import { + SearchResultResponseModel, + SearcherResource, + FieldPresentationModel, +} from '@umbraco-cms/backoffice/backend-api'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; import './modal-views/fields-viewer.element'; import './modal-views/fields-settings.element'; -import { UMB_EXAMINE_FIELDS_SETTINGS_MODAL_TOKEN } from './modal-views'; interface ExposedSearchResultField { name?: string | null; @@ -174,7 +181,7 @@ export class UmbDashboardExamineSearcherElement extends UmbLitElement { } private _onFieldFilterClick() { - const modalHandler = this._modalContext?.open(UMB_EXAMINE_FIELDS_SETTINGS_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_EXAMINE_FIELDS_SETTINGS_MODAL, { ...this._exposedFields, }); modalHandler?.onSubmit().then(({ fields } = {}) => { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/published-status/dashboard-published-status.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/published-status/dashboard-published-status.element.ts index 650a285706..1601dfd6b6 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/published-status/dashboard-published-status.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/published-status/dashboard-published-status.element.ts @@ -2,8 +2,7 @@ import { UUIButtonState } from '@umbraco-ui/uui'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; -import { UMB_CONFIRM_MODAL_TOKEN } from '../../../shared/modals/confirm'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_CONFIRM_MODAL } from '@umbraco-cms/backoffice/modal'; import { PublishedCacheResource } from '@umbraco-cms/backoffice/backend-api'; import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -14,10 +13,10 @@ export class UmbDashboardPublishedStatusElement extends UmbLitElement { UUITextStyles, css` :host { - display:block; - margin:var(--uui-size-layout-1); + display: block; + margin: var(--uui-size-layout-1); } - + uui-box + uui-box { margin-top: var(--uui-size-space-5); } @@ -86,7 +85,7 @@ export class UmbDashboardPublishedStatusElement extends UmbLitElement { } } private async _onReloadCacheHandler() { - const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL, { headline: 'Reload', content: html` Trigger a in-memory and local file cache reload on all servers. `, color: 'danger', @@ -109,7 +108,7 @@ export class UmbDashboardPublishedStatusElement extends UmbLitElement { } private async _onRebuildCacheHandler() { - const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL, { headline: 'Rebuild', content: html` Rebuild content in cmsContentNu database table. Expensive.`, color: 'danger', diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/manifests.ts index 8e68419718..0217359bf7 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/manifests.ts @@ -1,8 +1,7 @@ import { UmbDataTypeRepository } from '../repository/data-type.repository'; import { UmbDataTypeStore } from './data-type.store'; import { UmbDataTypeTreeStore } from './data-type.tree.store'; -import { ManifestRepository } from 'libs/extensions-registry/repository.models'; -import { ManifestStore, ManifestTreeStore } from '@umbraco-cms/backoffice/extensions-registry'; +import type { ManifestStore, ManifestTreeStore, ManifestRepository } from '@umbraco-cms/backoffice/extensions-registry'; export const DATA_TYPE_REPOSITORY_ALIAS = 'Umb.Repository.DataType'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace-edit.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace-edit.element.ts index 26dfe86ba8..0edb193b9f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace-edit.element.ts @@ -3,7 +3,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { UmbDataTypeWorkspaceContext } from './data-type-workspace.context'; -import { UmbRouteLocation } from '@umbraco-cms/internal/router'; +import { UmbRouteLocation } from '@umbraco-cms/backoffice/router'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { ManifestWorkspace } from '@umbraco-cms/backoffice/extensions-registry'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/details/data-type-details-workspace-view.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/details/data-type-details-workspace-view.element.ts index b5c5da1ce6..11a5ef4aa4 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/details/data-type-details-workspace-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/details/data-type-details-workspace-view.element.ts @@ -2,8 +2,11 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html, nothing } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { UmbDataTypeWorkspaceContext } from '../../data-type-workspace.context'; -import { UMB_PROPERTY_EDITOR_UI_PICKER_MODAL_TOKEN } from '../../../../../shared/property-editors/modals/property-editor-ui-picker'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { + UmbModalContext, + UMB_MODAL_CONTEXT_TOKEN, + UMB_PROPERTY_EDITOR_UI_PICKER_MODAL, +} from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import type { DataTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; @@ -101,7 +104,7 @@ export class UmbDataTypeDetailsWorkspaceViewEditElement extends UmbLitElement { private _openPropertyEditorUIPicker() { if (!this._dataType) return; - const modalHandler = this._modalContext?.open(UMB_PROPERTY_EDITOR_UI_PICKER_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_PROPERTY_EDITOR_UI_PICKER_MODAL, { selection: this._propertyEditorUiAlias ? [this._propertyEditorUiAlias] : [], }); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/extensions/workspace/extension-root-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/extensions/workspace/extension-root-workspace.element.ts index 0d9292a280..5831501295 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/extensions/workspace/extension-root-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/extensions/workspace/extension-root-workspace.element.ts @@ -1,19 +1,20 @@ import { css, html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { map } from 'rxjs'; -import { UMB_CONFIRM_MODAL_TOKEN } from '../../../shared/modals/confirm'; import { isManifestElementNameType, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; import type { ManifestTypes } from '@umbraco-cms/backoffice/extensions-registry'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_CONFIRM_MODAL } from '@umbraco-cms/backoffice/modal'; @customElement('umb-extension-root-workspace') export class UmbExtensionRootWorkspaceElement extends UmbLitElement { - static styles = [css` - uui-box { - margin: var(--uui-size-layout-1); - } - `] + static styles = [ + css` + uui-box { + margin: var(--uui-size-layout-1); + } + `, + ]; @state() private _extensions?: Array = undefined; @@ -50,7 +51,7 @@ export class UmbExtensionRootWorkspaceElement extends UmbLitElement { } async #removeExtension(extension: ManifestTypes) { - const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL, { headline: 'Unload extension', confirmLabel: 'Unload', content: html`

Are you sure you want to unload the extension ${extension.alias}?

`, diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/manifests.ts index a068bc239f..75dcb127c4 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/manifests.ts @@ -1,7 +1,6 @@ import { UmbLanguageRepository } from '../repository/language.repository'; import { UmbLanguageStore } from './language.store'; -import { ManifestRepository } from 'libs/extensions-registry/repository.models'; -import { ManifestStore } from '@umbraco-cms/backoffice/extensions-registry'; +import type { ManifestStore, ManifestRepository } from '@umbraco-cms/backoffice/extensions-registry'; export const LANGUAGE_REPOSITORY_ALIAS = 'Umb.Repository.Language'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/logviewer/workspace/logviewer.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/logviewer/workspace/logviewer.context.ts index c03196a421..272cccd715 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/logviewer/workspace/logviewer.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/logviewer/workspace/logviewer.context.ts @@ -1,5 +1,12 @@ import { UmbLogViewerRepository } from '../repository/log-viewer.repository'; -import { ArrayState, createObservablePart, DeepState, ObjectState, StringState } from '@umbraco-cms/backoffice/observable-api'; +import { + BasicState, + ArrayState, + createObservablePart, + DeepState, + ObjectState, + StringState, +} from '@umbraco-cms/backoffice/observable-api'; import { DirectionModel, LogLevelCountsReponseModel, @@ -11,7 +18,6 @@ import { } from '@umbraco-cms/backoffice/backend-api'; import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import { BasicState } from 'libs/observable-api/basic-state'; export type PoolingInterval = 0 | 2000 | 5000 | 10000 | 20000 | 30000; export interface PoolingCOnfig { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/repository/manifests.ts index fbbf4e87bb..7859009b6f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/repository/manifests.ts @@ -1,8 +1,7 @@ import { UmbRelationTypeRepository } from '../repository/relation-type.repository'; import { UmbRelationTypeStore } from './relation-type.store'; import { UmbRelationTypeTreeStore } from './relation-type.tree.store'; -import { ManifestRepository } from 'libs/extensions-registry/repository.models'; -import { ManifestStore, ManifestTreeStore } from '@umbraco-cms/backoffice/extensions-registry'; +import { ManifestStore, ManifestTreeStore, ManifestRepository } from '@umbraco-cms/backoffice/extensions-registry'; export const RELATION_TYPE_REPOSITORY_ALIAS = 'Umb.Repository.RelationType'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/repository/relation-type.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/repository/relation-type.repository.ts index 2c277ac890..201aa04df7 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/repository/relation-type.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/repository/relation-type.repository.ts @@ -6,8 +6,7 @@ import { RelationTypeTreeDataSource } from './sources'; import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { ProblemDetailsModel, RelationTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; -import type { UmbTreeRepository } from 'libs/repository/tree-repository.interface'; -import { UmbDetailRepository } from '@umbraco-cms/backoffice/repository'; +import { UmbDetailRepository, UmbTreeRepository } from '@umbraco-cms/backoffice/repository'; import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification'; type ItemType = RelationTypeResponseModel; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts index 3979c74ad9..61fef23527 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts @@ -3,8 +3,7 @@ import type { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backen import type { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; import { UmbContextToken, UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { ArrayState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; -import { createExtensionClass } from 'libs/extensions-api/create-extension-class.function'; +import { umbExtensionsRegistry, createExtensionClass } from '@umbraco-cms/backoffice/extensions-api'; import { UmbTreeRepository } from '@umbraco-cms/backoffice/repository'; // TODO: Clean up the need for store as Media has switched to use Repositories(repository). diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/debug/debug.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/debug/debug.element.ts index 128dbb33bb..64d73d8412 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/debug/debug.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/debug/debug.element.ts @@ -1,10 +1,10 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html, nothing, TemplateResult } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; -import { UMB_CONTEXT_DEBUGGER_MODAL_TOKEN } from './modals/debug'; + import { UmbContextDebugRequest } from '@umbraco-cms/backoffice/context-api'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_CONTEXT_DEBUGGER_MODAL, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; @customElement('umb-debug') export class UmbDebug extends UmbLitElement { @@ -107,7 +107,7 @@ export class UmbDebug extends UmbLitElement { } private _openDialog() { - this._modalContext?.open(UMB_CONTEXT_DEBUGGER_MODAL_TOKEN, { + this._modalContext?.open(UMB_CONTEXT_DEBUGGER_MODAL, { content: html`${this._renderContextAliases()}`, }); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/debug/modals/debug/debug-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/debug/modals/debug/debug-modal.element.ts index ba14f65b7a..74e5ade59c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/debug/modals/debug/debug-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/debug/modals/debug/debug-modal.element.ts @@ -1,7 +1,7 @@ import { css, html } from 'lit'; import { customElement } from 'lit/decorators.js'; import { UUITextStyles } from '@umbraco-ui/uui-css'; -import { UmbContextDebuggerModalData } from '.'; +import { UmbContextDebuggerModalData } from '@umbraco-cms/backoffice/modal'; import { UmbModalBaseElement } from '@umbraco-cms/internal/modal'; @customElement('umb-context-debugger-modal') diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-action/entity-action-list.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-action/entity-action-list.element.ts index a3edc8b9f8..3ea2640821 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-action/entity-action-list.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-action/entity-action-list.element.ts @@ -1,9 +1,9 @@ import { html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { map } from 'rxjs'; +import { ManifestEntityAction } from '@umbraco-cms/backoffice/extensions-registry'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; -import { ManifestEntityAction } from 'libs/extensions-registry/entity-action.models'; @customElement('umb-entity-action-list') class UmbEntityActionListElement extends UmbLitElement { 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 7b3d2bdea9..cf5bd39230 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 @@ -5,9 +5,12 @@ import { ifDefined } from 'lit/directives/if-defined.js'; import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins'; import { UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN } from '../../../documents/documents/repository/document.tree.store'; import type { UmbDocumentTreeStore } from '../../../documents/documents/repository/document.tree.store'; -import { UMB_CONFIRM_MODAL_TOKEN } from '../../modals/confirm'; -import { UMB_DOCUMENT_PICKER_MODAL_TOKEN } from '../../../documents/documents/modals/document-picker'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { + UmbModalContext, + UMB_MODAL_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'; @@ -123,7 +126,7 @@ 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_DOCUMENT_PICKER_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_DOCUMENT_PICKER_MODAL, { multiple: this.max === 1 ? false : true, selection: [...this._selectedKeys], }); @@ -134,7 +137,7 @@ export class UmbInputDocumentPickerElement extends FormControlMixin(UmbLitElemen } private async _removeItem(item: EntityTreeItemResponseModel) { - const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL, { color: 'danger', headline: `Remove ${item.name}?`, content: 'Are you sure you want to remove this item', diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-language-picker/input-language-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-language-picker/input-language-picker.element.ts index cd21264afa..263d9c76f0 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-language-picker/input-language-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-language-picker/input-language-picker.element.ts @@ -4,9 +4,12 @@ import { customElement, property, state } from 'lit/decorators.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins'; import { UmbLanguageRepository } from '../../../settings/languages/repository/language.repository'; -import { UMB_CONFIRM_MODAL_TOKEN } from '../../modals/confirm'; -import { UMB_LANGUAGE_PICKER_MODAL_TOKEN } from '../../../settings/languages/modals/language-picker'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { + UmbModalContext, + UMB_MODAL_CONTEXT_TOKEN, + UMB_CONFIRM_MODAL, + UMB_LANGUAGE_PICKER_MODAL, +} from '@umbraco-cms/backoffice/modal'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/events'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import type { LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api'; @@ -121,7 +124,7 @@ export class UmbInputLanguagePickerElement extends FormControlMixin(UmbLitElemen } private _openPicker() { - const modalHandler = this._modalContext?.open(UMB_LANGUAGE_PICKER_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_LANGUAGE_PICKER_MODAL, { multiple: this.max === 1 ? false : true, selection: [...this._selectedIsoCodes], filter: this.filter, @@ -133,7 +136,7 @@ export class UmbInputLanguagePickerElement extends FormControlMixin(UmbLitElemen } private _removeItem(item: LanguageResponseModel) { - const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL, { color: 'danger', headline: `Remove ${item.name}?`, content: 'Are you sure you want to remove this item', diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-media-picker/input-media-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-media-picker/input-media-picker.element.ts index d497dda6df..626bfd9def 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-media-picker/input-media-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-media-picker/input-media-picker.element.ts @@ -4,9 +4,12 @@ import { customElement, property, state } from 'lit/decorators.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins'; import { UmbMediaRepository } from '../../../media/media/repository/media.repository'; -import { UMB_CONFIRM_MODAL_TOKEN } from '../../modals/confirm'; -import { UMB_MEDIA_PICKER_MODAL_TOKEN } from '../../../media/media/modals/media-picker'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { + UmbModalContext, + UMB_MODAL_CONTEXT_TOKEN, + UMB_CONFIRM_MODAL, + UMB_MEDIA_PICKER_MODAL, +} from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import type { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; import type { UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; @@ -137,7 +140,7 @@ export class UmbInputMediaPickerElement extends FormControlMixin(UmbLitElement) 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_MEDIA_PICKER_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_MEDIA_PICKER_MODAL, { multiple: this.max === 1 ? false : true, selection: [...this._selectedKeys], }); @@ -148,7 +151,7 @@ export class UmbInputMediaPickerElement extends FormControlMixin(UmbLitElement) } private _removeItem(item: EntityTreeItemResponseModel) { - const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL_TOKEN, { + const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL, { color: 'danger', headline: `Remove ${item.name}?`, content: 'Are you sure you want to remove this item', diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-multi-url-picker/input-multi-url-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-multi-url-picker/input-multi-url-picker.element.ts index 83882a79e2..514effc3f9 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-multi-url-picker/input-multi-url-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-multi-url-picker/input-multi-url-picker.element.ts @@ -1,11 +1,16 @@ import { css, html } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; -import { customElement, property } from 'lit/decorators.js'; +import { customElement, property, state } from 'lit/decorators.js'; import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins'; import type { UUIModalSidebarSize } from '@umbraco-ui/uui'; -import { UmbLinkPickerLink, UMB_LINK_PICKER_MODAL_TOKEN } from '../../modals/link-picker'; +import type { UmbVariantId } from '../../variants/variant-id.class'; +import { + UmbLinkPickerLink, + UMB_LINK_PICKER_MODAL, + UmbModalRouteRegistrationController, +} from '@umbraco-cms/backoffice/modal'; +import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; /** * @element umb-input-multi-url-picker @@ -27,6 +32,17 @@ export class UmbInputMultiUrlPickerElement extends FormControlMixin(UmbLitElemen protected getFormElement() { return undefined; } + + @property() + public set alias(value: string | undefined) { + this.myModalRegistration.setUniqueIdentifier('propertyAlias', value); + } + + @property() + public set variantId(value: string | UmbVariantId | undefined) { + this.myModalRegistration.setUniqueIdentifier('variantId', value?.toString()); + } + /** * This is a minimum amount of selected items in this input. * @type {number} @@ -87,7 +103,7 @@ export class UmbInputMultiUrlPickerElement extends FormControlMixin(UmbLitElemen @property({ attribute: false }) set urls(data: Array) { if (!data) return; - this._urls = data; + this._urls = [...data]; // Unfreeze data coming from State, so we can manipulate it. super.value = this._urls.map((x) => x.url).join(','); } @@ -96,7 +112,11 @@ export class UmbInputMultiUrlPickerElement extends FormControlMixin(UmbLitElemen } private _urls: Array = []; - private _modalContext?: UmbModalContext; + + @state() + private _modalRoute?: UmbModalRouteBuilder; + + private myModalRegistration; constructor() { super(); @@ -111,9 +131,56 @@ export class UmbInputMultiUrlPickerElement extends FormControlMixin(UmbLitElemen () => !!this.max && this.urls.length > this.max ); - this.consumeContext(UMB_MODAL_CONTEXT_TOKEN, (instance) => { - this._modalContext = instance; - }); + this.myModalRegistration = new UmbModalRouteRegistrationController( + this, + UMB_LINK_PICKER_MODAL, + `:index`, + new Map([ + ['propertyAlias', undefined], + ['variantId', undefined], + ]) + ) + .onSetup((params) => { + // Get index: + const indexParam = params.index; + if (!indexParam) return false; + let index: number | null = parseInt(params.index); + if (Number.isNaN(index)) return false; + + // Use the index to find data: + let data: UmbLinkPickerLink | null = null; + if (index >= 0 && index < this.urls.length) { + data = this._getItemByIndex(index); + } else { + // If not then make a new pick: + index = null; + } + + return { + index: index, + link: { + name: data?.name, + published: data?.published, + queryString: data?.queryString, + target: data?.target, + trashed: data?.trashed, + udi: data?.udi, + url: data?.url, + }, + config: { + hideAnchor: this.hideAnchor, + ignoreUserStartNodes: this.ignoreUserStartNodes, + overlaySize: this.overlaySize || 'small', + }, + }; + }) + .onSubmit((submitData) => { + if (!submitData) return; + this._setSelection(submitData.link, submitData.index); + }) + .observeRouteBuilder((routeBuilder) => { + this._modalRoute = routeBuilder; + }); } private _removeItem(index: number) { @@ -121,9 +188,16 @@ export class UmbInputMultiUrlPickerElement extends FormControlMixin(UmbLitElemen this._dispatchChangeEvent(); } - private _setSelection(selection: UmbLinkPickerLink, index?: number) { - if (index !== undefined && index >= 0) this.urls[index] = selection; - else this.urls.push(selection); + private _getItemByIndex(index: number) { + return this.urls[index]; + } + + private _setSelection(selection: UmbLinkPickerLink, index: number | null) { + if (index !== null && index >= 0) { + this.urls[index] = selection; + } else { + this.urls.push(selection); + } this._dispatchChangeEvent(); } @@ -133,43 +207,24 @@ export class UmbInputMultiUrlPickerElement extends FormControlMixin(UmbLitElemen this.dispatchEvent(new CustomEvent('change', { composed: true, bubbles: true })); } - private _openPicker(data?: UmbLinkPickerLink, index?: number) { - const modalHandler = this._modalContext?.open(UMB_LINK_PICKER_MODAL_TOKEN, { - link: { - name: data?.name, - published: data?.published, - queryString: data?.queryString, - target: data?.target, - trashed: data?.trashed, - udi: data?.udi, - url: data?.url, - }, - config: { - hideAnchor: this.hideAnchor, - ignoreUserStartNodes: this.ignoreUserStartNodes, - overlaySize: this.overlaySize || 'small', - }, - }); - modalHandler?.onSubmit().then((newUrl: UmbLinkPickerLink) => { - if (!newUrl) return; - - this._setSelection(newUrl, index); - }); + // TODO: We should get a href property on uui-ref-node, and not use this method: + private _temporary_onClick_editItem(index: number) { + this.myModalRegistration.open({ index }); } render() { return html`${this.urls?.map((link, index) => this._renderItem(link, index))} - Add`; + Add`; } private _renderItem(link: UmbLinkPickerLink, index: number) { return html` + @open="${() => this._temporary_onClick_editItem(index)}"> - Edit + Edit Remove `; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-section/input-section.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-section/input-section.element.ts index 34799eeeff..3f560bd6f7 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-section/input-section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-section/input-section.element.ts @@ -2,7 +2,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css'; import { css, html, nothing } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { UmbInputListBase } from '../input-list-base/input-list-base'; -import { UMB_SECTION_PICKER_MODAL_TOKEN } from '../../modals/section-picker'; +import { UMB_SECTION_PICKER_MODAL } from '@umbraco-cms/backoffice/modal'; import type { ManifestSection } from '@umbraco-cms/backoffice/extensions-registry'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; @@ -42,7 +42,7 @@ export class UmbInputPickerSectionElement extends UmbInputListBase { connectedCallback(): void { super.connectedCallback(); - this.pickerToken = UMB_SECTION_PICKER_MODAL_TOKEN; + this.pickerToken = UMB_SECTION_PICKER_MODAL; this._observeSections(); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-user-group/input-user-group.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-user-group/input-user-group.element.ts index d27245d136..7315cd5a81 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-user-group/input-user-group.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-user-group/input-user-group.element.ts @@ -7,7 +7,7 @@ import { UMB_USER_GROUP_STORE_CONTEXT_TOKEN, } from '../../../users/user-groups/repository/user-group.store'; -import { UMB_USER_GROUP_PICKER_MODAL_TOKEN } from '../../../users/user-groups/modals/user-group-picker'; +import { UMB_USER_GROUP_PICKER_MODAL } from '@umbraco-cms/backoffice/modal'; import type { UserGroupEntity } from '@umbraco-cms/backoffice/models'; @customElement('umb-input-user-group') @@ -48,7 +48,7 @@ export class UmbInputPickerUserGroupElement extends UmbInputListBase { connectedCallback(): void { super.connectedCallback(); - this.pickerToken = UMB_USER_GROUP_PICKER_MODAL_TOKEN; + this.pickerToken = UMB_USER_GROUP_PICKER_MODAL; this.consumeContext(UMB_USER_GROUP_STORE_CONTEXT_TOKEN, (usersContext) => { this._userGroupStore = usersContext; this._observeUserGroups(); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-user/input-user.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-user/input-user.element.ts index 87fd8c3b19..bd72e5713b 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-user/input-user.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-user/input-user.element.ts @@ -3,7 +3,7 @@ import { css, html, nothing, PropertyValueMap } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { UmbInputListBase } from '../input-list-base/input-list-base'; import { UmbUserStore, UMB_USER_STORE_CONTEXT_TOKEN } from '../../../users/users/repository/user.store'; -import { UMB_USER_PICKER_MODAL_TOKEN } from '../../../users/users/modals/user-picker'; +import { UMB_USER_PICKER_MODAL } from '@umbraco-cms/backoffice/modal'; import type { UserEntity } from '@umbraco-cms/backoffice/models'; @customElement('umb-input-user') @@ -39,7 +39,7 @@ export class UmbPickerUserElement extends UmbInputListBase { connectedCallback(): void { super.connectedCallback(); - this.pickerToken = UMB_USER_PICKER_MODAL_TOKEN; + this.pickerToken = UMB_USER_PICKER_MODAL; this.consumeContext(UMB_USER_STORE_CONTEXT_TOKEN, (userStore) => { this._userStore = userStore; this._observeUser(); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.context.ts index 4ef7539100..ef32bc34fb 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.context.ts @@ -1,11 +1,10 @@ import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; -import { StringState } from '@umbraco-cms/backoffice/observable-api'; -import { BasicState } from 'libs/observable-api/basic-state'; +import { StringState, BooleanState } from '@umbraco-cms/backoffice/observable-api'; export class UmbSectionSidebarContext { #host: UmbControllerHostInterface; - #contextMenuIsOpen = new BasicState(false); + #contextMenuIsOpen = new BooleanState(false); contextMenuIsOpen = this.#contextMenuIsOpen.asObservable(); #entityType = new StringState(undefined); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item-base/tree-item-base.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item-base/tree-item-base.context.ts index 7216fb0683..8afb5da7cb 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item-base/tree-item-base.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item-base/tree-item-base.context.ts @@ -1,4 +1,5 @@ import { map } from 'rxjs'; +import { ManifestEntityAction } from '@umbraco-cms/backoffice/extensions-registry/entity-action.models'; import { UmbSectionSidebarContext, UMB_SECTION_SIDEBAR_CONTEXT_TOKEN, @@ -15,7 +16,6 @@ import { } from '@umbraco-cms/backoffice/context-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; import type { TreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api'; -import { ManifestEntityAction } from 'libs/extensions-registry/entity-action.models'; // add type for unique function export type UmbTreeItemUniqueFunction = (x: T) => string | null | undefined; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.context.interface.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.context.interface.ts index 0144422f76..7f78041319 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.context.interface.ts @@ -1,6 +1,6 @@ import { Observable } from 'rxjs'; +import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller/controller-host.mixin'; import { ProblemDetailsModel, TreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api'; -import { UmbControllerHostInterface } from 'libs/controller/controller-host.mixin'; // TODO: temp type. Add paged response type to the repository interface interface PagedResponse { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item/tree-item.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item/tree-item.element.ts index 9cecba3412..da60d3d1f0 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item/tree-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item/tree-item.element.ts @@ -1,9 +1,9 @@ import { css, html, nothing } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property } from 'lit/decorators.js'; +import { TreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api/src'; +import { ManifestTreeItem } from '@umbraco-cms/backoffice/extensions-registry/tree-item.models'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { TreeItemPresentationModel } from 'libs/backend-api/src'; -import { ManifestTreeItem } from 'libs/extensions-registry/tree-item.models'; @customElement('umb-tree-item') export class UmbTreeItemElement extends UmbLitElement { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts index be4146df44..655d0f11d3 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts @@ -2,9 +2,13 @@ import { UmbVariantId } from '../../variants/variant-id.class'; import { UmbWorkspaceVariableEntityContextInterface } from '../workspace/workspace-context/workspace-variable-entity-context.interface'; import { UMB_WORKSPACE_VARIANT_CONTEXT_TOKEN } from '../workspace/workspace-variant/workspace-variant.context'; import type { DataTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; -import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; -import { ObjectState, StringState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; -import { UmbContextConsumerController, UmbContextProviderController } from '@umbraco-cms/backoffice/context-api'; +import type { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; +import { ClassState, ObjectState, StringState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; +import { + UmbContextConsumerController, + UmbContextProviderController, + UmbContextToken, +} from '@umbraco-cms/backoffice/context-api'; // If we get this from the server then we can consider using TypeScripts Partial<> around the model from the Management-API. export type WorkspacePropertyData = { @@ -15,7 +19,7 @@ export type WorkspacePropertyData = { config?: DataTypeResponseModel['values']; // This could potentially then come from hardcoded JS object and not the DataType store. }; -export class UmbWorkspacePropertyContext { +export class UmbWorkspacePropertyContext { #host: UmbControllerHostInterface; private _providerController: UmbContextProviderController; @@ -28,12 +32,16 @@ export class UmbWorkspacePropertyContext { public readonly value = this._data.getObservablePart((data) => data.value); public readonly config = this._data.getObservablePart((data) => data.config); - private _variantId?: UmbVariantId; + #workspaceVariantId?: UmbVariantId; + + #variantId = new ClassState(undefined); + public readonly variantId = this.#variantId.asObservable(); private _variantDifference = new StringState(undefined); public readonly variantDifference = this._variantDifference.asObservable(); private _workspaceContext?: UmbWorkspaceVariableEntityContextInterface; + private _workspaceVariantConsumer?: UmbContextConsumerController; constructor(host: UmbControllerHostInterface) { this.#host = host; @@ -46,7 +54,32 @@ export class UmbWorkspacePropertyContext { } ); - this._providerController = new UmbContextProviderController(host, 'umbPropertyContext', this); + this._providerController = new UmbContextProviderController(host, UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN, this); + + this.variantId.subscribe((propertyVariantId) => { + if (propertyVariantId) { + if (!this._workspaceVariantConsumer) { + this._workspaceVariantConsumer = new UmbContextConsumerController( + this.#host, + UMB_WORKSPACE_VARIANT_CONTEXT_TOKEN, + (workspaceVariantContext) => { + new UmbObserverController(this.#host, workspaceVariantContext.variantId, (workspaceVariantId) => { + this.#workspaceVariantId = workspaceVariantId; + this._generateVariantDifferenceString(); + }); + } + ); + } else { + this._generateVariantDifferenceString(); + } + } + }); + } + + private _generateVariantDifferenceString() { + this._variantDifference.next( + this.#workspaceVariantId ? this.#variantId.getValue()?.toDifferencesString(this.#workspaceVariantId) : '' + ); } public setAlias(alias: WorkspacePropertyData['alias']) { @@ -67,22 +100,17 @@ export class UmbWorkspacePropertyContext { const alias = this._data.getValue().alias; if (alias) { - this._workspaceContext?.setPropertyValue(alias, value, this._variantId); + this._workspaceContext?.setPropertyValue(alias, value, this.#variantId.getValue()); } } public setConfig(config: WorkspacePropertyData['config']) { this._data.update({ config }); } public setVariantId(variantId: UmbVariantId | undefined) { - this._variantId = variantId; - new UmbContextConsumerController(this.#host, UMB_WORKSPACE_VARIANT_CONTEXT_TOKEN, (variantContext) => { - new UmbObserverController(this.#host, variantContext.variantId, (variantId) => { - this._variantDifference.next(variantId ? this._variantId?.toDifferencesString(variantId) : ''); - }); - }); + this.#variantId.next(variantId); } public getVariantId() { - return this._variantId; + return this.#variantId.getValue(); } public resetValue() { @@ -94,3 +122,7 @@ export class UmbWorkspacePropertyContext { this._providerController.destroy(); // This would also be handled by the controller host, but if someone wanted to replace/remove this context without the host being destroyed. Then we have clean up out selfs here. } } + +export const UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN = new UmbContextToken( + 'UmbWorkspacePropertyContext' +); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts index 2f270c7fef..b7965d8fe5 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts @@ -211,17 +211,25 @@ export class UmbWorkspacePropertyElement extends UmbLitElement { if (this._element) { this._element.addEventListener('property-value-change', this._onPropertyEditorChange as any as EventListener); - this._valueObserver = this.observe(this._propertyContext.value, (value) => { - this._value = value; - if (this._element) { - this._element.value = value; - } - }); - this._configObserver = this.observe(this._propertyContext.config, (config) => { - if (this._element && config) { - this._element.config = config; - } - }); + this._valueObserver = this.observe( + this._propertyContext.value, + (value) => { + this._value = value; + if (this._element) { + this._element.value = value; + } + }, + '_observePropertyValue' + ); + this._configObserver = this.observe( + this._propertyContext.config, + (config) => { + if (this._element && config) { + this._element.config = config; + } + }, + '_observePropertyConfig' + ); } this.requestUpdate('element', oldValue); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/confirm/confirm-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/confirm/confirm-modal.element.ts index 6f1735ad30..f710783f81 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/confirm/confirm-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/confirm/confirm-modal.element.ts @@ -1,9 +1,8 @@ import { html } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property } from 'lit/decorators.js'; -import { UmbConfirmModalData, UmbConfirmModalResult } from '.'; +import { UmbConfirmModalData, UmbConfirmModalResult, UmbModalHandler } from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UmbModalHandler } from '@umbraco-cms/backoffice/modal'; @customElement('umb-confirm-modal') export class UmbConfirmModalElement extends UmbLitElement { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/confirm/confirm-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/confirm/confirm-modal.stories.ts index 8a33def986..790bc96c46 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/confirm/confirm-modal.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/confirm/confirm-modal.stories.ts @@ -4,7 +4,7 @@ import { Meta, Story } from '@storybook/web-components'; import { html } from 'lit'; import type { UmbConfirmModalElement } from './confirm-modal.element'; -import type { UmbConfirmModalData } from './'; +import type { UmbConfirmModalData } from '@umbraco-cms/backoffice/modal'; export default { title: 'API/Modals/Layouts/Confirm', diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/embedded-media/embedded-media-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/embedded-media/embedded-media-modal.element.ts index 6df72714a6..6f38cc48dc 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/embedded-media/embedded-media-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/embedded-media/embedded-media-modal.element.ts @@ -3,10 +3,15 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property, state } from 'lit/decorators.js'; import { when } from 'lit-html/directives/when.js'; import { unsafeHTML } from 'lit-html/directives/unsafe-html.js'; -import { OEmbedResult, OEmbedStatus, UmbEmbeddedMediaModalData, UmbEmbeddedMediaModalResult } from '.'; +import { + OEmbedResult, + OEmbedStatus, + UmbEmbeddedMediaModalData, + UmbEmbeddedMediaModalResult, + UmbModalHandler, +} from '@umbraco-cms/backoffice/modal'; import { umbracoPath } from '@umbraco-cms/backoffice/utils'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UmbModalHandler } from '@umbraco-cms/backoffice/modal'; interface UmbEmbeddedMediaModalModel { url?: string; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/embedded-media/embedded-media-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/embedded-media/embedded-media-modal.stories.ts index 5eb102c153..2638f0142c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/embedded-media/embedded-media-modal.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/embedded-media/embedded-media-modal.stories.ts @@ -3,7 +3,7 @@ import './embedded-media-modal.element'; import { Meta } from '@storybook/web-components'; import { html } from 'lit'; -import { UmbEmbeddedMediaModalData } from '.'; +import { UmbEmbeddedMediaModalData } from '@umbraco-cms/backoffice/modal'; export default { title: 'API/Modals/Layouts/Embedded Media', diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/icon-picker/icon-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/icon-picker/icon-picker-modal.element.ts index 758696c00d..ff0cab31b1 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/icon-picker/icon-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/icon-picker/icon-picker-modal.element.ts @@ -6,7 +6,7 @@ import { customElement, property, state } from 'lit/decorators.js'; import { styleMap } from 'lit/directives/style-map.js'; import icons from '../../../../../public-assets/icons/icons.json'; -import { UmbIconPickerModalData, UmbIconPickerModalResult } from '.'; +import { UmbIconPickerModalData, UmbIconPickerModalResult } from '@umbraco-cms/backoffice/modal'; import { UmbModalBaseElement } from '@umbraco-cms/internal/modal'; // TODO: Make use of UmbPickerLayoutBase diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/icon-picker/icon-picker-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/icon-picker/icon-picker-modal.stories.ts index 2facbfb42a..53ee8a5bdb 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/icon-picker/icon-picker-modal.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/icon-picker/icon-picker-modal.stories.ts @@ -5,7 +5,7 @@ import { Meta, Story } from '@storybook/web-components'; import { html } from 'lit'; import type { UmbIconPickerModalElement } from './icon-picker-modal.element'; -import { UmbIconPickerModalData } from '.'; +import { UmbIconPickerModalData } from '@umbraco-cms/backoffice/modal'; export default { title: 'API/Modals/Layouts/Icon Picker', diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/link-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/link-picker-modal.element.ts index d1f290d0ff..7325caf339 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/link-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/link-picker-modal.element.ts @@ -3,7 +3,12 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, query, state } from 'lit/decorators.js'; import { UUIBooleanInputEvent, UUIInputElement } from '@umbraco-ui/uui'; import { UmbTreeElement } from '../../components/tree/tree.element'; -import { UmbLinkPickerConfig, UmbLinkPickerLink, UmbLinkPickerModalData, UmbLinkPickerModalResult } from '.'; +import { + UmbLinkPickerConfig, + UmbLinkPickerLink, + UmbLinkPickerModalData, + UmbLinkPickerModalResult, +} from '@umbraco-cms/backoffice/modal'; import { UmbModalBaseElement } from '@umbraco-cms/internal/modal'; import { buildUdi, getKeyFromUdi } from '@umbraco-cms/backoffice/utils'; @@ -46,6 +51,9 @@ export class UmbLinkPickerModalElement extends UmbModalBaseElement { +export class UmbPropertySettingsModalElement extends UmbModalBaseElement { static styles = [ UUITextStyles, css` @@ -261,7 +264,7 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement { + this.consumeContext(UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN, (propertyContext: UmbWorkspacePropertyContext) => { this._propertyContext = propertyContext; }); } @@ -40,7 +41,7 @@ export class UmbPropertyActionClearElement extends UmbLitElement implements UmbP //this.value = '';// This is though bad as it assumes we are dealing with a string. So wouldn't work as a generalized element. //this.dispatchEvent(new CustomEvent('property-value-change')); // Or you can do this: - this._propertyContext?.resetValue();// This resets value to what the property wants. + this._propertyContext?.resetValue(); // This resets value to what the property wants. } render() { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-creator/property-creator.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-creator/property-creator.element.ts index 9959e13c7d..a925a8299a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-creator/property-creator.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-creator/property-creator.element.ts @@ -1,8 +1,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css'; import { css, html } from 'lit'; import { customElement } from 'lit/decorators.js'; -import { UMB_PROPERTY_SETTINGS_MODAL_TOKEN } from '../modals/property-settings'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_PROPERTY_SETTINGS_MODAL } from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-property-creator') @@ -18,7 +17,7 @@ export class UmbPropertyCreatorElement extends UmbLitElement { } #onAddProperty() { - const modalHandler = this.#modalContext?.open(UMB_PROPERTY_SETTINGS_MODAL_TOKEN); + const modalHandler = this.#modalContext?.open(UMB_PROPERTY_SETTINGS_MODAL); modalHandler?.onSubmit().then((result) => { console.log('result', result); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts index 0c41abf92c..62dfc7906e 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/modals/property-editor-ui-picker/property-editor-ui-picker-modal.element.ts @@ -4,7 +4,10 @@ import { customElement, property, state } from 'lit/decorators.js'; import { repeat } from 'lit/directives/repeat.js'; import { groupBy } from 'lodash-es'; import type { UUIInputEvent } from '@umbraco-ui/uui'; -import { UmbPropertyEditorUIPickerModalData, UmbPropertyEditorUIPickerModalResult } from '.'; +import { + UmbPropertyEditorUIPickerModalData, + UmbPropertyEditorUIPickerModalResult, +} from '@umbraco-cms/backoffice/modal'; import type { UmbModalHandler } from '@umbraco-cms/backoffice/modal'; import type { ManifestPropertyEditorUI } from '@umbraco-cms/backoffice/extensions-registry'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/modals/property-editor-ui-picker/property-editor-ui-picker-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/modals/property-editor-ui-picker/property-editor-ui-picker-modal.stories.ts index 16d9f48b9d..fd6291ef18 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/modals/property-editor-ui-picker/property-editor-ui-picker-modal.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/modals/property-editor-ui-picker/property-editor-ui-picker-modal.stories.ts @@ -1,7 +1,7 @@ import { Meta, Story } from '@storybook/web-components'; import { html } from 'lit'; import type { UmbPropertyEditorUIPickerModalElement } from './property-editor-ui-picker-modal.element'; -import type { UmbPropertyEditorUIPickerModalData } from './'; +import type { UmbPropertyEditorUIPickerModalData } from '@umbraco-cms/backoffice/modal'; import './property-editor-ui-picker-modal.element'; import '../../../components/body-layout/body-layout.element'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/block-grid/property-editor-ui-block-grid.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/block-grid/property-editor-ui-block-grid.element.ts index 9e3a965a29..8939911174 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/block-grid/property-editor-ui-block-grid.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/block-grid/property-editor-ui-block-grid.element.ts @@ -2,8 +2,9 @@ import { html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { IRoute, IRoutingInfo } from 'router-slot'; -import { UMB_WORKSPACE_VARIANT_CONTEXT_TOKEN } from '../../../../shared/components/workspace/workspace-variant/workspace-variant.context'; import { UmbVariantId } from '../../../../shared/variants/variant-id.class'; +import { UMB_WORKSPACE_VARIANT_CONTEXT_TOKEN } from '../../../../shared/components/workspace/workspace-variant/workspace-variant.context'; +import { UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN } from '../../../../shared/components/workspace-property/workspace-property.context'; import { UmbRouterSlotChangeEvent, UmbRouterSlotInitEvent } from '@umbraco-cms/internal/router'; import { UmbPropertyEditorElement } from '@umbraco-cms/backoffice/property-editor'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -38,10 +39,9 @@ export class UmbPropertyEditorUIBlockGridElement extends UmbLitElement implement constructor() { super(); - this.consumeContext(UMB_WORKSPACE_VARIANT_CONTEXT_TOKEN, (context) => { - this._variantContext = context; - this.observe(this._variantContext?.variantId, (variantId) => { - this._variantId = variantId; + this.consumeContext(UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN, (context) => { + this.observe(context?.variantId, (propertyVariantId) => { + this._variantId = propertyVariantId; this.setupRoutes(); }); }); @@ -52,7 +52,7 @@ export class UmbPropertyEditorUIBlockGridElement extends UmbLitElement implement if (this._variantId !== undefined) { this._routes = [ { - path: this._variantId.toString() + '/modal-1', + path: 'modal-1', component: () => { return import('./property-editor-ui-block-grid-inner-test.element'); }, @@ -64,7 +64,7 @@ export class UmbPropertyEditorUIBlockGridElement extends UmbLitElement implement }, }, { - path: this._variantId.toString() + '/modal-2', + path: 'modal-2', //pathMatch: 'full', component: () => { return import('./property-editor-ui-block-grid-inner-test.element'); @@ -88,17 +88,16 @@ export class UmbPropertyEditorUIBlockGridElement extends UmbLitElement implement + href="${this._routerPath + '/'}modal-1" + .active=${this._routerPath + '/' + 'modal-1' === this._activePath}> + href="${this._routerPath + '/'}modal-2" + .active=${this._routerPath + '/' + 'modal-2' === this._activePath}> - { @@ -107,7 +106,7 @@ export class UmbPropertyEditorUIBlockGridElement extends UmbLitElement implement @change=${(event: UmbRouterSlotChangeEvent) => { this._activePath = event.target.localActiveViewPath; }}> - +
` : 'loading...'; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/icon-picker/property-editor-ui-icon-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/icon-picker/property-editor-ui-icon-picker.element.ts index 6448b70d4b..aebdeeb989 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/icon-picker/property-editor-ui-icon-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/icon-picker/property-editor-ui-icon-picker.element.ts @@ -1,9 +1,8 @@ import { html } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property } from 'lit/decorators.js'; -import { UMB_ICON_PICKER_MODAL_TOKEN } from '../../../modals/icon-picker'; import { UmbPropertyEditorElement } from '@umbraco-cms/backoffice/property-editor'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_ICON_PICKER_MODAL } from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; /** @@ -29,7 +28,7 @@ export class UmbPropertyEditorUIIconPickerElement extends UmbLitElement implemen } private _openModal() { - this._modalContext?.open(UMB_ICON_PICKER_MODAL_TOKEN); + this._modalContext?.open(UMB_ICON_PICKER_MODAL); } render() { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multi-url-picker/property-editor-ui-multi-url-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multi-url-picker/property-editor-ui-multi-url-picker.element.ts index 60ae567bad..e4111187c0 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multi-url-picker/property-editor-ui-multi-url-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multi-url-picker/property-editor-ui-multi-url-picker.element.ts @@ -3,15 +3,15 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property, state } from 'lit/decorators.js'; import type { UUIModalSidebarSize } from '@umbraco-ui/uui'; import { UmbInputMultiUrlPickerElement } from '../../../../shared/components/input-multi-url-picker/input-multi-url-picker.element'; -import { UmbLinkPickerLink } from '../../../../shared/modals/link-picker'; +import { UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN } from '../../../../shared/components/workspace-property/workspace-property.context'; +import { UmbLinkPickerLink } from '@umbraco-cms/backoffice/modal'; import { UmbPropertyEditorElement } from '@umbraco-cms/backoffice/property-editor'; -import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { DataTypePropertyPresentationModel } from '@umbraco-cms/backoffice/backend-api'; +import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; /** * @element umb-property-editor-ui-multi-url-picker */ - @customElement('umb-property-editor-ui-multi-url-picker') export class UmbPropertyEditorUIMultiUrlPickerElement extends UmbLitElement implements UmbPropertyEditorElement { static styles = [UUITextStyles]; @@ -51,6 +51,25 @@ export class UmbPropertyEditorUIMultiUrlPickerElement extends UmbLitElement impl @state() private _minNumber?: number; + @state() + private _alias?: string; + + @state() + private _propertyVariantId?: string; + + constructor() { + super(); + + this.consumeContext(UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN, (context) => { + this.observe(context.alias, (alias) => { + this._alias = alias; + }); + this.observe(context.variantId, (variantId) => { + this._propertyVariantId = variantId?.toString() || 'invariant'; + }); + }); + } + private _onChange(event: CustomEvent) { this.value = (event.target as UmbInputMultiUrlPickerElement).urls; this.dispatchEvent(new CustomEvent('property-value-change')); @@ -58,6 +77,8 @@ export class UmbPropertyEditorUIMultiUrlPickerElement extends UmbLitElement impl render() { return html`) => { + this.consumeContext(UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN, (instance: UmbWorkspacePropertyContext) => { this.propertyContext = instance; }); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/repository/manifests.ts index 34225d54b8..31a346ebde 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/repository/manifests.ts @@ -1,6 +1,6 @@ +import { ManifestRepository } from '@umbraco-cms/backoffice/extensions-registry/repository.models'; import { UmbStylesheetRepository } from './stylesheet.repository'; import { UmbStylesheetTreeStore } from './stylesheet.tree.store'; -import { ManifestRepository } from 'libs/extensions-registry/repository.models'; import { ManifestTreeStore } from '@umbraco-cms/backoffice/extensions-registry'; export const STYLESHEET_REPOSITORY_ALIAS = 'Umb.Repository.Stylesheet'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/entity-actions/manifests.ts index 81e96d6281..a85363c686 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/entity-actions/manifests.ts @@ -1,7 +1,7 @@ import { TEMPLATE_REPOSITORY_ALIAS } from '../repository/manifests'; import { UmbCreateEntityAction } from './create/create.action'; +import { ManifestEntityAction } from '@umbraco-cms/backoffice/extensions-registry'; import { UmbDeleteEntityAction } from '@umbraco-cms/backoffice/entity-action'; -import { ManifestEntityAction } from 'libs/extensions-registry/entity-action.models'; const entityActions: Array = [ { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/manifests.ts index 031c27f56a..2ff152e430 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/manifests.ts @@ -1,8 +1,7 @@ import { UmbTemplateRepository } from '../repository/template.repository'; import { UmbTemplateTreeStore } from './template.tree.store'; import { UmbTemplateStore } from './template.store'; -import { ManifestRepository } from 'libs/extensions-registry/repository.models'; -import { ManifestStore, ManifestTreeStore } from '@umbraco-cms/backoffice/extensions-registry'; +import { ManifestStore, ManifestTreeStore, ManifestRepository } from '@umbraco-cms/backoffice/extensions-registry'; export const TEMPLATE_REPOSITORY_ALIAS = 'Umb.Repository.Template'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/template.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/template.repository.ts index b8f1044c21..ec47ab953d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/template.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/template.repository.ts @@ -2,12 +2,11 @@ import { UmbTemplateDetailServerDataSource } from './sources/template.detail.ser import { TemplateTreeServerDataSource } from './sources/template.tree.server.data'; import { UmbTemplateStore, UMB_TEMPLATE_STORE_CONTEXT_TOKEN } from './template.store'; import { UmbTemplateTreeStore, UMB_TEMPLATE_TREE_STORE_CONTEXT_TOKEN } from './template.tree.store'; +import type { UmbDetailRepository, UmbTreeRepository } from '@umbraco-cms/backoffice/repository'; import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { ProblemDetailsModel, TemplateResponseModel } from '@umbraco-cms/backoffice/backend-api'; -import { UmbDetailRepository } from 'libs/repository/detail-repository.interface'; -import { UmbTreeRepository } from 'libs/repository/tree-repository.interface'; export class UmbTemplateRepository implements UmbTreeRepository, UmbDetailRepository { #init; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dashboards/dictionary/dashboard-translation-dictionary.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dashboards/dictionary/dashboard-translation-dictionary.element.ts index 8107a3c8f4..d324fb3b32 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dashboards/dictionary/dashboard-translation-dictionary.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dashboards/dictionary/dashboard-translation-dictionary.element.ts @@ -4,10 +4,9 @@ import { customElement, state } from 'lit/decorators.js'; import { when } from 'lit/directives/when.js'; import { UmbTableConfig, UmbTableColumn, UmbTableItem } from '../../../../backoffice/shared/components/table'; import { UmbDictionaryRepository } from '../../dictionary/repository/dictionary.repository'; -import { UMB_CREATE_DICTIONARY_MODAL_TOKEN } from '../../dictionary/entity-actions/create/'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { DictionaryOverviewResponseModel, LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_CREATE_DICTIONARY_MODAL } from '@umbraco-cms/backoffice/modal'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; @customElement('umb-dashboard-translation-dictionary') @@ -157,7 +156,7 @@ export class UmbDashboardTranslationDictionaryElement extends UmbLitElement { // TODO: what to do if modal service is not available? if (!this.#modalContext) return; - const modalHandler = this.#modalContext?.open(UMB_CREATE_DICTIONARY_MODAL_TOKEN, { unique: null }); + const modalHandler = this.#modalContext?.open(UMB_CREATE_DICTIONARY_MODAL, { unique: null }); // TODO: get type from modal result const { name } = await modalHandler.onSubmit(); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/create/create-dictionary-modal-layout.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/create/create-dictionary-modal-layout.element.ts index 984a59dacf..6756740c65 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/create/create-dictionary-modal-layout.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/create/create-dictionary-modal-layout.element.ts @@ -2,11 +2,11 @@ import { html } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, query } from 'lit/decorators.js'; import { when } from 'lit/directives/when.js'; -import { UmbCreateDictionaryModalData, UmbCreateDictionaryModalResult } from '.'; +import { UmbCreateDictionaryModalData, UmbCreateDictionaryModalResult } from '@umbraco-cms/backoffice/modal'; import { UmbModalBaseElement } from '@umbraco-cms/internal/modal'; @customElement('umb-create-dictionary-modal') -export class UmbCreateDictionaryModalLayoutElement extends UmbModalBaseElement< +export class UmbCreateDictionaryModalElement extends UmbModalBaseElement< UmbCreateDictionaryModalData, UmbCreateDictionaryModalResult > { @@ -71,10 +71,10 @@ export class UmbCreateDictionaryModalLayoutElement extends UmbModalBaseElement< } } -export default UmbCreateDictionaryModalLayoutElement; +export default UmbCreateDictionaryModalElement; declare global { interface HTMLElementTagNameMap { - 'umb-create-dictionary-modal': UmbCreateDictionaryModalLayoutElement; + 'umb-create-dictionary-modal': UmbCreateDictionaryModalElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/create/create.action.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/create/create.action.ts index 0f9020988f..7e0686e77a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/create/create.action.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/create/create.action.ts @@ -4,11 +4,10 @@ import { UmbSectionSidebarContext, UMB_SECTION_SIDEBAR_CONTEXT_TOKEN, } from '../../../../../backoffice/shared/components/section/section-sidebar/section-sidebar.context'; -import { UMB_CREATE_DICTIONARY_MODAL_TOKEN } from '.'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_CREATE_DICTIONARY_MODAL } from '@umbraco-cms/backoffice/modal'; // TODO: temp import import './create-dictionary-modal-layout.element'; @@ -38,7 +37,7 @@ export default class UmbCreateDictionaryEntityAction extends UmbEntityActionBase // TODO: how can we get the current entity detail in the modal? Passing the observable // feels a bit hacky. Works, but hacky. - const modalHandler = this.#modalContext?.open(UMB_CREATE_DICTIONARY_MODAL_TOKEN, { + const modalHandler = this.#modalContext?.open(UMB_CREATE_DICTIONARY_MODAL, { unique: this.unique, parentName: this.#sectionSidebarContext.headline, }); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/export/export-dictionary-modal-layout.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/export/export-dictionary-modal.element.ts similarity index 84% rename from src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/export/export-dictionary-modal-layout.element.ts rename to src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/export/export-dictionary-modal.element.ts index 75355409cb..9f746ae2e1 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/export/export-dictionary-modal-layout.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/export/export-dictionary-modal.element.ts @@ -1,11 +1,11 @@ import { html } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, query } from 'lit/decorators.js'; -import { UmbExportDictionaryModalData, UmbExportDictionaryModalResult } from '.'; +import { UmbExportDictionaryModalData, UmbExportDictionaryModalResult } from '@umbraco-cms/backoffice/modal'; import { UmbModalBaseElement } from '@umbraco-cms/internal/modal'; -@customElement('umb-export-dictionary-modal-layout') -export class UmbExportDictionaryModalLayoutElement extends UmbModalBaseElement< +@customElement('umb-export-dictionary-modal') +export class UmbExportDictionaryModalElement extends UmbModalBaseElement< UmbExportDictionaryModalData, UmbExportDictionaryModalResult > { @@ -49,10 +49,10 @@ export class UmbExportDictionaryModalLayoutElement extends UmbModalBaseElement< } } -export default UmbExportDictionaryModalLayoutElement; +export default UmbExportDictionaryModalElement; declare global { interface HTMLElementTagNameMap { - 'umb-export-dictionary-modal-layout': UmbExportDictionaryModalLayoutElement; + 'umb-export-dictionary-modal': UmbExportDictionaryModalElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/export/export.action.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/export/export.action.ts index 4ed076c561..d3351707bc 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/export/export.action.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/export/export.action.ts @@ -1,12 +1,11 @@ import { UUITextStyles } from '@umbraco-ui/uui-css'; import { UmbDictionaryRepository } from '../../repository/dictionary.repository'; -import { UMB_EXPORT_DICTIONARY_MODAL_TOKEN } from '.'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_EXPORT_DICTIONARY_MODAL } from '@umbraco-cms/backoffice/modal'; -import './export-dictionary-modal-layout.element'; +import './export-dictionary-modal.element'; export default class UmbExportDictionaryEntityAction extends UmbEntityActionBase { static styles = [UUITextStyles]; @@ -25,7 +24,7 @@ export default class UmbExportDictionaryEntityAction extends UmbEntityActionBase // TODO: what to do if modal service is not available? if (!this.#modalContext) return; - const modalHandler = this.#modalContext?.open(UMB_EXPORT_DICTIONARY_MODAL_TOKEN, { unique: this.unique }); + const modalHandler = this.#modalContext?.open(UMB_EXPORT_DICTIONARY_MODAL, { unique: this.unique }); // TODO: get type from modal result const { includeChildren } = await modalHandler.onSubmit(); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/import/import-dictionary-modal-layout.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/import/import-dictionary-modal.element.ts similarity index 93% rename from src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/import/import-dictionary-modal-layout.element.ts rename to src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/import/import-dictionary-modal.element.ts index 3dc06e07e5..92e2e6f507 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/import/import-dictionary-modal-layout.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/import/import-dictionary-modal.element.ts @@ -5,12 +5,12 @@ import { when } from 'lit/directives/when.js'; import { repeat } from 'lit/directives/repeat.js'; import { UmbTreeElement } from '../../../../shared/components/tree/tree.element'; import { UmbDictionaryRepository } from '../../repository/dictionary.repository'; -import { UmbImportDictionaryModalData, UmbImportDictionaryModalResult } from '.'; +import { UmbImportDictionaryModalData, UmbImportDictionaryModalResult } from '@umbraco-cms/backoffice/modal'; import { UploadDictionaryResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { UmbModalBaseElement } from '@umbraco-cms/internal/modal'; -@customElement('umb-import-dictionary-modal-layout') -export class UmbImportDictionaryModalLayoutElement extends UmbModalBaseElement< +@customElement('umb-import-dictionary-modal') +export class UmbImportDictionaryModalLayout extends UmbModalBaseElement< UmbImportDictionaryModalData, UmbImportDictionaryModalResult > { @@ -156,10 +156,10 @@ export class UmbImportDictionaryModalLayoutElement extends UmbModalBaseElement< } } -export default UmbImportDictionaryModalLayoutElement; +export default UmbImportDictionaryModalLayout; declare global { interface HTMLElementTagNameMap { - 'umb-import-dictionary-modal-layout': UmbImportDictionaryModalLayoutElement; + 'umb-import-dictionary-modal': UmbImportDictionaryModalLayout; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/import/import.action.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/import/import.action.ts index 562d8618dd..83c4f68cee 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/import/import.action.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/import/import.action.ts @@ -1,12 +1,11 @@ import { UUITextStyles } from '@umbraco-ui/uui-css'; import { UmbDictionaryRepository } from '../../repository/dictionary.repository'; -import { UMB_IMPORT_DICTIONARY_MODAL_TOKEN } from '.'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_IMPORT_DICTIONARY_MODAL } from '@umbraco-cms/backoffice/modal'; -import './import-dictionary-modal-layout.element'; +import './import-dictionary-modal.element'; export default class UmbImportDictionaryEntityAction extends UmbEntityActionBase { static styles = [UUITextStyles]; @@ -25,7 +24,7 @@ export default class UmbImportDictionaryEntityAction extends UmbEntityActionBase // TODO: what to do if modal service is not available? if (!this.#modalContext) return; - const modalHandler = this.#modalContext?.open(UMB_IMPORT_DICTIONARY_MODAL_TOKEN, { unique: this.unique }); + const modalHandler = this.#modalContext?.open(UMB_IMPORT_DICTIONARY_MODAL, { unique: this.unique }); // TODO: get type from modal result const { fileName, parentKey } = await modalHandler.onSubmit(); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/manifests.ts index 181aedb3fc..fc79433c06 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/manifests.ts @@ -113,13 +113,13 @@ const modals: Array = [ type: 'modal', alias: 'Umb.Modal.ExportDictionary', name: 'Export Dictionary Modal', - loader: () => import('./export/export-dictionary-modal-layout.element'), + loader: () => import('./export/export-dictionary-modal.element'), }, { type: 'modal', alias: 'Umb.Modal.ImportDictionary', name: 'Import Dictionary Modal', - loader: () => import('./import/import-dictionary-modal-layout.element'), + loader: () => import('./import/import-dictionary-modal.element'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/manifests.ts index 688a4769a7..012668b989 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/manifests.ts @@ -1,8 +1,7 @@ import { UmbDictionaryRepository } from '../repository/dictionary.repository'; import { UmbDictionaryTreeStore } from './dictionary.tree.store'; import { UmbDictionaryStore } from './dictionary.store'; -import { ManifestRepository } from 'libs/extensions-registry/repository.models'; -import { ManifestStore, ManifestTreeStore } from '@umbraco-cms/backoffice/extensions-registry'; +import { ManifestStore, ManifestTreeStore, ManifestRepository } from '@umbraco-cms/backoffice/extensions-registry'; export const DICTIONARY_REPOSITORY_ALIAS = 'Umb.Repository.Dictionary'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user-header-app.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user-header-app.element.ts index 209cf80101..baca5d0566 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user-header-app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user-header-app.element.ts @@ -2,9 +2,8 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, CSSResultGroup, html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { UmbCurrentUserStore, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN } from './current-user.store'; -import { UMB_CURRENT_USER_MODAL_TOKEN } from './modals/current-user'; import type { UserDetails } from '@umbraco-cms/backoffice/models'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_CURRENT_USER_MODAL } from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-current-user-header-app') @@ -47,7 +46,7 @@ export class UmbCurrentUserHeaderApp extends UmbLitElement { } private _handleUserClick() { - this._modalContext?.open(UMB_CURRENT_USER_MODAL_TOKEN); + this._modalContext?.open(UMB_CURRENT_USER_MODAL); } render() { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/modals/change-password/change-password-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/modals/change-password/change-password-modal.element.ts index eb8830b5a0..f5fc28ad43 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/modals/change-password/change-password-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/modals/change-password/change-password-modal.element.ts @@ -1,8 +1,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, CSSResultGroup, html, nothing } from 'lit'; import { customElement, property } from 'lit/decorators.js'; -import { UmbChangePasswordModalData } from '.'; -import { UmbModalHandler } from '@umbraco-cms/backoffice/modal'; +import { UmbModalHandler, UmbChangePasswordModalData } from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-change-password-modal') diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/modals/change-password/index.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/modals/change-password/index.ts deleted file mode 100644 index 686021241b..0000000000 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/modals/change-password/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; - -export interface UmbChangePasswordModalData { - requireOldPassword: boolean; -} - -export const UMB_CHANGE_PASSWORD_MODAL_TOKEN = new UmbModalToken( - 'Umb.Modal.ChangePassword', - { - type: 'dialog', - } -); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/modals/current-user/current-user-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/modals/current-user/current-user-modal.element.ts index 959687fd9c..3b60bc0f36 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/modals/current-user/current-user-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/modals/current-user/current-user-modal.element.ts @@ -7,8 +7,12 @@ import { UMB_CURRENT_USER_HISTORY_STORE_CONTEXT_TOKEN, } from '../../current-user-history.store'; import { UmbCurrentUserStore, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN } from '../../current-user.store'; -import { UMB_CHANGE_PASSWORD_MODAL_TOKEN } from '../change-password'; -import { UmbModalHandler, UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { + UmbModalHandler, + UmbModalContext, + UMB_MODAL_CONTEXT_TOKEN, + UMB_CHANGE_PASSWORD_MODAL, +} from '@umbraco-cms/backoffice/modal'; import type { UserDetails } from '@umbraco-cms/backoffice/models'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -139,7 +143,7 @@ export class UmbCurrentUserModalElement extends UmbLitElement { private _changePassword() { if (!this._modalContext) return; - this._modalContext.open(UMB_CHANGE_PASSWORD_MODAL_TOKEN, { + this._modalContext.open(UMB_CHANGE_PASSWORD_MODAL, { requireOldPassword: this._currentUserStore?.isAdmin || false, }); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-section/views/users/workspace-view-users-overview.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-section/views/users/workspace-view-users-overview.element.ts index b0e9bb188b..5aa001acf5 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-section/views/users/workspace-view-users-overview.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-section/views/users/workspace-view-users-overview.element.ts @@ -2,10 +2,13 @@ import { css, html, nothing } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, state } from 'lit/decorators.js'; import { UUIPopoverElement } from '@umbraco-ui/uui'; -import { UMB_INVITE_USER_MODAL_TOKEN } from '../../../../users/users/modals/invite-user'; -import { UMB_CREATE_USER_MODAL_TOKEN } from '../../../../users/users/modals/create-user'; import type { UmbSectionViewUsersElement } from './section-view-users.element'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal'; +import { + UmbModalContext, + UMB_MODAL_CONTEXT_TOKEN, + UMB_INVITE_USER_MODAL, + UMB_CREATE_USER_MODAL, +} from '@umbraco-cms/backoffice/modal'; import type { IRoute } from '@umbraco-cms/internal/router'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -160,9 +163,9 @@ export class UmbWorkspaceViewUsersOverviewElement extends UmbLitElement { let token = undefined; // TODO: we need to find a better way to determine if we should create or invite if (this.isCloud) { - token = UMB_INVITE_USER_MODAL_TOKEN; + token = UMB_INVITE_USER_MODAL; } else { - token = UMB_CREATE_USER_MODAL_TOKEN; + token = UMB_CREATE_USER_MODAL; } this._modalContext?.open(token); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace-edit.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace-edit.element.ts index 67f89f7402..06e603d4d9 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace-edit.element.ts @@ -6,8 +6,8 @@ import { ifDefined } from 'lit/directives/if-defined.js'; import { repeat } from 'lit/directives/repeat.js'; import { UmbCurrentUserStore, UMB_CURRENT_USER_STORE_CONTEXT_TOKEN } from '../../current-user/current-user.store'; -import { UMB_CHANGE_PASSWORD_MODAL_TOKEN } from '../../current-user/modals/change-password'; import { UmbUserWorkspaceContext } from './user-workspace.context'; +import { UMB_CHANGE_PASSWORD_MODAL } from '@umbraco-cms/backoffice/modal'; import type { UmbModalContext } from '@umbraco-cms/backoffice/modal'; import { getLookAndColorFromUserStatus } from '@umbraco-cms/backoffice/utils'; import type { UserDetails } from '@umbraco-cms/backoffice/models'; @@ -181,7 +181,7 @@ export class UmbUserWorkspaceEditElement extends UmbLitElement { } private _changePassword() { - this._modalContext?.open(UMB_CHANGE_PASSWORD_MODAL_TOKEN, { + this._modalContext?.open(UMB_CHANGE_PASSWORD_MODAL, { requireOldPassword: this._currentUserStore?.isAdmin === false, }); } diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/data/document-type.data.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/data/document-type.data.ts index 1f418b6b0c..ec6d416519 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/data/document-type.data.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/data/document-type.data.ts @@ -84,7 +84,7 @@ export const data: Array = [ name: 'Multi URL Picker', description: '', dataTypeKey: 'dt-multiUrlPicker', - variesByCulture: false, + variesByCulture: true, variesBySegment: false, validation: { mandatory: true, diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/data/document.data.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/data/document.data.ts index 49e4d48e49..e1fa80708c 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/data/document.data.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/data/document.data.ts @@ -50,7 +50,24 @@ export const data: Array = [ { $type: '', alias: 'multiUrlPicker', - culture: null, + culture: 'en-us', + segment: null, + value: [ + { + name: undefined, + published: undefined, + queryString: undefined, + target: undefined, + trashed: undefined, + udi: 'umb://document/c05da24d7740447b9cdcbd8ce2172e38', + url: 'umb://document/c05da24d7740447b9cdcbd8ce2172e38', + }, + ], + }, + { + $type: '', + alias: 'multiUrlPicker', + culture: 'da-dk', segment: null, value: null, }, @@ -269,6 +286,16 @@ export const data: Array = [ createDate: '2023-02-06T15:31:46.876902', updateDate: '2023-02-06T15:31:51.354764', }, + { + $type: '', + state: ContentStateModel.PUBLISHED, + publishDate: '2023-02-06T15:31:51.354764', + culture: 'da-dk', + segment: null, + name: 'Alle redigeringsfelter', + createDate: '2023-02-06T15:31:46.876902', + updateDate: '2023-02-06T15:31:51.354764', + }, ], }, { diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/domains/rte-embed.handlers.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/domains/rte-embed.handlers.ts index 3a8b1247e1..f08e233ed5 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/domains/rte-embed.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/domains/rte-embed.handlers.ts @@ -1,17 +1,17 @@ -import { rest } from "msw"; -import { OEmbedResult, OEmbedStatus } from "../../../backoffice/shared/modals/embedded-media"; -import { umbracoPath } from "@umbraco-cms/backoffice/utils"; +import { rest } from 'msw'; +import { OEmbedResult, OEmbedStatus } from '../../../backoffice/shared/modals/embedded-media'; +import { umbracoPath } from '@umbraco-cms/backoffice/utils'; export const handlers = [ rest.get(umbracoPath('/rteembed'), (req, res, ctx) => { const width = req.url.searchParams.get('width') ?? 360; const height = req.url.searchParams.get('height') ?? 240; const response: OEmbedResult = { - supportsDimensions: true, + supportsDimensions: true, markup: ``, - oEmbedStatus: OEmbedStatus.Success, + oEmbedStatus: OEmbedStatus.Success, }; return res(ctx.status(200), ctx.json(response)); }), -]; \ No newline at end of file +]; diff --git a/src/Umbraco.Web.UI.Client/src/core/modal/modal-element.element.ts b/src/Umbraco.Web.UI.Client/src/core/modal/modal-element.element.ts index 4463c9c8fa..005bf6baac 100644 --- a/src/Umbraco.Web.UI.Client/src/core/modal/modal-element.element.ts +++ b/src/Umbraco.Web.UI.Client/src/core/modal/modal-element.element.ts @@ -3,7 +3,7 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbModalHandler } from '@umbraco-cms/backoffice/modal'; @customElement('umb-modal-element') -export class UmbModalBaseElement extends UmbLitElement { +export class UmbModalBaseElement extends UmbLitElement { @property({ attribute: false }) modalHandler?: UmbModalHandler; @@ -13,6 +13,6 @@ export class UmbModalBaseElement ext declare global { interface HTMLElementTagNameMap { - 'umb-modal-element': UmbModalBaseElement; + 'umb-modal-element': UmbModalBaseElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/core/modal/stories/modal.mdx b/src/Umbraco.Web.UI.Client/src/core/modal/stories/modal.mdx index 9cb19bc473..e04cdacc9e 100644 --- a/src/Umbraco.Web.UI.Client/src/core/modal/stories/modal.mdx +++ b/src/Umbraco.Web.UI.Client/src/core/modal/stories/modal.mdx @@ -42,7 +42,138 @@ class MyElement extends UmbElementMixin(LitElement) { ### Open a modal -A modal is opened by calling the open method on the UmbModalContext. The methods will accept a modal token (or extension alias), an optional dataset, and optional modal options .It returns an instance of UmbModalHandler. +A modal can be opned in two ways. Either you registrer the modal with a route or at runtime open the modal. Notice the first one will enable users to deep link to the modal, this might be preferable in some cases if not go for the last. + +#### Simple Modal Route Registration + +A modal can be registred via the UmbModalRouteRegistrationController. The registration will accept a modal token (or extension alias). + +Notice we are using a Controller here, this means your element has to be a Controller Host (TODO: Insert link to story about Controller Host also available through the UmbElementMixin) + +```ts +this.myModalRegistration = new UmbModalRouteRegistrationController(this, UMB_LINK_PICKER_MODAL) + .onSubmit((submitData) => { + console.log('Modal submitted with data'.submitData); + }) + .observeRouteBuilder((routeBuilder) => { + this._modalRouteBuilder = routeBuilder; + }); +``` + +The registration holds an instance of its UmbModalHandler when the modal it active. +The modal registration accepts 4 different callbacks: + +- onSetup - called when the modal is opened +- onSubmit - called when the modal is submitted +- onReject - called when the modal is rejected +- observeRouteBuilder - called when the modal route changes, use the given route builder to build a route to open the modal. + +#### TODOS: + +descripe the addional features of the route Registration: + +##### Hints: + +- Add unique parts to the path. (How is this cone properly as part of a Property Editor) +- A modal registred in a dashboard can be relatively simple. +- A modal registred in a property editor, needs to become specific for the property and the variant of that property. + +- Build some data for the setup. +- Reject a modal by returning false in setup. +- Use a param as part of setup to determin the data going to the modal. + +#### Modal registration for UI as part of a Property Editor + +```ts + + + @property() + public set alias(value: string | undefined) { + this.myModalRegistration.setUniqueIdentifier('propertyAlias', value); + } + + @property() + public set variantId(value: string | UmbVariantId | undefined) { + this.myModalRegistration.setUniqueIdentifier('variantId', value?.toString()); + } + + private _items = [ + { name: 'Item 1' }, + { name: 'Item 2' }, + { name: 'Item 3' }, + ] + + + constructor() { + super(); + + this.myModalRegistration = new UmbModalRouteRegistrationController( + this, + MY_MODAL_TOKEN, + `:index`, + new Map([ + ['propertyAlias', undefined], + ['variantId', undefined], + ]) + ) + .onSetup((params) => { + // Get item index: + const indexParam = params.index; + if (!indexParam) return false; + let index: number | null = parseInt(params.index); + if (Number.isNaN(index)) return false; + + // Use the index to find data: + let data = null; + if (index >= 0 && index < this._items.length) { + data = this._items[index]; + } else { + // If not then make a new pick: + index = null; + } + + return { + index: index, + itemData: { + name: data?.name + }, + }; + }) + .onSubmit((submitData) => { + if (!submitData) return; + this._items[submitData.index] = submitData.itemData; + }) + .observeRouteBuilder((routeBuilder) => { + this._modalRouteBuilder = routeBuilder; + }); + } + + render() { + return html` + ${this._items?.map((item, index) => + html`Add` + )} + `; + } +``` + +#### Generate the URL to a Modal Route Registration + +The Modal registration has an option to retrive a URL Builder, this is a function that can be used to generate a URL to a modal. + +```ts +const modalLink = _myUrlBuilder?.({ alias: 'my-input-alias' }); +``` + +The modalLink from above could look like: /umbraco/backoffice/my/location/modal/Our.Modal.SomethingPicker/my-input-alias + +Notice the Property Editor registration will add the property alias and variant id to the URL, so it becomes: + +/umbraco/backoffice/my/location/modal/Our.Modal.SomethingPicker/my-property-alias/en-us/my-input-alias + +#### Open A Modal at own initiative + +A modal can be opened by calling the open method on the UmbModalContext. The methods will accept a modal token (or extension alias), an optional dataset, and optional modal options. It returns an instance of UmbModalHandler. ```ts import { html, LitElement } from 'lit'; @@ -53,7 +184,7 @@ class MyElement extends UmbElementMixin(LitElement) { constructor() { super(); - this.consumeContext(UMB_MODAL_CONTEXT_ALIAS, (instance) => { + this.consumeContext(UMB_MODAL_CONTEXT_TOKEN, (instance) => { this.#modalContext = instance; // modalContext is now ready to be used }); @@ -62,7 +193,7 @@ class MyElement extends UmbElementMixin(LitElement) { #onClick() { const data = {'data goes here'}; const options {'options go here'}; - const modalHandler = this.#modalContext?.open(SOME_MODAL_TOKEN), data, options); + const modalHandler = this.#modalContext?.open(MY_MODAL_TOKEN), data, options); modalHandler?.onSubmit().then((data) => { // if modal submitted, then data is supplied here. diff --git a/src/Umbraco.Web.UI.Client/src/core/router/index.ts b/src/Umbraco.Web.UI.Client/src/core/router/index.ts index e05a188067..d3c5361842 100644 --- a/src/Umbraco.Web.UI.Client/src/core/router/index.ts +++ b/src/Umbraco.Web.UI.Client/src/core/router/index.ts @@ -2,4 +2,4 @@ export * from 'router-slot'; export * from './router-slot.element'; export * from './router-slot-change.event'; export * from './router-slot-init.event'; -export * from './route-location.interface'; +export * from './variant-router-slot.element'; diff --git a/src/Umbraco.Web.UI.Client/src/core/router/router-slot.element.ts b/src/Umbraco.Web.UI.Client/src/core/router/router-slot.element.ts index 4f33902f87..1dc354dc29 100644 --- a/src/Umbraco.Web.UI.Client/src/core/router/router-slot.element.ts +++ b/src/Umbraco.Web.UI.Client/src/core/router/router-slot.element.ts @@ -1,40 +1,45 @@ import type { IRoute } from 'router-slot/model'; import { RouterSlot } from 'router-slot'; -import { css, LitElement, PropertyValueMap } from 'lit'; +import { css, html, PropertyValueMap } from 'lit'; import { customElement, property } from 'lit/decorators.js'; +import { UmbLitElement } from '../lit-element'; import { UmbRouterSlotInitEvent } from './router-slot-init.event'; import { UmbRouterSlotChangeEvent } from './router-slot-change.event'; +import { UmbRouteContext, UmbRoute } from '@umbraco-cms/backoffice/router'; /** * @element umb-router-slot * @description - Component for wrapping Router Slot element, providing some local events for implementation. - * @extends UmbRouterSlotElement + * @extends UmbLitElement * @fires {UmbRouterSlotInitEvent} init - fires when the router is connected * @fires {UmbRouterSlotChangeEvent} change - fires when a path of this router is changed */ @customElement('umb-router-slot') -export class UmbRouterSlotElement extends LitElement { - static styles = [css` - :host { - display:flex; - flex-direction:column; - height:100%; - } +export class UmbRouterSlotElement extends UmbLitElement { + static styles = [ + css` + :host { + display: flex; + flex-direction: column; + height: 100%; + } - router-slot { - height:100%; - } - `] + router-slot { + height: 100%; + } + `, + ]; #router: RouterSlot = new RouterSlot(); + #modalRouter: RouterSlot = new RouterSlot(); #listening = false; @property() - public get routes(): IRoute[] | undefined { + public get routes(): UmbRoute[] | undefined { return (this.#router as any).routes; } - public set routes(value: IRoute[] | undefined) { - (this.#router as any).routes = value; + public set routes(value: UmbRoute[] | undefined) { + this.#router.routes = (value as IRoute[]) || []; } private _routerPath?: string; @@ -51,14 +56,29 @@ export class UmbRouterSlotElement extends LitElement { return this._routerPath + '/' + this._activeLocalPath; } + #routeContext = new UmbRouteContext(this, (contextRoutes) => { + (this.#modalRouter as any).routes = contextRoutes; + // Force a render? + this.#modalRouter.render(); + }); + constructor() { super(); - this.#router.addEventListener('changestate', this._onChangeState); + this.#modalRouter.parent = this.#router; + this.#router.addEventListener('changestate', this._updateRouterPath.bind(this)); + //this.#router.appendChild(this.#modalRouter); this.#router.appendChild(document.createElement('slot')); } + protected _constructAbsoluteRouterPath() { + return this.#router.constructAbsolutePath('') || ''; + } + connectedCallback() { super.connectedCallback(); + // Currently we have to set this every time as RouteSlot looks for its parent every-time it is connected. Aka it has not way to explicitly set the parent. + // And we cannot insert the modal router as a slotted-child of the router, as it flushes its children on every route change. + this.#modalRouter.parent = this.#router; if (this.#listening === false) { window.addEventListener('navigationsuccess', this._onNavigationChanged); this.#listening = true; @@ -73,14 +93,16 @@ export class UmbRouterSlotElement extends LitElement { protected firstUpdated(_changedProperties: PropertyValueMap | Map): void { super.firstUpdated(_changedProperties); - this._routerPath = this.#router.constructAbsolutePath('') || ''; + this._routerPath = this._constructAbsoluteRouterPath(); + this.#routeContext._internal_routerGotBasePath(this._routerPath); this.dispatchEvent(new UmbRouterSlotInitEvent()); } - private _onChangeState = () => { - const newAbsolutePath = this.#router.constructAbsolutePath('') || ''; + protected _updateRouterPath() { + const newAbsolutePath = this._constructAbsoluteRouterPath(); if (this._routerPath !== newAbsolutePath) { this._routerPath = newAbsolutePath; + this.#routeContext._internal_routerGotBasePath(this._routerPath); this.dispatchEvent(new UmbRouterSlotInitEvent()); const newActiveLocalPath = this.#router.match?.route.path; @@ -89,17 +111,20 @@ export class UmbRouterSlotElement extends LitElement { this.dispatchEvent(new UmbRouterSlotChangeEvent()); } } - }; + } private _onNavigationChanged = (event?: any) => { if (event.detail.slot === this.#router) { this._activeLocalPath = event.detail.match.route.path; this.dispatchEvent(new UmbRouterSlotChangeEvent()); + } else if (event.detail.slot === this.#modalRouter) { + const newActiveModalLocalPath = event.detail.match.route.path; + this.#routeContext._internal_modalRouterChanged(newActiveModalLocalPath); } }; render() { - return this.#router; + return html`${this.#router}${this.#modalRouter}`; } } diff --git a/src/Umbraco.Web.UI.Client/src/core/router/variant-router-slot.element.ts b/src/Umbraco.Web.UI.Client/src/core/router/variant-router-slot.element.ts new file mode 100644 index 0000000000..c5e125fee7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/router/variant-router-slot.element.ts @@ -0,0 +1,79 @@ +import { customElement, property } from 'lit/decorators.js'; +import { UmbVariantId } from '../../backoffice/shared/variants/variant-id.class'; +import { UmbRouterSlotElement } from './router-slot.element'; +import { UmbRoute } from '@umbraco-cms/backoffice/router'; + +function variantIdsToString(variantIds: UmbVariantId[]): string { + return variantIds.map((id) => id.toString()).join('_&_'); +} + +/** + * @element umb-variant-router-slot-element + * @description - Component for wrapping Router Slot element, providing + * @extends UmbRouterSlotElement + * @fires {UmbRouterSlotInitEvent} init - fires when the router is connected + * @fires {UmbRouterSlotChangeEvent} change - fires when a path of this router is changed + */ +@customElement('umb-variant-router-slot') +export class UmbVariantRouterSlotElement extends UmbRouterSlotElement { + #variantIds: UmbVariantId[] = []; + + #getPathPrefix() { + return variantIdsToString(this.#variantIds); + } + + #currentPathPrefix = ''; + private _routes?: UmbRoute[]; + public get routes(): UmbRoute[] | undefined { + return this._routes; + } + public set routes(value: UmbRoute[] | undefined) { + this._routes = value; + if (this.#variantIds.length > 0) { + this._updateRoutes(); + } + } + + private _updateRoutes() { + const newPrefix = this.#getPathPrefix(); + if (newPrefix !== this.#currentPathPrefix) { + this.#currentPathPrefix = newPrefix; + const prepend = newPrefix === '' ? '' : newPrefix + '/'; + const mappedRoutes = this._routes?.map((route) => { + return { + ...route, + path: prepend + route.path, + }; + }); + super.routes = mappedRoutes; + this._updateRouterPath(); + } + } + + @property() + public get variantId(): UmbVariantId[] { + return this.#variantIds; + } + public set variantId(value: UmbVariantId[] | UmbVariantId) { + if (Array.isArray(value)) { + this.#variantIds = [...(value as UmbVariantId[])]; + } else if (value) { + this.#variantIds = [value]; + } else { + this.#variantIds = []; + } + if (this._routes) { + this._updateRoutes(); + } + } + + protected _constructAbsoluteRouterPath() { + return super._constructAbsoluteRouterPath() + (this.#currentPathPrefix !== '' ? '/' + this.#currentPathPrefix : ''); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-variant-router-slot': UmbVariantRouterSlotElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/stories/extending/modals/intro.mdx b/src/Umbraco.Web.UI.Client/src/stories/extending/modals/intro.mdx new file mode 100644 index 0000000000..cde473b6b8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/stories/extending/modals/intro.mdx @@ -0,0 +1,57 @@ +import { Meta } from '@storybook/addon-docs'; + + + +# Modals + +// TODO: add description about what modals is + +- Sidebar +- Infinite Editors +- Dialogs + +## Define a Manifest for a Modal Type + +### Manifest + +```json +{ + "type": "modal", + "alias": "Our.Modal.SomethingPicker", + "name": "My Something Picker Modal", + "loader": "./my-something-picker-modal-element.js", +}, +``` + +## Implement a Modal + +### Modal Token + +For type safety we recommend that you make a Modal Token, Its posible to go without. +The Modal Token binds the Modal Type to the Modal Data Type and Modal Result Type. + +`` + +```ts +import { ModalToken } from '@umbraco-cms/element'; + +export type OurSomethingPickerModalData = { + key: string | null; +}; + +export type OurSomethingPickerModalResult = { + key: string; +}; + +export const MY_SOMETHING_PICKER_MODAL = new UmbModalToken( + 'Our.Modal.SomethingPicker', + { + type: 'sidebar', + size: 'small', + } +); +``` + +### Make a modal registration + +# TODO Link to modal documentation here. diff --git a/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index 40bbd59d48..147a4a8288 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/tsconfig.json @@ -35,6 +35,7 @@ "@umbraco-cms/backoffice/property-editor": ["libs/property-editor"], "@umbraco-cms/backoffice/repository": ["libs/repository"], "@umbraco-cms/backoffice/resources": ["libs/resources"], + "@umbraco-cms/backoffice/router": ["libs/router"], "@umbraco-cms/backoffice/store": ["libs/store"], "@umbraco-cms/backoffice/utils": ["libs/utils"], "@umbraco-cms/backoffice/workspace": ["libs/workspace"], diff --git a/src/Umbraco.Web.UI.Client/vite.cms.config.ts b/src/Umbraco.Web.UI.Client/vite.cms.config.ts index b174cd7f82..2d0006a27b 100644 --- a/src/Umbraco.Web.UI.Client/vite.cms.config.ts +++ b/src/Umbraco.Web.UI.Client/vite.cms.config.ts @@ -14,7 +14,7 @@ export default defineConfig({ external: [/^@umbraco-cms\/backoffice\//] }, outDir: '../Umbraco.Cms.StaticAssets/wwwroot/umbraco/backoffice', - emptyOutDir: true, + emptyOutDir: false, sourcemap: true, }, base: '/umbraco/backoffice/',