diff --git a/src/Umbraco.Web.UI.Client/.github/CONTRIBUTING.md b/src/Umbraco.Web.UI.Client/.github/CONTRIBUTING.md index 6a340b6392..f301c54a71 100644 --- a/src/Umbraco.Web.UI.Client/.github/CONTRIBUTING.md +++ b/src/Umbraco.Web.UI.Client/.github/CONTRIBUTING.md @@ -15,9 +15,9 @@ Here is the LIT documentation and playground: [https://lit.dev](https://lit.dev) - Read the [README](README.md) to learn how to get the project up and running - Find an issue marked as [community/up-for-grabs](https://github.com/umbraco/Umbraco.CMS.Backoffice/issues?q=is%3Aissue+is%3Aopen+label%3Acommunity%2Fup-for-grabs) - note that some are also marked [good first issue](https://github.com/umbraco/Umbraco.CMS.Backoffice/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) which indicates they are simple to get started on -- Umbraco HQ owns the Management API on the backend, so features can be worked on in the frontend only when there is an API, or otherwise, if no API is required +- Umbraco HQ owns the Management API on the backend, so features can be worked on in the frontend only when there is an API, or otherwise if no API is required - A contribution should be made in a fork of the repository -- Once a contribution is ready, a pull request should be made towards this repository and HQ will assign a reviewer +- Once a contribution is ready, a pull request should be made to this repository and HQ will assign a reviewer - A pull request should always indicate what part of a feature it tries to solve, i.e. does it close the targeted issue (if any) or does the developer expect Umbraco HQ to take over ## Contributing in general terms @@ -115,7 +115,7 @@ To declare the Published Cache Status Dashboard as a new manifest, we need to ad alias: 'Umb.Dashboard.PublishedStatus', name: 'Published Status Dashboard', elementName: 'umb-dashboard-published-status', - loader: () => import('./published-status/dashboard-published-status.element.js'), + element: () => import('./published-status/dashboard-published-status.element.js'), weight: 200, meta: { label: 'Published Status', @@ -152,17 +152,17 @@ Let’s go through each of these properties… @customElement('umb-dashboard-published-status') ``` -- Loader: references a function call to import the file that the element is declared within +- Js: references a function call to import the file that the element is declared within - Weight: allows us to specify the order in which the dashboard will be displayed within the tabs bar -- Meta: allows us to reference additional data - in our case we can specify the label that is shown in the tabs bar and the pathname that will be displayed in the url +- Meta: allows us to reference additional data - in our case, we can specify the label that is shown in the tabs bar and the pathname that will be displayed in the URL -- Conditions: allows us to specify the conditions that must be met in order for the dashboard to be displayed. In our case we are specifying that the dashboard will only be displayed within the Settings section +- Conditions: allows us to specify the conditions that must be met for the dashboard to be displayed. In our case, we are specifying that the dashboard will only be displayed within the Settings section ## API mock handlers -Running the app with `npm run dev`, you will quickly notice the API requests turn into 404 errors. In order to hit the API, we need to add a mock handler to define the endpoints which our dashboard will call. In the case of the Published Cache Status section, we have a number of calls to work through. Let’s start by looking at the call to retrieve the current status of the cache: +Running the app with `npm run dev`, you will quickly notice the API requests turn into 404 errors. To hit the API, we need to add a mock handler to define the endpoints that our dashboard will call. In the case of the Published Cache Status section, we have several calls to work through. Let’s start by looking at the call to retrieve the current status of the cache: ![Published Status Dashboard](/.github/images/contributing/status-of-cache.png) @@ -180,8 +180,8 @@ export const handlers = [ // Respond with a 200 status code ctx.status(200), ctx.json( - 'Database cache is ok. ContentStore contains 1 item and has 1 generation and 0 snapshot. MediaStore contains 5 items and has 1 generation and 0 snapshot.' - ) + 'Database cache is ok. ContentStore contains 1 item and has 1 generation and 0 snapshot. MediaStore contains 5 items and has 1 generation and 0 snapshot.', + ), ); }), ]; @@ -195,7 +195,7 @@ An example `POST` is similar. Let’s take the “Refresh status” button as an ![Published Status Dashboard](/.github/images/contributing/refresh-status.png) -From our existing functionality we can see that this makes a `POST`call to the server to prompt a reload of the published cache. So we would add a new endpoint to the mock handler that would look like: +From our existing functionality, we can see that this makes a `POST` call to the server to prompt a reload of the published cache. So we would add a new endpoint to the mock handler that would look like: ```typescript rest.post(umbracoPath('/published-cache/reload'), async (_req, res, ctx) => { @@ -216,7 +216,7 @@ This call returns a simple `OK` status code and no other object. We try to make good Storybook stories for new components, which is a nice way to work with a component in an isolated state. Imagine you are working with a dialog on page 3 and have to navigate back to that every time you make a change - this is now eliminated with Storybook as you can just make a story that displays that step. Storybook can only show one component at a time, so it also helps us to isolate view logic into more and smaller components, which in turn are more testable. -In depth: [https://storybook.js.org/docs/web-components/get-started/introduction](https://storybook.js.org/docs/web-components/get-started/introduction) +In-depth: [https://storybook.js.org/docs/web-components/get-started/introduction](https://storybook.js.org/docs/web-components/get-started/introduction) Reference: [https://ambitious-stone-0033b3603.1.azurestaticapps.net/](https://ambitious-stone-0033b3603.1.azurestaticapps.net/) diff --git a/src/Umbraco.Web.UI.Client/.vscode/settings.json b/src/Umbraco.Web.UI.Client/.vscode/settings.json index d94391ca3b..c55d22e11e 100644 --- a/src/Umbraco.Web.UI.Client/.vscode/settings.json +++ b/src/Umbraco.Web.UI.Client/.vscode/settings.json @@ -4,6 +4,8 @@ "backoffice", "Backoffice", "combobox", + "ctrls", + "devs", "Elementable", "invariantable", "lucide", diff --git a/src/Umbraco.Web.UI.Client/apps/auth/src/external-login-providers/manifests.ts b/src/Umbraco.Web.UI.Client/apps/auth/src/external-login-providers/manifests.ts index d3153587ae..534b6a9cc2 100644 --- a/src/Umbraco.Web.UI.Client/apps/auth/src/external-login-providers/manifests.ts +++ b/src/Umbraco.Web.UI.Client/apps/auth/src/external-login-providers/manifests.ts @@ -7,7 +7,7 @@ export const manifests: Array = [ alias: 'Umb.ExternalLoginProvider.Test', name: 'Test External Login Provider', elementName: 'umb-external-login-provider-test', - loader: () => import('./external-login-provider-test.element.js'), + element: () => import('./external-login-provider-test.element.js'), weight: 2, meta: { label: 'Test External Login Provider', @@ -19,7 +19,7 @@ export const manifests: Array = [ alias: 'Umb.ExternalLoginProvider.Test2', name: 'Test External Login Provider 2', elementName: 'umb-external-login-provider-test2', - loader: () => import('./external-login-provider-test2.element.js'), + element: () => import('./external-login-provider-test2.element.js'), weight: 1, meta: { label: 'Test External Login Provider 2', diff --git a/src/Umbraco.Web.UI.Client/devops/plop/templates/property-editor-ui/element.ts.hbs b/src/Umbraco.Web.UI.Client/devops/plop/templates/property-editor-ui/element.ts.hbs index d2f7851d28..e578918f13 100644 --- a/src/Umbraco.Web.UI.Client/devops/plop/templates/property-editor-ui/element.ts.hbs +++ b/src/Umbraco.Web.UI.Client/devops/plop/templates/property-editor-ui/element.ts.hbs @@ -1,5 +1,6 @@ -import { html } from 'lit'; -import { customElement, property } from 'lit/decorators.js'; +import { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; +import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbLitElement } from '@umbraco-cms/element'; @@ -7,14 +8,14 @@ import { UmbLitElement } from '@umbraco-cms/element'; * @element {{ extensionTagName extensionType name }} */ @customElement('{{ extensionTagName extensionType name }}') -export class {{className extensionType name }} extends UmbLitElement { +export class {{className extensionType name }} extends UmbLitElement implements UmbPropertyEditorUiElement { static styles = [UmbTextStyles]; @property() value = ''; @property({ type: Array, attribute: false }) - public config = []; + public config?: UmbPropertyEditorConfigCollection; render() { return html`
{{ extensionTagName extensionType name }}
`; diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 8b2e117b61..6917c9a275 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -76,9 +76,9 @@ "rollup-plugin-web-worker-loader": "^1.6.1", "storybook": "7.5.3", "tiny-glob": "^0.2.9", - "tsc-alias": "^1.8.7", + "tsc-alias": "^1.8.8", "typescript": "^5.1.6", - "typescript-json-schema": "^0.59.0", + "typescript-json-schema": "^0.62.0", "vite": "^4.4.9", "vite-plugin-static-copy": "^0.17.0", "vite-tsconfig-paths": "^4.2.0", @@ -20734,9 +20734,9 @@ "dev": true }, "node_modules/tsc-alias": { - "version": "1.8.7", - "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.7.tgz", - "integrity": "sha512-59Q/zUQa3miTf99mLbSqaW0hi1jt4WoG8Uhe5hSZJHQpSoFW9eEwvW7jlKMHXWvT+zrzy3SN9PE/YBhQ+WVydA==", + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.8.tgz", + "integrity": "sha512-OYUOd2wl0H858NvABWr/BoSKNERw3N9GTi3rHPK8Iv4O1UyUXIrTTOAZNHsjlVpXFOhpJBVARI1s+rzwLivN3Q==", "dev": true, "dependencies": { "chokidar": "^3.5.3", @@ -20909,9 +20909,9 @@ } }, "node_modules/typescript-json-schema": { - "version": "0.59.0", - "resolved": "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.59.0.tgz", - "integrity": "sha512-eYB9RO8p4PntznWUukdDQHckNfxzjEFCJUgsWeCE43mcFioE0wXGTSECGk1uhty9XQMxkpuI4pKAqqnb62ln3Q==", + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.62.0.tgz", + "integrity": "sha512-qRO6pCgyjKJ230QYdOxDRpdQrBeeino4v5p2rYmSD72Jf4rD3O+cJcROv46sQukm46CLWoeusqvBgKpynEv25g==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", @@ -20920,7 +20920,7 @@ "path-equal": "^1.2.5", "safe-stable-stringify": "^2.2.0", "ts-node": "^10.9.1", - "typescript": "~4.9.5", + "typescript": "~5.1.0", "yargs": "^17.1.1" }, "bin": { @@ -20947,19 +20947,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/typescript-json-schema/node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, "node_modules/typical": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 8c61b584f4..b29e814b79 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -194,9 +194,9 @@ "rollup-plugin-web-worker-loader": "^1.6.1", "storybook": "7.5.3", "tiny-glob": "^0.2.9", - "tsc-alias": "^1.8.7", + "tsc-alias": "^1.8.8", "typescript": "^5.1.6", - "typescript-json-schema": "^0.59.0", + "typescript-json-schema": "^0.62.0", "vite": "^4.4.9", "vite-plugin-static-copy": "^0.17.0", "vite-tsconfig-paths": "^4.2.0", diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts index e9c797684b..abddc9b622 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts @@ -1,7 +1,7 @@ import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbBasicState, UmbStringState } from '@umbraco-cms/backoffice/observable-api'; -import { UmbExtensionManifestInitializer, UmbExtensionsManifestController } from '@umbraco-cms/backoffice/extension-api'; -import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbExtensionManifestInitializer, UmbExtensionsManifestInitializer } from '@umbraco-cms/backoffice/extension-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { ManifestSection, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; export class UmbBackofficeContext { @@ -13,7 +13,7 @@ export class UmbBackofficeContext { public readonly allowedSections = this.#allowedSections.asObservable(); constructor(host: UmbControllerHost) { - new UmbExtensionsManifestController(host, umbExtensionsRegistry, 'section', null, (sections) => { + new UmbExtensionsManifestInitializer(host, umbExtensionsRegistry, 'section', null, (sections) => { this.#allowedSections.next([...sections]); }); } diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-main.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-main.element.ts index 625097f0d6..46bb96b2c6 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-main.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-main.element.ts @@ -4,8 +4,7 @@ import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '@umbraco-cms/backo import type { UmbRoute, UmbRouterSlotChangeEvent } from '@umbraco-cms/backoffice/router'; import type { ManifestSection, UmbSectionElement } from '@umbraco-cms/backoffice/extension-registry'; import { - UmbExtensionManifestInitializer, - createExtensionElement, + UmbExtensionManifestInitializer, createExtensionElement } from '@umbraco-cms/backoffice/extension-api'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -56,7 +55,7 @@ export class UmbBackofficeMainElement extends UmbLitElement { return { alias: section.alias, path: this._routePrefix + (section.manifest as ManifestSection).meta.pathname, - component: () => createExtensionElement((section.manifest as ManifestSection), 'umb-section-default'), + component: () => createExtensionElement(section.manifest!, 'umb-section-default'), setup: (component) => { (component as UmbSectionElement).manifest = section.manifest as ManifestSection; }, diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/server-extension-registrator.controller.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/server-extension-registrator.controller.ts index fc56c583c0..fc11e076de 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/server-extension-registrator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/server-extension-registrator.controller.ts @@ -1,11 +1,10 @@ import { PackageResource, OpenAPI } from '@umbraco-cms/backoffice/backend-api'; -import { UmbBaseController, UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbBaseController, type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbBackofficeExtensionRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; -import { ManifestBase, isManifestJSType } from '@umbraco-cms/backoffice/extension-api'; +import { ManifestBase, isManifestBaseType } from '@umbraco-cms/backoffice/extension-api'; // TODO: consider if this can be replaced by the new extension controllers -// TODO: move local part out of this, and name something with server. export class UmbServerExtensionRegistrator extends UmbBaseController { #extensionRegistry: UmbBackofficeExtensionRegistry; #apiBaseUrl = OpenAPI.BASE; @@ -13,7 +12,6 @@ export class UmbServerExtensionRegistrator extends UmbBaseController { constructor(host: UmbControllerHost, extensionRegistry: UmbBackofficeExtensionRegistry) { super(host, UmbServerExtensionRegistrator.name); this.#extensionRegistry = extensionRegistry; - // TODO: This was before in hostConnected(), but I don't see the reason to wait. lets just do it right away. this.#loadServerPackages(); } @@ -25,7 +23,7 @@ export class UmbServerExtensionRegistrator extends UmbBaseController { This code is copy pasted from the package repository. We probably don't need this is the package repository anymore. */ - const { data: packages } = await tryExecuteAndNotify(this._host, PackageResource.getPackageManifest()); + const { data: packages } = await tryExecuteAndNotify(this, PackageResource.getPackageManifest()); if (packages) { // Append packages to the store but only if they have a name @@ -36,16 +34,26 @@ export class UmbServerExtensionRegistrator extends UmbBaseController { p.extensions?.forEach((e) => { // Crudely validate that the extension at least follows a basic manifest structure // Idea: Use `Zod` to validate the manifest - if (this.isManifestBase(e)) { + if (isManifestBaseType(e)) { /** * Crude check to see if extension is of type "js" since it is safe to assume we do not * need to load any other types of extensions in the backoffice (we need a js file to load) */ - if (isManifestJSType(e)) { - // Add API base url if the js path is relative - if (!e.js.startsWith('http')) { - e.js = `${this.#apiBaseUrl}${e.js}`; - } + + // TODO: add helper to check for relative paths + // Add base url if the js path is relative + if ('js' in e && typeof e.js === 'string' && !e.js.startsWith('http')) { + e.js = `${this.#apiBaseUrl}${e.js}`; + } + + // Add base url if the element path is relative + if ('element' in e && typeof e.element === 'string' && !e.element.startsWith('http')) { + e.element = `${this.#apiBaseUrl}${e.element}`; + } + + // Add base url if the element path api relative + if ('api' in e && typeof e.api === 'string' && !e.api.startsWith('http')) { + e.api = `${this.#apiBaseUrl}${e.api}`; } extensions.push(e); @@ -56,8 +64,4 @@ export class UmbServerExtensionRegistrator extends UmbBaseController { this.#extensionRegistry.registerMany(extensions); } } - - private isManifestBase(x: unknown): x is ManifestBase { - return typeof x === 'object' && x !== null && 'alias' in x; - } } diff --git a/src/Umbraco.Web.UI.Client/src/apps/installer/installer.context.test.ts b/src/Umbraco.Web.UI.Client/src/apps/installer/installer.context.test.ts deleted file mode 100644 index 7624d47097..0000000000 --- a/src/Umbraco.Web.UI.Client/src/apps/installer/installer.context.test.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { expect, fixture, html } from '@open-wc/testing'; - -describe('UmbInstallerContext', () => { - // TODO: Write tests -}); diff --git a/src/Umbraco.Web.UI.Client/src/external/monaco-editor/index.ts b/src/Umbraco.Web.UI.Client/src/external/monaco-editor/index.ts index b3fa3e2f46..190e558dea 100644 --- a/src/Umbraco.Web.UI.Client/src/external/monaco-editor/index.ts +++ b/src/Umbraco.Web.UI.Client/src/external/monaco-editor/index.ts @@ -1,15 +1,18 @@ -/* eslint local-rules/enforce-umbraco-external-imports: 0 */ +/* eslint-disable */ +// @ts-ignore import styles from 'monaco-editor/min/vs/editor/editor.main.css'; -//eslint-disable-next-line +// @ts-ignore import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker.js?worker'; -//eslint-disable-next-line +// @ts-ignore import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker.js?worker'; -//eslint-disable-next-line +// @ts-ignore import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker.js?worker'; -//eslint-disable-next-line +// @ts-ignore import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker.js?worker'; -//eslint-disable-next-line +// @ts-ignore import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker.js?worker'; +/* eslint-enable */ + import { css, unsafeCSS } from '@umbraco-cms/backoffice/external/lit'; export const monacoEditorStyles = css` diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.controller.ts index 4a37a0a406..ff81bb7be2 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.controller.ts @@ -14,7 +14,7 @@ export class UmbContextConsumerController< public get controllerAlias() { return this.#controllerAlias; } - + constructor(host: UmbControllerHost, contextAlias: string | UmbContextToken, callback: UmbContextCallback) { super(host.getHostElement(), contextAlias, callback); this.#host = host; diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.ts index 31f75bea1f..406c481cfe 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.ts @@ -41,27 +41,24 @@ ResultType extends BaseType = BaseType> { ) { this.#contextAlias = contextAlias.toString(); this.#callback = callback; - this.#discriminator = (contextAlias as any).getDiscriminator?.(); + this.#discriminator = (contextAlias as UmbContextToken).getDiscriminator?.(); } - - - /* Idea: Niels: If we need to filter for specific contexts, we could make the response method return true/false. If false, the event should then then not be stopped. Alternatively parse the event it self on to the response-callback. - This will enable the event to continue to bubble up finding a context that matches. - The reason for such would be to have some who are more specific than others. For example, some might just need the current workspace-context, others might need the closest handling a certain entityType. - As I'm writing this is not relevant, but I wanted to keep the idea as we have had some circumstance that might be solved with this approach. - */ - protected _onResponse = (instance: BaseType) => { + + protected _onResponse = (instance: BaseType): boolean => { if (this.#instance === instance) { - return; + return false; } if(this.#discriminator) { // Notice if discriminator returns false, we do not want to setInstance. if(this.#discriminator(instance)) { this.setInstance(instance as unknown as ResultType); + return true; } } else { this.setInstance(instance as ResultType); + return true; } + return false; }; protected setInstance(instance: ResultType) { diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-request.event.test.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-request.event.test.ts index 19f17f929b..f6557a2a75 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-request.event.test.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-request.event.test.ts @@ -4,6 +4,7 @@ import { UmbContextRequestEventImplementation, UmbContextRequestEvent } from './ describe('UmbContextRequestEvent', () => { const contextRequestCallback = () => { console.log('hello from callback'); + return true; }; const event: UmbContextRequestEvent = new UmbContextRequestEventImplementation( diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-request.event.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-request.event.ts index 444a788353..6b2d64bafc 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-request.event.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-request.event.ts @@ -11,7 +11,7 @@ export type UmbContextCallback = (instance: T) => void; */ export interface UmbContextRequestEvent extends Event { readonly contextAlias: string | UmbContextToken; - readonly callback: UmbContextCallback; + readonly callback: (context: ResultType) => boolean; } /** @@ -23,16 +23,12 @@ export interface UmbContextRequestEvent extends Event { export class UmbContextRequestEventImplementation extends Event implements UmbContextRequestEvent { public constructor( public readonly contextAlias: string | UmbContextToken, - public readonly callback: UmbContextCallback + public readonly callback: (context: ResultType) => boolean ) { super(umbContextRequestEventType, { bubbles: true, composed: true, cancelable: true }); } } -export const isUmbContextRequestEvent = (event: Event): event is UmbContextRequestEventImplementation => { - return event.type === umbContextRequestEventType; -}; - export class UmbContextDebugRequest extends Event { public constructor(public readonly callback: any) { super(umbDebugContextEventType, { bubbles: true, composed: true, cancelable: false }); diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.controller.test.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.controller.test.ts index adef350e7b..9d09cc989e 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.controller.test.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.controller.test.ts @@ -26,8 +26,8 @@ describe('UmbContextProviderController', () => { it('has a controllerAlias property', () => { expect(provider).to.have.property('controllerAlias'); }); - it('has a controllerAlias property, is equal to the controllerAlias', () => { - expect(provider.controllerAlias).to.eq('my-test-context'); + it('has a controllerAlias property, is equal to the controllerAlias plus instance name', () => { + expect(provider.controllerAlias).to.eq('my-test-context' + '_' + instance.constructor.name); }); }); diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.controller.ts index 9eb834bd44..4d8a283a0c 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.controller.ts @@ -8,14 +8,18 @@ export class UmbContextProviderController< InstanceType extends ResultType = ResultType > extends UmbContextProvider implements UmbController { #host: UmbControllerHost; + #controllerAlias:string; public get controllerAlias() { - return this._contextAlias.toString(); + return this.#controllerAlias; } constructor(host: UmbControllerHost, contextAlias: string | UmbContextToken, instance: InstanceType) { super(host.getHostElement(), contextAlias, instance); this.#host = host; + // Makes the controllerAlias unique for this instance, this enables multiple Contexts to be provided under the same name. (This only makes sense cause of Context Token Discriminators) + // This does mean that if someone provides a context with the same name, but with a different instance, it will not override the previous instance. But its good since it enables extensions to provide contexts at the same scope of other contexts. + this.#controllerAlias = contextAlias.toString() + '_' + (instance as any).constructor?.name; // If this API is already provided with this alias? Then we do not want to register this controller: const existingControllers = host.getControllers((x) => x.controllerAlias === this.controllerAlias); @@ -23,9 +27,10 @@ export class UmbContextProviderController< existingControllers.length > 0 && (existingControllers[0] as UmbContextProviderController).providerInstance?.() === instance ) { + // This just an additional awareness feature to make devs Aware, the alternative would be adding it anyway, but that would destroy existing controller of this alias. // Back out, this instance is already provided, by another controller. throw new Error( - `Context API: The context of '${this.controllerAlias}' is already provided with the same API by another Context Provider Controller.` + `Context API: The context of '${this.controllerAlias}' and instance '${(instance as any).constructor?.name ?? 'unnamed'}' is already provided by another Context Provider Controller.` ); } else { host.addController(this); diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.element.test.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.element.test.ts index b30909442d..e61536e8f8 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.element.test.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.element.test.ts @@ -10,7 +10,7 @@ export class UmbTestContextElement extends UmbControllerHostElementMixin(HTMLEle constructor() { super(); - new UmbContextConsumerController(this, 'test-context', (value) => { + new UmbContextConsumerController(this, 'test-context', (value) => { this.value = value; }); } diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.test.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.test.ts index 70e77fc03f..e8ff8bb119 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.test.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.test.ts @@ -45,6 +45,7 @@ describe('UmbContextProvider', () => { (_instance: UmbTestContextProviderClass) => { expect(_instance.prop).to.eq('value from provider'); done(); + return true; } ); diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.ts index e506a5e46f..10d43595ec 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.ts @@ -1,6 +1,6 @@ import { + UmbContextRequestEvent, umbContextRequestEventType, - isUmbContextRequestEvent, umbDebugContextEventType, } from '../consume/context-request.event.js'; import { UmbContextToken } from '../token/context-token.js'; @@ -46,13 +46,17 @@ export class UmbContextProvider { - if (!isUmbContextRequestEvent(event)) return; + #handleContextRequest = ((event: UmbContextRequestEvent) => { if (event.contextAlias !== this._contextAlias) return; + // Since the alias matches, we will stop it from bubbling further up. But we still allow it to ask the other Contexts of the element. Hence not calling `event.stopImmediatePropagation();` event.stopPropagation(); - event.callback(this.#instance); - }; + + if(event.callback(this.#instance)) { + // Make sure the event not hits any more Contexts as we have found a match. + event.stopImmediatePropagation(); + } + }) as EventListener; /** * @memberof UmbContextProvider diff --git a/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host-base.mixin.ts b/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host-base.mixin.ts index 2ec3251b38..c156d26b41 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host-base.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host-base.mixin.ts @@ -1,4 +1,4 @@ -import { ClassConstructor } from '../extension-api/types.js'; +import { ClassConstructor } from '../extension-api/types/utils.js'; import { UmbControllerHost } from './controller-host.interface.js'; import type { UmbController } from './controller.interface.js'; @@ -21,7 +21,7 @@ declare class UmbControllerHostBaseDeclaration implements Omit>(superClass: T) => { +export const UmbControllerHostBaseMixin = (superClass: T) => { class UmbControllerHostBaseClass extends superClass { #controllers: UmbController[] = []; @@ -59,8 +59,12 @@ export const UmbControllerHostBaseMixin = >(supe this.#controllers.push(ctrl); if (this.#attached) { // If a controller is created on a already attached element, then it will be added directly. This might not be optimal. As the controller it self has not finished its constructor method jet. therefor i postpone the call: - Promise.resolve().then(() => ctrl.hostConnected()); - //ctrl.hostConnected(); + Promise.resolve().then(() => { + // Extra check to see if we are still attached at this point: + if (this.#attached) { + ctrl.hostConnected(); + } + }); } } @@ -97,16 +101,22 @@ export const UmbControllerHostBaseMixin = >(supe hostConnected() { this.#attached = true; + // Note: this might not be optimal, as if hostDisconnected remove one of the controllers, then the next controller will be skipped. this.#controllers.forEach((ctrl: UmbController) => ctrl.hostConnected()); } hostDisconnected() { this.#attached = false; + // Note: this might not be optimal, as if hostDisconnected remove one of the controllers, then the next controller will be skipped. this.#controllers.forEach((ctrl: UmbController) => ctrl.hostDisconnected()); } destroy() { - this.#controllers.forEach((ctrl: UmbController) => ctrl.destroy()); + let ctrl: UmbController | undefined; + // Note: A very important way of doing this loop, as foreach will skip over the next item if the current item is removed. + while ((ctrl = this.#controllers[0])) { + ctrl.destroy(); + } this.#controllers.length = 0; } } diff --git a/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host-element.mixin.ts b/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host-element.mixin.ts index 68cc3d21bf..727eef7e3f 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host-element.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host-element.mixin.ts @@ -1,8 +1,8 @@ -import type { HTMLElementConstructor } from '../extension-api/types.js'; import type { UmbControllerAlias } from './controller-alias.type.js'; import { UmbControllerHostBaseMixin } from './controller-host-base.mixin.js'; import type { UmbControllerHost } from './controller-host.interface.js'; import type { UmbController } from './controller.interface.js'; +import { HTMLElementConstructor } from '@umbraco-cms/backoffice/extension-api'; export declare class UmbControllerHostElement extends HTMLElement implements UmbControllerHost { hasController(controller: UmbController): boolean; diff --git a/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller.test.ts b/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller.test.ts index 65f4f8e2e1..ff023ef662 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller.test.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller.test.ts @@ -66,12 +66,13 @@ describe('UmbController', () => { describe('Controller Public API', () => { let controller: UmbTestControllerImplementationElement; beforeEach(() => { - controller = new UmbTestControllerImplementationElement(hostElement, 'my-test-context'); + controller = new UmbTestControllerImplementationElement(hostElement, 'my-test-controller-alias'); }); describe('methods', () => { it('has an controllerAlias property', () => { expect(controller).to.have.property('controllerAlias').that.is.a('string'); + expect(controller.controllerAlias).to.be.equal('my-test-controller-alias'); }); it('has an hasController method', () => { expect(controller).to.have.property('hasController').that.is.a('function'); @@ -102,8 +103,8 @@ describe('UmbController', () => { describe('Controllers lifecycle', () => { it('controller is removed from host when destroyed', () => { - const ctrl = new UmbTestControllerImplementationElement(hostElement, 'my-test-context'); - const subCtrl = new UmbTestControllerImplementationElement(ctrl, 'my-test-context'); + const ctrl = new UmbTestControllerImplementationElement(hostElement); + const subCtrl = new UmbTestControllerImplementationElement(ctrl); expect(hostElement.hasController(ctrl)).to.be.true; expect(ctrl.hasController(subCtrl)).to.be.true; @@ -119,8 +120,8 @@ describe('UmbController', () => { }); it('controller is destroyed when removed from host', () => { - const ctrl = new UmbTestControllerImplementationElement(hostElement, 'my-test-context'); - const subCtrl = new UmbTestControllerImplementationElement(ctrl, 'my-test-context'); + const ctrl = new UmbTestControllerImplementationElement(hostElement); + const subCtrl = new UmbTestControllerImplementationElement(ctrl); expect(ctrl.testIsDestroyed).to.be.false; expect(subCtrl.testIsDestroyed).to.be.false; @@ -135,9 +136,45 @@ describe('UmbController', () => { expect(ctrl.hasController(subCtrl)).to.be.false; }); + it('all controllers are destroyed when the hosting controller gets destroyed', () => { + const ctrl = new UmbTestControllerImplementationElement(hostElement); + const subCtrl = new UmbTestControllerImplementationElement(ctrl); + const subCtrl2 = new UmbTestControllerImplementationElement(ctrl); + const subSubCtrl1 = new UmbTestControllerImplementationElement(subCtrl); + const subSubCtrl2 = new UmbTestControllerImplementationElement(subCtrl); + + expect(ctrl.testIsDestroyed).to.be.false; + expect(hostElement.hasController(ctrl)).to.be.true; + // Subs: + expect(subCtrl.testIsDestroyed).to.be.false; + expect(subCtrl2.testIsDestroyed).to.be.false; + expect(ctrl.hasController(subCtrl)).to.be.true; + expect(ctrl.hasController(subCtrl2)).to.be.true; + // Sub subs: + expect(subSubCtrl1.testIsDestroyed).to.be.false; + expect(subSubCtrl2.testIsDestroyed).to.be.false; + expect(subCtrl.hasController(subSubCtrl1)).to.be.true; + expect(subCtrl.hasController(subSubCtrl2)).to.be.true; + + ctrl.destroy(); + + expect(ctrl.testIsDestroyed).to.be.true; + expect(hostElement.hasController(ctrl)).to.be.false; + // Subs: + expect(subCtrl.testIsDestroyed).to.be.true; + expect(subCtrl2.testIsDestroyed).to.be.true; + expect(ctrl.hasController(subCtrl)).to.be.false; + expect(ctrl.hasController(subCtrl2)).to.be.false; + // Sub subs: + expect(subSubCtrl1.testIsDestroyed).to.be.true; + expect(subSubCtrl2.testIsDestroyed).to.be.true; + expect(subCtrl.hasController(subSubCtrl1)).to.be.false; + expect(subCtrl.hasController(subSubCtrl2)).to.be.false; + }); + it('hostConnected & hostDisconnected is triggered accordingly to the state of the controller host.', () => { - const ctrl = new UmbTestControllerImplementationElement(hostElement, 'my-test-context'); - const subCtrl = new UmbTestControllerImplementationElement(ctrl, 'my-test-context'); + const ctrl = new UmbTestControllerImplementationElement(hostElement); + const subCtrl = new UmbTestControllerImplementationElement(ctrl); expect(hostElement.hasController(ctrl)).to.be.true; expect(ctrl.hasController(subCtrl)).to.be.true; @@ -154,12 +191,35 @@ describe('UmbController', () => { expect(ctrl.testIsConnected).to.be.false; expect(subCtrl.testIsConnected).to.be.false; }); + + it('hostConnected is triggered if controller host is already connected at time of adding controller.', async () => { + document.body.appendChild(hostElement); + + const ctrl = new UmbTestControllerImplementationElement(hostElement); + const subCtrl = new UmbTestControllerImplementationElement(ctrl); + + expect(hostElement.hasController(ctrl)).to.be.true; + expect(ctrl.hasController(subCtrl)).to.be.true; + expect(ctrl.testIsConnected).to.be.false; + expect(subCtrl.testIsConnected).to.be.false; + + // Wait one JS cycle, to ensure that the hostConnected is triggered. (Currently its by design that we trigger the hostConnected with one cycle delay) + await Promise.resolve(); + + expect(ctrl.testIsConnected).to.be.true; + expect(subCtrl.testIsConnected).to.be.true; + + document.body.removeChild(hostElement); + + expect(ctrl.testIsConnected).to.be.false; + expect(subCtrl.testIsConnected).to.be.false; + }); }); - describe('Controllers against other Controller', () => { + describe('Controllers against other Controllers', () => { it('controller is replaced by another controller using the same string as controller-alias', () => { - const firstCtrl = new UmbTestControllerImplementationElement(hostElement, 'my-test-context'); - const secondCtrl = new UmbTestControllerImplementationElement(hostElement, 'my-test-context'); + const firstCtrl = new UmbTestControllerImplementationElement(hostElement, 'my-test-alias'); + const secondCtrl = new UmbTestControllerImplementationElement(hostElement, 'my-test-alias'); expect(hostElement.hasController(firstCtrl)).to.be.false; expect(hostElement.hasController(secondCtrl)).to.be.true; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/condition/condition-controller-arguments.type.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/condition/condition-controller-arguments.type.ts index 0dc5c97330..ea0a211160 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/condition/condition-controller-arguments.type.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/condition/condition-controller-arguments.type.ts @@ -1,4 +1,4 @@ -import type { UmbConditionConfigBase } from '../types.js'; +import { UmbConditionConfigBase } from '../types/index.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; export type UmbConditionControllerArguments< diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/condition/extension-condition.interface.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/condition/extension-condition.interface.ts index 9166e0eccb..446d3b2d03 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/condition/extension-condition.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/condition/extension-condition.interface.ts @@ -1,4 +1,4 @@ -import type { UmbConditionConfigBase } from '../types.js'; +import type { UmbConditionConfigBase } from '../types/index.js'; import { UmbController } from '@umbraco-cms/backoffice/controller-api'; export interface UmbExtensionCondition extends UmbController { diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extension-initializer.controller.test.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extension-initializer.controller.test.ts index 0007b26380..5ab3eb2e06 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extension-initializer.controller.test.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extension-initializer.controller.test.ts @@ -4,7 +4,7 @@ import type { ManifestKind, ManifestWithDynamicConditions, UmbConditionConfigBase, -} from '../types.js'; +} from '../types/index.js'; import { UmbExtensionRegistry } from '../registry/extension.registry.js'; import type { UmbExtensionCondition } from '../condition/extension-condition.interface.js'; import { @@ -25,9 +25,9 @@ class UmbTestExtensionController extends UmbBaseExtensionInitializer { host: UmbControllerHostElement, extensionRegistry: UmbExtensionRegistry, alias: string, - onPermissionChanged: (isPermitted: boolean) => void + onPermissionChanged: (isPermitted: boolean) => void, ) { - super(host, extensionRegistry, 'test_', alias, onPermissionChanged); + super(host, extensionRegistry, 'test', alias, onPermissionChanged); this._init(); } @@ -90,7 +90,7 @@ describe('UmbBaseExtensionController', () => { done(); }); } - } + }, ); }); }); @@ -128,7 +128,7 @@ describe('UmbBaseExtensionController', () => { done(); }); } - } + }, ); }); }); @@ -168,7 +168,7 @@ describe('UmbBaseExtensionController', () => { (isPermitted) => { // No relevant for this test. expect(isPermitted).to.be.true; - } + }, ); extensionRegistry.register(manifest); @@ -189,7 +189,7 @@ describe('UmbBaseExtensionController', () => { 'Umb.Test.Section.1', () => { expect.fail('Callback should not be called when never permitted'); - } + }, ); extensionController.asPromise().then(() => { expect.fail('Promise should not resolve'); @@ -216,22 +216,24 @@ describe('UmbBaseExtensionController', () => { hostElement, extensionRegistry, 'Umb.Test.Section.1', - () => { - count++; - if (count === 1) { - // First time render, there is no conditions. - expect(extensionController.manifest?.weight).to.be.equal(2); - expect(extensionController.manifest?.conditions?.length).to.be.equal(1); - } else if (count === 2) { - // Second time render, there is conditions and weight is 22. - expect(extensionController.manifest?.weight).to.be.equal(22); - expect(extensionController.manifest?.conditions?.length).to.be.equal(1); - // Check that the promise has been resolved for the first render to ensure timing is right. - expect(initialPromiseResolved).to.be.true; - done(); - extensionController.destroy(); + (isPermitted) => { + if (isPermitted) { + count++; + if (count === 1) { + // First time render, there is no conditions. + expect(extensionController.manifest?.weight).to.be.equal(2); + expect(extensionController.manifest?.conditions?.length).to.be.equal(1); + } else if (count === 2) { + // Second time render, there is conditions and weight is 22. + expect(extensionController.manifest?.weight).to.be.equal(22); + expect(extensionController.manifest?.conditions?.length).to.be.equal(1); + // Check that the promise has been resolved for the first render to ensure timing is right. + expect(initialPromiseResolved).to.be.true; + done(); + extensionController.destroy(); + } } - } + }, ); extensionController.asPromise().then(() => { initialPromiseResolved = true; @@ -259,22 +261,24 @@ describe('UmbBaseExtensionController', () => { hostElement, extensionRegistry, 'Umb.Test.Section.1', - () => { - count++; - if (count === 1) { - // First time render, there is no conditions. - expect(extensionController.manifest?.weight).to.be.equal(3); - expect(extensionController.manifest?.conditions?.length).to.be.equal(0); - } else if (count === 2) { - // Second time render, there is conditions and weight is 33. - expect(extensionController.manifest?.weight).to.be.equal(33); - expect(extensionController.manifest?.conditions?.length).to.be.equal(0); - // Check that the promise has been resolved for the first render to ensure timing is right. - expect(initialPromiseResolved).to.be.true; - done(); - extensionController.destroy(); + (isPermitted) => { + if (isPermitted) { + count++; + if (count === 1) { + // First time render, there is no conditions. + expect(extensionController.manifest?.weight).to.be.equal(3); + expect(extensionController.manifest?.conditions?.length).to.be.equal(0); + } else if (count === 2) { + // Second time render, there is conditions and weight is 33. + expect(extensionController.manifest?.weight).to.be.equal(33); + expect(extensionController.manifest?.conditions?.length).to.be.equal(0); + // Check that the promise has been resolved for the first render to ensure timing is right. + expect(initialPromiseResolved).to.be.true; + done(); + extensionController.destroy(); + } } - } + }, ); extensionController.asPromise().then(() => { initialPromiseResolved = true; @@ -302,22 +306,24 @@ describe('UmbBaseExtensionController', () => { hostElement, extensionRegistry, 'Umb.Test.Section.1', - () => { - count++; - if (count === 1) { - // First time render, there is no conditions. - expect(extensionController.manifest?.weight).to.be.equal(4); - expect(extensionController.manifest?.conditions?.length).to.be.equal(0); - } else if (count === 2) { - // Second time render, there is conditions and weight is 33. - expect(extensionController.manifest?.weight).to.be.equal(44); - expect(extensionController.manifest?.conditions?.length).to.be.equal(1); - // Check that the promise has been resolved for the first render to ensure timing is right. - expect(initialPromiseResolved).to.be.true; - done(); - extensionController.destroy(); + (isPermitted) => { + if (isPermitted) { + count++; + if (count === 1) { + // First time render, there is no conditions. + expect(extensionController.manifest?.weight).to.be.equal(4); + expect(extensionController.manifest?.conditions?.length).to.be.equal(0); + } else if (count === 2) { + // Second time render, there is conditions and weight is 33. + expect(extensionController.manifest?.weight).to.be.equal(44); + expect(extensionController.manifest?.conditions?.length).to.be.equal(1); + // Check that the promise has been resolved for the first render to ensure timing is right. + expect(initialPromiseResolved).to.be.true; + done(); + extensionController.destroy(); + } } - } + }, ); extensionController.asPromise().then(() => { initialPromiseResolved = true; @@ -355,22 +361,24 @@ describe('UmbBaseExtensionController', () => { hostElement, extensionRegistry, 'Umb.Test.Section.1', - () => { - count++; - if (count === 1) { - // First time render, there is no conditions. - expect(extensionController.manifest?.weight).to.be.undefined; - expect(extensionController.manifest?.conditions?.length).to.be.equal(0); - } else if (count === 2) { - // Second time render, there is a matching kind and then weight is 123. - expect(extensionController.manifest?.weight).to.be.equal(123); - expect(extensionController.manifest?.conditions?.length).to.be.equal(0); - // Check that the promise has been resolved for the first render to ensure timing is right. - expect(initialPromiseResolved).to.be.true; - done(); - extensionController.destroy(); + (isPermitted) => { + if (isPermitted) { + count++; + if (count === 1) { + // First time render, there is no conditions. + expect(extensionController.manifest?.weight).to.be.undefined; + expect(extensionController.manifest?.conditions?.length).to.be.equal(0); + } else if (count === 2) { + // Second time render, there is a matching kind and then weight is 123. + expect(extensionController.manifest?.weight).to.be.equal(123); + expect(extensionController.manifest?.conditions?.length).to.be.equal(0); + // Check that the promise has been resolved for the first render to ensure timing is right. + expect(initialPromiseResolved).to.be.true; + done(); + extensionController.destroy(); + } } - } + }, ); extensionController.asPromise().then(() => { initialPromiseResolved = true; @@ -421,7 +429,7 @@ describe('UmbBaseExtensionController', () => { () => { // This should not be called. expect(true).to.be.false; - } + }, ); Promise.resolve().then(() => { expect(extensionController?.manifest?.alias).to.eq('Umb.Test.Section.1'); @@ -439,7 +447,7 @@ describe('UmbBaseExtensionController', () => { () => { // This should not be called. expect(true).to.be.false; - } + }, ); extensionRegistry.register(manifest); @@ -460,7 +468,7 @@ describe('UmbBaseExtensionController', () => { 'Umb.Test.Section.1', () => { // Empty callback. - } + }, ); extensionController.hasConditions().then((hasConditions) => { expect(hasConditions).to.be.true; @@ -528,7 +536,7 @@ describe('UmbBaseExtensionController', () => { extensionController.destroy(); // need to destroy the conditions. done(); } - } + }, ); }); }); @@ -549,20 +557,20 @@ describe('UmbBaseExtensionController', () => { conditions: [ { alias: 'Umb.Test.Condition.Delay', - value: '100', + value: '10', }, { alias: 'Umb.Test.Condition.Delay', - value: '200', + value: '20', }, ], }; // A ASCII timeline for the conditions, when allowed and then not allowed: - // Condition 0ms 100ms 200ms 300ms 400ms 500ms - // First condition: - + - + - + - // Second condition: - - + + - - - // Sum: - - - + - - + // Condition 0ms 10ms 20ms 30ms 40ms 50ms 60ms + // First condition: - + - + - + - + // Second condition: - - + + - - + + // Sum: - - - + - - - const conditionManifest = { type: 'condition', @@ -581,27 +589,33 @@ describe('UmbBaseExtensionController', () => { hostElement, extensionRegistry, 'Umb.Test.Section.1', - async () => { + async (isPermitted) => { count++; // We want the controller callback to first fire when conditions are initialized. expect(extensionController.manifest?.conditions?.length).to.be.equal(2); expect(extensionController?.manifest?.alias).to.eq('Umb.Test.Section.1'); if (count === 1) { + expect(isPermitted).to.be.true; expect(extensionController?.permitted).to.be.true; // Hack to double check that its two conditions that make up the state: expect(extensionController.getControllers((controller) => (controller as any).permitted).length).to.equal( - 2 + 2, ); } else if (count === 2) { + expect(isPermitted).to.be.false; expect(extensionController?.permitted).to.be.false; // Hack to double check that its two conditions that make up the state, in this case its one, cause we already got the callback when one of the conditions changed. meaning in this split second one is still good: expect(extensionController.getControllers((controller) => (controller as any).permitted).length).to.equal( - 1 + 1, ); - extensionController.destroy(); // need to destroy the conditions. - done(); + + // Then we are done: + extensionController.destroy(); // End this test. + setTimeout(() => done(), 60); // Lets wait another round of the conditions approve/disapprove, just to see if the destroy stopped the conditions. (60ms, as that should be enough to test that another round does not happen.) + } else if (count === 5) { + expect(false).to.be.true; // This should not be called. } - } + }, ); }); }); diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extension-initializer.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extension-initializer.controller.ts index 79b67c8df5..d64037be56 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extension-initializer.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extension-initializer.controller.ts @@ -5,13 +5,23 @@ import { type ManifestWithDynamicConditions, type UmbExtensionRegistry, createExtensionApi, + UmbConditionConfigBase, } from '@umbraco-cms/backoffice/extension-api'; import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; +/** + * This abstract Controller holds the core to manage a single Extension. + * When the extension is permitted to be used, then the extender of this class can instantiate what is relevant for this type and thereby make it available for the consumer. + * + * @export + * @abstract + * @class UmbBaseExtensionInitializer + */ export abstract class UmbBaseExtensionInitializer< ManifestType extends ManifestWithDynamicConditions = ManifestWithDynamicConditions, - SubClassType = never + SubClassType = never, > extends UmbBaseController { + // #promiseResolvers: Array<() => void> = []; #manifestObserver!: UmbObserverController; #extensionRegistry: UmbExtensionRegistry; @@ -20,7 +30,7 @@ export abstract class UmbBaseExtensionInitializer< #manifest?: ManifestType; #conditionControllers: Array = []; #onPermissionChanged?: (isPermitted: boolean, controller: SubClassType) => void; - protected _positive?: boolean; + protected _isConditionsPositive?: boolean; #isPermitted?: boolean; get weight() { @@ -53,9 +63,9 @@ export abstract class UmbBaseExtensionInitializer< extensionRegistry: UmbExtensionRegistry, extensionTypeName: string, alias: string, - onPermissionChanged?: (isPermitted: boolean, controller: SubClassType) => void + onPermissionChanged?: (isPermitted: boolean, controller: SubClassType) => void, ) { - super(host, extensionTypeName+alias); + super(host, extensionTypeName + '_' + alias); this.#extensionRegistry = extensionRegistry; this.#alias = alias; this.#onPermissionChanged = onPermissionChanged; @@ -64,7 +74,7 @@ export abstract class UmbBaseExtensionInitializer< this.#manifestObserver = this.observe( this.#extensionRegistry.getByAlias(this.#alias), async (extensionManifest) => { - this.#isPermitted = undefined; + this.#clearPermittedState(); this.#manifest = extensionManifest; if (extensionManifest) { if (extensionManifest.overwrites) { @@ -74,12 +84,13 @@ export abstract class UmbBaseExtensionInitializer< this.#overwrites = extensionManifest.overwrites; } } - this.#gotConditions(); + this.#gotManifest(); } else { this.#overwrites = []; this.#cleanConditions(); } - } + }, + '_observeExtensionManifest', ); } @@ -90,16 +101,17 @@ export abstract class UmbBaseExtensionInitializer< } #cleanConditions() { + if (this.#conditionControllers.length === 0) return; this.#conditionControllers.forEach((controller) => controller.destroy()); this.#conditionControllers = []; this.removeControllerByAlias('_observeConditions'); } - #gotConditions() { + #gotManifest() { const conditionConfigs = this.#manifest?.conditions ?? []; // As conditionConfigs might have been configured as something else than an array, then we ignorer them. - if (conditionConfigs.length === undefined || conditionConfigs.length === 0) { + if (conditionConfigs.length === 0) { this.#cleanConditions(); this.#onConditionsChangedCallback(); return; @@ -131,37 +143,8 @@ export abstract class UmbBaseExtensionInitializer< // Observes the conditions and initialize as they come in. this.observe( this.#extensionRegistry.getByTypeAndAliases('condition', conditionAliases), - async (manifests) => { - // New comers: - manifests.forEach((conditionManifest) => { - const configsOfThisType = conditionConfigs.filter( - (conditionConfig) => conditionConfig.alias === conditionManifest.alias - ); - - // Spin up conditions, based of condition configs: - configsOfThisType.forEach(async (conditionConfig) => { - // Check if we already have a controller for this config: - const existing = this.#conditionControllers.find((controller) => controller.config === conditionConfig); - if (!existing) { - const conditionController = await createExtensionApi(conditionManifest, [ - { - host: this, - manifest: conditionManifest, - config: conditionConfig, - onChange: this.#onConditionsChangedCallback, - }, - ]); - if (conditionController) { - // Some how listen to it? callback/event/onChange something. - // then call this one: this.#onConditionsChanged(); - this.#conditionControllers.push(conditionController); - this.#onConditionsChangedCallback(); - } - } - }); - }); - }, - '_observeConditions' + this.#gotConditions, + '_observeConditions', ); } else { this.removeControllerByAlias('_observeConditions'); @@ -173,6 +156,67 @@ export abstract class UmbBaseExtensionInitializer< } } + #gotConditions = (manifests: ManifestCondition[]) => { + manifests.forEach(this.#gotCondition); + }; + + #gotCondition = async (conditionManifest: ManifestCondition) => { + const conditionConfigs = this.#manifest?.conditions ?? []; + // + // Get just the conditions that uses this condition alias: + const configsOfThisType = conditionConfigs.filter( + (conditionConfig) => conditionConfig.alias === conditionManifest.alias, + ); + + // Create conditions, based of condition configs: + const newConditionControllers = await Promise.all( + configsOfThisType.map((conditionConfig) => this.#createConditionController(conditionManifest, conditionConfig)), + ); + + const oldLength = this.#conditionControllers.length; + + newConditionControllers + .filter((x) => x !== undefined) + .forEach((emerging) => { + // TODO: All of this could use a refactor at one point, when someone is fresh in their mind. + // Niels Notes: Current problem being that we are not aware about what is in the making, so we don't know if we end up creating the same condition multiple times. + // Because it took some time to create the conditions, it maybe have already gotten created by another cycle, so lets test again. + const existing = this.#conditionControllers.find((existing) => existing.config === emerging?.config); + if (!existing) { + this.#conditionControllers.push(emerging!); + } else { + emerging?.destroy(); + } + }); + + // If a change to amount of condition controllers, this will make sure that when new conditions are added, the callback is fired, so the extensions can be re-evaluated, starting out as bad. + if (oldLength !== this.#conditionControllers.length) { + this.#onConditionsChangedCallback(); + } + }; + + async #createConditionController( + conditionManifest: ManifestCondition, + conditionConfig: UmbConditionConfigBase, + ): Promise { + // Check if we already have a controller for this config: + const existing = this.#conditionControllers.find((controller) => controller.config === conditionConfig); + if (!existing) { + const conditionController = await createExtensionApi(conditionManifest, [ + { + host: this, + manifest: conditionManifest, + config: conditionConfig, + onChange: this.#onConditionsChangedCallback, + }, + ]); + if (conditionController) { + return conditionController; + } + } + return undefined; + } + #conditionsAreInitialized() { // Not good if we don't have a manifest. // Only good if conditions of manifest is equal to the amount of condition controllers (one for each condition). @@ -182,23 +226,33 @@ export abstract class UmbBaseExtensionInitializer< } #onConditionsChangedCallback = async () => { - const oldValue = this.#isPermitted ?? false; + // We will collect old value here, but we need to re-collect it after a async method have been called, as it could have changed in the mean time. + let oldValue = this.#isPermitted ?? false; + // Find a condition that is not permitted (Notice how no conditions, means that this extension is permitted) const isPositive = this.#conditionsAreInitialized() && this.#conditionControllers.find((condition) => condition.permitted === false) === undefined; - this._positive = isPositive; + this._isConditionsPositive = isPositive; if (isPositive) { if (this.#isPermitted !== true) { - this.#isPermitted = await this._conditionsAreGood(); + const newPermission = await this._conditionsAreGood(); + // Only set new permission if we are still positive, otherwise it means that we have been destroyed in the mean time. + if (newPermission === false) { + return; + } + // We update the oldValue as this point, cause in this way we are sure its the value at this point, when doing async code someone else might have changed the state in the mean time. + oldValue = this.#isPermitted ?? false; + this.#isPermitted = newPermission; } } else if (this.#isPermitted !== false) { - await this._conditionsAreBad(); + // Clean up: this.#isPermitted = false; + await this._conditionsAreBad(); } - if (oldValue !== this.#isPermitted) { + if (oldValue !== this.#isPermitted && this.#isPermitted !== undefined) { if (this.#isPermitted) { this.#promiseResolvers.forEach((x) => x()); this.#promiseResolvers = []; @@ -215,13 +269,41 @@ export abstract class UmbBaseExtensionInitializer< return otherClass?.manifest === this.manifest; } - public destroy(): void { - this.#promiseResolvers = []; + /* + public hostConnected(): void { + super.hostConnected(); + //this.#onConditionsChangedCallback(); + } + + public hostDisconnected(): void { + super.hostDisconnected(); + this._runtimePositive = false; + if (this.#isPermitted === true) { + this.#isPermitted = false; + this._conditionsAreBad(); + this.#onPermissionChanged?.(false, this as any); + } + } + */ + + #clearPermittedState() { if (this.#isPermitted === true) { this.#isPermitted = undefined; this._conditionsAreBad(); this.#onPermissionChanged?.(false, this as any); } + } + + public destroy(): void { + if (!this.#extensionRegistry) return; + this.#promiseResolvers = []; + this.#clearPermittedState(); // This fires the callback as not permitted, if it was permitted before. + this.#isPermitted = undefined; + this._isConditionsPositive = false; + this.#overwrites = []; + this.#cleanConditions(); + this.#onPermissionChanged = undefined; + (this.#extensionRegistry as any) = undefined; super.destroy(); // Destroy the conditions controllers, are begin destroyed cause they are controllers. } diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.test.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.controller.test.ts similarity index 90% rename from src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.test.ts rename to src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.controller.test.ts index e883077d6c..232899c638 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.test.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.controller.test.ts @@ -1,6 +1,6 @@ import { expect, fixture } from '@open-wc/testing'; import { UmbExtensionRegistry } from '../registry/extension.registry.js'; -import { ManifestCondition, ManifestWithDynamicConditions, UmbConditionConfigBase } from '../types.js'; +import { ManifestCondition, ManifestWithDynamicConditions, UmbConditionConfigBase } from '../types/index.js'; import { UmbExtensionCondition } from '../condition/extension-condition.interface.js'; import { PermittedControllerType, UmbBaseExtensionInitializer, UmbBaseExtensionsInitializer } from './index.js'; import { @@ -18,7 +18,7 @@ class UmbTestExtensionController extends UmbBaseExtensionInitializer { host: UmbControllerHost, extensionRegistry: UmbExtensionRegistry, alias: string, - onPermissionChanged: (isPermitted: boolean, controller: UmbTestExtensionController) => void + onPermissionChanged: (isPermitted: boolean, controller: UmbTestExtensionController) => void, ) { super(host, extensionRegistry, 'test_', alias, onPermissionChanged); this._init(); @@ -40,7 +40,7 @@ type myTestManifestTypesUnion = 'extension-type-extra' | 'extension-type'; type myTestManifestTypes = myTestManifestTypesUnion | myTestManifestTypesUnion[]; class UmbTestExtensionsController< - MyPermittedControllerType extends UmbTestExtensionController = PermittedControllerType + MyPermittedControllerType extends UmbTestExtensionController = PermittedControllerType, > extends UmbBaseExtensionsInitializer< myTestManifests, myTestManifestTypesUnion, @@ -48,21 +48,21 @@ class UmbTestExtensionsController< UmbTestExtensionController, MyPermittedControllerType > { - #host: UmbControllerHost; + #extensionRegistry: UmbExtensionRegistry; constructor( host: UmbControllerHost, extensionRegistry: UmbExtensionRegistry, type: myTestManifestTypes, filter: null | ((manifest: ManifestWithDynamicConditions) => boolean), - onChange: (permittedManifests: Array) => void + onChange: (permittedManifests: Array) => void, ) { super(host, extensionRegistry, type, filter, onChange); - this.#host = host; + this.#extensionRegistry = extensionRegistry; this._init(); } protected _createController(manifest: ManifestWithDynamicConditions) { - return new UmbTestExtensionController(this.#host, testExtensionRegistry, manifest.alias, this._extensionChanged); + return new UmbTestExtensionController(this, this.#extensionRegistry, manifest.alias, this._extensionChanged); } } @@ -109,6 +109,7 @@ describe('UmbBaseExtensionsController', () => { it('exposes both manifests', (done) => { let count = 0; + const extensionController = new UmbTestExtensionsController( hostElement, testExtensionRegistry, @@ -119,13 +120,13 @@ describe('UmbBaseExtensionsController', () => { if (count === 1) { // First callback gives just one. We need to make a feature to gather changes to only reply after a computation cycle if we like to avoid this. expect(permitted.length).to.eq(1); - } - if (count === 2) { + } else if (count === 2) { expect(permitted.length).to.eq(2); - done(); extensionController.destroy(); + } else if (count === 3) { + done(); } - } + }, ); }); @@ -136,6 +137,7 @@ describe('UmbBaseExtensionsController', () => { alias: 'Umb.Test.Extension.Extra', }; testExtensionRegistry.register(manifestExtra); + let count = 0; const extensionController = new UmbTestExtensionsController( hostElement, @@ -147,20 +149,21 @@ describe('UmbBaseExtensionsController', () => { if (count === 1) { // First callback gives just one. We need to make a feature to gather changes to only reply after a computation cycle if we like to avoid this. expect(permitted.length).to.eq(1); - } - if (count === 2) { + } else if (count === 2) { expect(permitted.length).to.eq(2); - } - if (count === 3) { + } else if (count === 3) { expect(permitted.length).to.eq(3); expect(permitted[0].alias).to.eq('Umb.Test.Extension.A'); expect(permitted[1].alias).to.eq('Umb.Test.Extension.B'); expect(permitted[2].alias).to.eq('Umb.Test.Extension.Extra'); - done(); extensionController.destroy(); + } else if (count === 4) { + // Cause we destroyed there will be a last call to reset permitted controllers: + expect(permitted.length).to.eq(0); + done(); } - } + }, ); }); }); @@ -181,6 +184,7 @@ describe('UmbBaseExtensionsController', () => { alias: 'Umb.Test.Extension.B', overwrites: ['Umb.Test.Extension.A'], }; + testExtensionRegistry.register(manifestA); testExtensionRegistry.register(manifestB); }); @@ -202,8 +206,7 @@ describe('UmbBaseExtensionsController', () => { // First callback gives just one. We need to make a feature to gather changes to only reply after a computation cycle if we like to avoid this. expect(permitted.length).to.eq(1); expect(permitted[0].alias).to.eq('Umb.Test.Extension.A'); - } - if (count === 2) { + } else if (count === 2) { // Still just equal one, as the second one overwrites the first one. expect(permitted.length).to.eq(1); expect(permitted[0].alias).to.eq('Umb.Test.Extension.B'); @@ -216,7 +219,7 @@ describe('UmbBaseExtensionsController', () => { done(); extensionController.destroy(); } - } + }, ); }); }); @@ -273,8 +276,7 @@ describe('UmbBaseExtensionsController', () => { // First callback gives just one. We need to make a feature to gather changes to only reply after a computation cycle if we like to avoid this. expect(permitted.length).to.eq(1); expect(permitted[0].alias).to.eq('Umb.Test.Extension.A'); - } - if (count === 2) { + } else if (count === 2) { // Still just equal one, as the second one overwrites the first one. expect(permitted.length).to.eq(1); expect(permitted[0].alias).to.eq('Umb.Test.Extension.B'); @@ -284,10 +286,16 @@ describe('UmbBaseExtensionsController', () => { } else if (count === 3) { expect(permitted.length).to.eq(1); expect(permitted[0].alias).to.eq('Umb.Test.Extension.A'); - done(); extensionController.destroy(); + } else if (count === 4) { + // Expect that destroy will only result in one last callback with no permitted controllers. + expect(permitted.length).to.eq(0); + Promise.resolve().then(() => done()); // This wrap is to enable the test to detect if more callbacks are fired. + } else if (count === 5) { + // This should not happen, we do only want one last callback when destroyed. + expect(false).to.eq(true); } - } + }, ); }); }); @@ -313,6 +321,7 @@ describe('UmbBaseExtensionsController', () => { }, ], }; + // Register opposite order, to ensure B is there when A comes around. A fix to be able to test this. Cause a late registration of B would not cause a change that is test able. testExtensionRegistry.register(manifestB); testExtensionRegistry.register(manifestA); @@ -349,7 +358,7 @@ describe('UmbBaseExtensionsController', () => { done(); extensionController.destroy(); } - } + }, ); }); }); diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.controller.ts index 61fa373aed..301069cca4 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.controller.ts @@ -1,26 +1,32 @@ +import { ManifestTypeMap, SpecificManifestTypeOrManifestBase } from '../types/map.types.js'; import { map } from '@umbraco-cms/backoffice/external/rxjs'; import type { ManifestBase, - ManifestTypeMap, - SpecificManifestTypeOrManifestBase, UmbBaseExtensionInitializer, UmbExtensionRegistry, } from '@umbraco-cms/backoffice/extension-api'; -import { UmbBaseController, UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbBaseController, type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; export type PermittedControllerType = ControllerType & { manifest: Required>; }; /** + * This abstract Controller holds the core to manage a multiple Extensions. + * When one or more extensions are permitted to be used, then the extender of this class can instantiate the relevant single extension initiator relevant for this type. + * + * @export + * @abstract + * @class UmbBaseExtensionsInitializer */ export abstract class UmbBaseExtensionsInitializer< ManifestTypes extends ManifestBase, ManifestTypeName extends keyof ManifestTypeMap | string, ManifestType extends ManifestBase = SpecificManifestTypeOrManifestBase, ControllerType extends UmbBaseExtensionInitializer = UmbBaseExtensionInitializer, - MyPermittedControllerType extends ControllerType = PermittedControllerType + MyPermittedControllerType extends ControllerType = PermittedControllerType, > extends UmbBaseController { + #promiseResolvers: Array<() => void> = []; #extensionRegistry: UmbExtensionRegistry; #type: ManifestTypeName | Array; #filter: undefined | null | ((manifest: ManifestType) => boolean); @@ -28,14 +34,20 @@ export abstract class UmbBaseExtensionsInitializer< protected _extensions: Array = []; private _permittedExts: Array = []; + asPromise(): Promise { + return new Promise((resolve) => { + this._permittedExts.length > 0 ? resolve() : this.#promiseResolvers.push(resolve); + }); + } + constructor( host: UmbControllerHost, extensionRegistry: UmbExtensionRegistry, type: ManifestTypeName | Array, filter: undefined | null | ((manifest: ManifestType) => boolean), - onChange?: (permittedManifests: Array) => void + onChange?: (permittedManifests: Array) => void, ) { - super(host); + super(host, 'extensionsInitializer_' + (Array.isArray(type) ? type.join('_') : type)); this.#extensionRegistry = extensionRegistry; this.#type = type; this.#filter = filter; @@ -48,7 +60,7 @@ export abstract class UmbBaseExtensionsInitializer< if (this.#filter) { source = source.pipe(map((extensions: Array) => extensions.filter(this.#filter!))); } - this.observe(source, this.#gotManifests); + this.observe(source, this.#gotManifests, '_observeManifests') as any; } #gotManifests = (manifests: Array) => { @@ -63,9 +75,9 @@ export abstract class UmbBaseExtensionsInitializer< } // Clean up extensions that are no longer. - this._extensions = this._extensions.filter((controller) => { - if (!manifests.find((manifest) => manifest.alias === controller.alias)) { - controller.destroy(); + this._extensions = this._extensions.filter((extension) => { + if (!manifests.find((manifest) => manifest.alias === extension.alias)) { + extension.destroy(); // destroying the controller will, if permitted, make a last callback with isPermitted = false. This will also remove it from the _permittedExts array. return false; } @@ -81,7 +93,6 @@ export abstract class UmbBaseExtensionsInitializer< if (!existing) { // Idea: could be abstracted into a createController method, so we can override it in a subclass. // (This should be enough to be able to create a element extension controller instead.) - this._extensions.push(this._createController(manifest)); } }); @@ -103,6 +114,7 @@ export abstract class UmbBaseExtensionsInitializer< hasChanged = true; } } + if (hasChanged) { // The final list of permitted extensions to be displayed, this will be stripped from extensions that are overwritten by another extension and sorted accordingly. const exposedPermittedExts = [...this._permittedExts]; @@ -121,6 +133,10 @@ export abstract class UmbBaseExtensionsInitializer< // Sorting: exposedPermittedExts.sort((a, b) => b.weight - a.weight); + if (exposedPermittedExts.length > 0) { + this.#promiseResolvers.forEach((x) => x()); + this.#promiseResolvers = []; + } this.#onChange?.(exposedPermittedExts); } }; @@ -141,9 +157,17 @@ export abstract class UmbBaseExtensionsInitializer< } public destroy() { - super.destroy(); + // The this.#extensionRegistry is an indication of wether this is already destroyed. + if (!this.#extensionRegistry) return; + + const oldPermittedExtsLength = this._permittedExts.length; this._extensions.length = 0; this._permittedExts.length = 0; - this.#onChange?.(this._permittedExts); + if (oldPermittedExtsLength > 0) { + this.#onChange?.(this._permittedExts); + } + this.#onChange = undefined; + (this.#extensionRegistry as any) = undefined; + super.destroy(); } } diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-api-initializer.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-api-initializer.controller.ts index 6c76f10906..dddca2a2ba 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-api-initializer.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-api-initializer.controller.ts @@ -1,28 +1,28 @@ import { createExtensionApi } from '../functions/create-extension-api.function.js'; -import { UmbExtensionRegistry } from '../registry/extension.registry.js'; -import { isManifestApiType } from '../type-guards/is-manifest-apiable-type.function.js'; -import { UmbApi, ManifestApi, ManifestCondition } from '../types.js'; +import type { UmbApi } from '../models/api.interface.js'; +import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; +import type { ManifestApi, ManifestCondition } from '../types/index.js'; import { UmbBaseExtensionInitializer } from './base-extension-initializer.controller.js'; -import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; - +import { type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; /** * This Controller manages a single Extension and its API instance. * When the extension is permitted to be used, its API will be instantiated and available for the consumer. * * @example -* ```ts -* const controller = new UmbExtensionApiController(host, extensionRegistry, alias, [], (permitted, ctrl) => { ctrl.api.helloWorld() })); -* ``` + * ```ts + * const controller = new UmbExtensionApiController(host, extensionRegistry, alias, [], (permitted, ctrl) => { ctrl.api.helloWorld() })); + * ``` * @export * @class UmbExtensionApiController */ export class UmbExtensionApiInitializer< - ManifestType extends ManifestApi = ManifestApi, + ManifestType extends ManifestApi = ManifestApi, ControllerType extends UmbExtensionApiInitializer = any, - ExtensionApiInterface extends UmbApi = ManifestType extends ManifestApi ? NonNullable : UmbApi + ExtensionApiInterface extends UmbApi = ManifestType extends ManifestApi + ? NonNullable + : UmbApi, > extends UmbBaseExtensionInitializer { - #api?: ExtensionApiInterface; #constructorArguments?: Array; @@ -35,7 +35,6 @@ export class UmbExtensionApiInitializer< return this.#api; } - /** * The props that are passed to the class. * @type {Record} @@ -67,7 +66,7 @@ export class UmbExtensionApiInitializer< extensionRegistry: UmbExtensionRegistry, alias: string, constructorArguments: Array | undefined, - onPermissionChanged?: (isPermitted: boolean, controller: ControllerType) => void + onPermissionChanged?: (isPermitted: boolean, controller: ControllerType) => void, ) { super(host, extensionRegistry, 'extApi_', alias, onPermissionChanged); this.#constructorArguments = constructorArguments; @@ -88,23 +87,22 @@ export class UmbExtensionApiInitializer< protected async _conditionsAreGood() { const manifest = this.manifest!; // In this case we are sure its not undefined. - if (isManifestApiType(manifest)) { - const newApi = await createExtensionApi(manifest as unknown as ManifestApi, this.#constructorArguments); - if (!this._positive) { - // We are not positive anymore, so we will back out of this creation. - return false; - } - this.#api = newApi; - - } else { - this.#api = undefined; - console.warn('Manifest did not provide any useful data for a api class to construct.') + const newApi = await createExtensionApi( + manifest as unknown as ManifestApi, + this.#constructorArguments, + ); + if (!this._isConditionsPositive) { + // We are not positive anymore, so we will back out of this creation. + return false; } + this.#api = newApi; + if (this.#api) { //this.#assignProperties(); return true; // we will confirm we have a component and are still good to go. } + console.warn('Manifest did not provide any useful data for a api class to construct.'); return false; // we will reject the state, we have no component, we are not good to be shown. } diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-api-initializer.test.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-api-initializer.test.ts index 7a83f1f02a..e31650bcb3 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-api-initializer.test.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-api-initializer.test.ts @@ -1,6 +1,6 @@ import { expect, fixture } from '@open-wc/testing'; import { UmbExtensionRegistry } from '../registry/extension.registry.js'; -import { ManifestApi, ManifestWithDynamicConditions } from '../types.js'; +import { ManifestApi, ManifestWithDynamicConditions } from '../types/index.js'; import { UmbExtensionApiInitializer } from './index.js'; import { UmbBaseController, UmbControllerHost, UmbControllerHostElement, UmbControllerHostElementMixin } from '@umbraco-cms/backoffice/controller-api'; import { customElement, html } from '@umbraco-cms/backoffice/external/lit'; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-element-initializer.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-element-initializer.controller.ts index bb33c51127..a7a7566212 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-element-initializer.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-element-initializer.controller.ts @@ -1,7 +1,6 @@ import { createExtensionElement } from '../functions/create-extension-element.function.js'; import { UmbExtensionRegistry } from '../registry/extension.registry.js'; -import { isManifestElementableType } from '../type-guards/is-manifest-elementable-type.function.js'; -import { ManifestCondition, ManifestWithDynamicConditions } from '../types.js'; +import { ManifestCondition, ManifestWithDynamicConditions } from '../types/index.js'; import { UmbBaseExtensionInitializer } from './base-extension-initializer.controller.js'; import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -10,15 +9,15 @@ import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; * When the extension is permitted to be used, its Element will be instantiated and available for the consumer. * * @example -* ```ts -* const controller = new UmbExtensionElementController(host, extensionRegistry, alias, (permitted, ctrl) => { console.log("Extension is permitted and this is the element: ", ctrl.component) })); -* ``` + * ```ts + * const controller = new UmbExtensionElementController(host, extensionRegistry, alias, (permitted, ctrl) => { console.log("Extension is permitted and this is the element: ", ctrl.component) })); + * ``` * @export * @class UmbExtensionElementController */ export class UmbExtensionElementInitializer< ManifestType extends ManifestWithDynamicConditions = ManifestWithDynamicConditions, - ControllerType extends UmbExtensionElementInitializer = any + ControllerType extends UmbExtensionElementInitializer = any, > extends UmbBaseExtensionInitializer { #defaultElement?: string; #component?: HTMLElement; @@ -61,7 +60,7 @@ export class UmbExtensionElementInitializer< extensionRegistry: UmbExtensionRegistry, alias: string, onPermissionChanged: (isPermitted: boolean, controller: ControllerType) => void, - defaultElement?: string + defaultElement?: string, ) { super(host, extensionRegistry, 'extElement_', alias, onPermissionChanged); this.#defaultElement = defaultElement; @@ -80,24 +79,18 @@ export class UmbExtensionElementInitializer< protected async _conditionsAreGood() { const manifest = this.manifest!; // In this case we are sure its not undefined. - if (isManifestElementableType(manifest)) { - const newComponent = await createExtensionElement(manifest, this.#defaultElement); - if (!this._positive) { - // We are not positive anymore, so we will back out of this creation. - return false; - } - this.#component = newComponent; - - } else if (this.#defaultElement) { - this.#component = document.createElement(this.#defaultElement); - } else { - this.#component = undefined; - console.warn('Manifest did not provide any useful data for a web component to be created.') + const newComponent = await createExtensionElement(manifest, this.#defaultElement); + if (!this._isConditionsPositive) { + // We are not positive anymore, so we will back out of this creation. + return false; } + this.#component = newComponent; if (this.#component) { this.#assignProperties(); (this.#component as any).manifest = manifest; return true; // we will confirm we have a component and are still good to go. + } else { + console.warn('Manifest did not provide any useful data for a web component to be created.'); } return false; // we will reject the state, we have no component, we are not good to be shown. diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-manifest-initializer.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-manifest-initializer.controller.ts index 222c495e30..b772dd0f95 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-manifest-initializer.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extension-manifest-initializer.controller.ts @@ -1,4 +1,4 @@ -import type { ManifestCondition, ManifestWithDynamicConditions } from '../types.js'; +import type { ManifestCondition, ManifestWithDynamicConditions } from '../types/index.js'; import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; import { UmbBaseExtensionInitializer } from './base-extension-initializer.controller.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extensions-api-initializer.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extensions-api-initializer.controller.ts index 29de2f954c..bf56b2a980 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extensions-api-initializer.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extensions-api-initializer.controller.ts @@ -1,8 +1,7 @@ +import { ManifestTypeMap, SpecificManifestTypeOrManifestBase } from '../types/map.types.js'; import { type PermittedControllerType, UmbBaseExtensionsInitializer } from './base-extensions-initializer.controller.js'; import { UmbExtensionApiInitializer } from './extension-api-initializer.controller.js'; import { - type ManifestTypeMap, - type SpecificManifestTypeOrManifestBase, type UmbExtensionRegistry, ManifestApi, ManifestBase, diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extensions-element-initializer.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extensions-element-initializer.controller.ts index 9a8a9caf75..d12714c73b 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extensions-element-initializer.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extensions-element-initializer.controller.ts @@ -1,8 +1,10 @@ -import { type PermittedControllerType, UmbBaseExtensionsInitializer } from './base-extensions-initializer.controller.js'; +import type { ManifestTypeMap, SpecificManifestTypeOrManifestBase } from '../types/map.types.js'; +import { + type PermittedControllerType, + UmbBaseExtensionsInitializer, +} from './base-extensions-initializer.controller.js'; import { type ManifestBase, - type ManifestTypeMap, - type SpecificManifestTypeOrManifestBase, UmbExtensionElementInitializer, type UmbExtensionRegistry, } from '@umbraco-cms/backoffice/extension-api'; @@ -10,12 +12,12 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; /** */ -export class UmbExtensionsElementController< +export class UmbExtensionsElementInitializer< ManifestTypes extends ManifestBase, - ManifestTypeName extends keyof ManifestTypeMap | string = string, + ManifestTypeName extends keyof ManifestTypeMap | string = ManifestTypes['type'], ManifestType extends ManifestBase = SpecificManifestTypeOrManifestBase, ControllerType extends UmbExtensionElementInitializer = UmbExtensionElementInitializer, - MyPermittedControllerType extends ControllerType = PermittedControllerType + MyPermittedControllerType extends ControllerType = PermittedControllerType, > extends UmbBaseExtensionsInitializer< ManifestTypes, ManifestTypeName, @@ -44,7 +46,7 @@ export class UmbExtensionsElementController< type: ManifestTypeName | Array, filter: undefined | null | ((manifest: ManifestType) => boolean), onChange: (permittedManifests: Array) => void, - defaultElement?: string + defaultElement?: string, ) { super(host, extensionRegistry, type, filter, onChange); this.#extensionRegistry = extensionRegistry; @@ -58,7 +60,7 @@ export class UmbExtensionsElementController< this.#extensionRegistry, manifest.alias, this._extensionChanged, - this._defaultElement + this._defaultElement, ) as ControllerType; extController.properties = this.#props; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extensions-manifest-initializer.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extensions-manifest-initializer.controller.ts index 2ec52aa342..59fd8888c7 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extensions-manifest-initializer.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/extensions-manifest-initializer.controller.ts @@ -1,16 +1,15 @@ +import type { ManifestTypeMap, SpecificManifestTypeOrManifestBase } from '../types/map.types.js'; import { UmbExtensionManifestInitializer } from './extension-manifest-initializer.controller.js'; import { type PermittedControllerType, UmbBaseExtensionsInitializer } from './base-extensions-initializer.controller.js'; import { - ManifestBase, - ManifestTypeMap, - SpecificManifestTypeOrManifestBase, - UmbExtensionRegistry, + type ManifestBase, + type UmbExtensionRegistry, } from '@umbraco-cms/backoffice/extension-api'; import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; /** */ -export class UmbExtensionsManifestController< +export class UmbExtensionsManifestInitializer< ManifestTypes extends ManifestBase, ManifestTypeName extends keyof ManifestTypeMap | string, ManifestType extends ManifestBase = SpecificManifestTypeOrManifestBase, diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-api.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-api.function.ts index 0430f7fc84..909a3990b1 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-api.function.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-api.function.ts @@ -1,37 +1,37 @@ -import { hasApiExport, hasDefaultExport, isManifestApiConstructorType } from '../type-guards/index.js'; -import type { ManifestApi, ClassConstructor, ManifestElementAndApi, UmbApi } from '../types.js'; -import { loadExtensionApi } from '../functions/load-extension-api.function.js'; +import { UmbApi } from "../models/api.interface.js"; +import { ManifestApi, ManifestElementAndApi } from "../types/base.types.js"; +import { loadManifestApi } from "./load-manifest-api.function.js"; -//TODO: Write tests for this method: -export async function createExtensionApi( - manifest: ManifestApi | ManifestElementAndApi, - constructorArguments: unknown[] = [] -): Promise { - const js = await loadExtensionApi(manifest); +export async function createExtensionApi(manifest: ManifestApi | ManifestElementAndApi, constructorArguments: Array = []): Promise { - if (isManifestApiConstructorType(manifest)) { - return new manifest.api(...constructorArguments); + if(manifest.api) { + const apiConstructor = await loadManifestApi(manifest.api); + if(apiConstructor) { + return new apiConstructor(...constructorArguments); + } else { + console.error( + `-- Extension of alias "${manifest.alias}" did not succeed instantiate a API class via the extension manifest property 'api', using either a 'api' or 'default' export`, + manifest + ); + } } - if (js) { - if (hasApiExport>(js)) { - return new js.api(...constructorArguments); + if(manifest.js) { + const apiConstructor2 = await loadManifestApi(manifest.js); + if(apiConstructor2) { + return new apiConstructor2(...constructorArguments); + } else { + console.error( + `-- Extension of alias "${manifest.alias}" did not succeed instantiate a API class via the extension manifest property 'js', using either a 'api' or 'default' export`, + manifest + ); } - if (hasDefaultExport>(js)) { - return new js.default(...constructorArguments); - } - - console.error( - `-- Extension of alias "${manifest.alias}" did not succeed creating an api class instance, missing a 'api' or 'default' export of the served JavaScript file`, - manifest - ); - - return undefined; } console.error( - `-- Extension of alias "${manifest.alias}" did not succeed creating an api class instance, missing a JavaScript file via the 'apiJs' or 'js' property or a ClassConstructor in 'api' in the manifest.`, + `-- Extension of alias "${manifest.alias}" did not succeed creating an api class instance, missing a JavaScript file via the 'api' or 'js' property.`, manifest ); + return undefined; } diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-api.test.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-api.test.ts index f04db5c957..28417d3eda 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-api.test.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-api.test.ts @@ -1,5 +1,6 @@ import { expect } from '@open-wc/testing'; -import { ManifestApi, UmbApi } from '../types.js'; +import { ManifestApi } from '../types/index.js'; +import { UmbApi } from '../models/api.interface.js'; import { createExtensionApi } from './create-extension-api.function.js'; @@ -44,7 +45,7 @@ describe('Extension-Api: Create Extension Api', () => { const manifest: ManifestApi = { type: 'my-test-type', - alias: 'Umb.Test.CreateExtensionApi', + alias: 'Umb.Test.createManifestApi', name: 'pretty name' }; @@ -56,7 +57,7 @@ describe('Extension-Api: Create Extension Api', () => { const manifest: ManifestApi = { type: 'my-test-type', - alias: 'Umb.Test.CreateExtensionApi', + alias: 'Umb.Test.createManifestApi', name: 'pretty name', api: UmbExtensionApiTrueTestClass }; @@ -72,9 +73,9 @@ describe('Extension-Api: Create Extension Api', () => { const manifest: ManifestApi = { type: 'my-test-type', - alias: 'Umb.Test.CreateExtensionApi', + alias: 'Umb.Test.createManifestApi', name: 'pretty name', - loader: () => Promise.resolve(jsModuleWithDefaultExport) + js: () => Promise.resolve(jsModuleWithDefaultExport) }; const api = await createExtensionApi(manifest, []); @@ -88,9 +89,9 @@ describe('Extension-Api: Create Extension Api', () => { const manifest: ManifestApi = { type: 'my-test-type', - alias: 'Umb.Test.CreateExtensionApi', + alias: 'Umb.Test.createManifestApi', name: 'pretty name', - loader: () => Promise.resolve(jsModuleWithApiExport) + js: () => Promise.resolve(jsModuleWithApiExport) }; const api = await createExtensionApi(manifest, []); @@ -104,9 +105,9 @@ describe('Extension-Api: Create Extension Api', () => { const manifest: ManifestApi = { type: 'my-test-type', - alias: 'Umb.Test.CreateExtensionApi', + alias: 'Umb.Test.createManifestApi', name: 'pretty name', - loader: () => Promise.resolve(jsModuleWithDefaultAndApiExport) + js: () => Promise.resolve(jsModuleWithDefaultAndApiExport) }; const api = await createExtensionApi(manifest, []); diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-element.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-element.function.ts index 91d66ba23a..2cd8677813 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-element.function.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-element.function.ts @@ -1,45 +1,42 @@ -import { hasDefaultExport, hasElementExport, isManifestElementNameType } from '../type-guards/index.js'; -import type { HTMLElementConstructor, ManifestElement } from '../types.js'; -import { loadExtensionElement } from '../functions/load-extension-element.function.js'; +import { ManifestElement, ManifestElementAndApi } from "../types/base.types.js"; +import { loadManifestElement } from "./load-manifest-element.function.js"; -export async function createExtensionElement( - manifest: ManifestElement, fallbackElementName?: string -): Promise { - //TODO: Write tests for these extension options: - const js = await loadExtensionElement(manifest); +export async function createExtensionElement(manifest: ManifestElement | ManifestElementAndApi, fallbackElement?: string): Promise { - if (isManifestElementNameType(manifest)) { - // created by manifest method providing HTMLElement + if(manifest.element) { + const elementConstructor = await loadManifestElement(manifest.element); + if(elementConstructor) { + return new elementConstructor(); + } else { + console.error( + `-- Extension of alias "${manifest.alias}" did not succeed creating an element class instance via the extension manifest property 'element', using either a 'element' or 'default' export`, + manifest + ); + } + } + + if(manifest.js) { + const elementConstructor2 = await loadManifestElement(manifest.js); + if(elementConstructor2) { + return new elementConstructor2(); + } else { + console.error( + `-- Extension of alias "${manifest.alias}" did not succeed creating an element class instance via the extension manifest property 'js', using either a 'element' or 'default' export`, + manifest + ); + } + } + + if(manifest.elementName) { return document.createElement(manifest.elementName) as ElementType; } - // TODO: Do we need this except for the default() loader? - if (js) { - if (hasElementExport>(js)) { - // Element will be created by default class - return new js.element(); - } - if (hasDefaultExport>(js)) { - // Element will be created by default class - return new js.default(); - } - - if(!fallbackElementName) { - console.error( - `-- Extension of alias "${manifest.alias}" did not succeed creating an api class instance, missing a 'element' or 'default' export of the served JavaScript file`, - manifest - ); - return undefined; - } + if(fallbackElement) { + return document.createElement(fallbackElement) as ElementType; } - if(fallbackElementName) { - return document.createElement(fallbackElementName) as ElementType; - } - - // If some JS was loaded and manifest did not have a elementName neither it the JS file contain a default export, so we will fail: console.error( - `-- Extension of alias "${manifest.alias}" did not succeed creating an element, missing a JavaScript file via the 'elementJs' or 'js' property or a Element Name in 'elementName' in the manifest.`, + `-- Extension of alias "${manifest.alias}" did not succeed creating an element, missing a JavaScript file via the 'element' or 'js' property or a Element Name in 'elementName' in the manifest.`, manifest ); return undefined; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-element.test.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-element.test.ts index a19728584d..21c4611c14 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-element.test.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/create-extension-element.test.ts @@ -1,5 +1,5 @@ import { expect } from '@open-wc/testing'; -import { ManifestElement, ManifestElementAndApi } from '../types.js'; +import { ManifestElement, ManifestElementAndApi } from '../types/index.js'; import { createExtensionElement } from './create-extension-element.function.js'; import { customElement } from '@umbraco-cms/backoffice/external/lit'; @@ -43,7 +43,7 @@ describe('Extension-Api: Create Extension Element', () => { const manifest: ManifestElement = { type: 'my-test-type', - alias: 'Umb.Test.CreateExtensionElement', + alias: 'Umb.Test.CreateManifestElement', name: 'pretty name' }; @@ -55,7 +55,7 @@ describe('Extension-Api: Create Extension Element', () => { const manifest: ManifestElement = { type: 'my-test-type', - alias: 'Umb.Test.CreateExtensionElement', + alias: 'Umb.Test.CreateManifestElement', name: 'pretty name' }; @@ -71,7 +71,7 @@ describe('Extension-Api: Create Extension Element', () => { const manifest: ManifestElementAndApi = { type: 'my-test-type', - alias: 'Umb.Test.CreateExtensionElement', + alias: 'Umb.Test.CreateManifestElement', name: 'pretty name', api: class TestApi {} }; @@ -87,7 +87,7 @@ describe('Extension-Api: Create Extension Element', () => { const manifest: ManifestElement = { type: 'my-test-type', - alias: 'Umb.Test.CreateExtensionElement', + alias: 'Umb.Test.CreateManifestElement', name: 'pretty name', elementName: 'umb-extension-api-true-test-element' }; @@ -103,9 +103,9 @@ describe('Extension-Api: Create Extension Element', () => { const manifest: ManifestElement = { type: 'my-test-type', - alias: 'Umb.Test.CreateExtensionElement', + alias: 'Umb.Test.CreateManifestElement', name: 'pretty name', - loader: () => Promise.resolve(jsModuleWithDefaultExport) + js: () => Promise.resolve(jsModuleWithDefaultExport) }; const element = await createExtensionElement(manifest); @@ -119,9 +119,9 @@ describe('Extension-Api: Create Extension Element', () => { const manifest: ManifestElement = { type: 'my-test-type', - alias: 'Umb.Test.CreateExtensionElement', + alias: 'Umb.Test.CreateManifestElement', name: 'pretty name', - loader: () => Promise.resolve(jsModuleWithElementExport) + js: () => Promise.resolve(jsModuleWithElementExport) }; const element = await createExtensionElement(manifest); @@ -135,9 +135,9 @@ describe('Extension-Api: Create Extension Element', () => { const manifest: ManifestElement = { type: 'my-test-type', - alias: 'Umb.Test.CreateExtensionElement', + alias: 'Umb.Test.CreateManifestElement', name: 'pretty name', - loader: () => Promise.resolve(jsModuleWithDefaultAndElementExport) + js: () => Promise.resolve(jsModuleWithDefaultAndElementExport) }; const element = await createExtensionElement(manifest); diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-init-export.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-init-export.function.ts index 53058f7f82..3c808cc56e 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-init-export.function.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-init-export.function.ts @@ -1,7 +1,7 @@ import type { UmbEntryPointModule } from '../models/entry-point.interface.js'; /** - * Validate if an ESModule exports a known init function called 'onInit' + * Validate if an ESModule export has a function called 'onInit' */ export function hasInitExport(obj: unknown): obj is Pick { return obj !== null && typeof obj === 'object' && 'onInit' in obj; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/index.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/index.ts index 23cf8c67b6..b90a955eac 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/index.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/index.ts @@ -2,6 +2,7 @@ export * from './create-extension-api.function.js'; export * from './create-extension-element.function.js'; export * from './has-init-export.function.js'; -export * from './load-extension-api.function.js'; -export * from './load-extension-element.function.js'; -export * from './load-extension.function.js'; +export * from './load-manifest-api.function.js'; +export * from './load-manifest-element.function.js'; +export * from './load-manifest-plain-js.function.js'; +export * from './load-manifest-plain-css.function.js'; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-extension-api.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-extension-api.function.ts deleted file mode 100644 index 7570cbb4c8..0000000000 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-extension-api.function.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { isManifestApiJSType, isManifestJSType, isManifestLoaderType } from '../type-guards/index.js'; -import type { ManifestWithLoader } from '../types.js'; - -export async function loadExtensionApi(manifest: ManifestWithLoader): Promise { - try { - if (isManifestLoaderType(manifest)) { - return manifest.loader(); - } - - if (isManifestApiJSType(manifest) && manifest.apiJs) { - return await import(/* @vite-ignore */ manifest.apiJs); - } - - if (isManifestJSType(manifest) && manifest.js) { - return await import(/* @vite-ignore */ manifest.js); - } - } catch (err: any) { - console.warn('-- Extension failed to load script', manifest, err); - return Promise.resolve(null); - } - - return Promise.resolve(null); -} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-extension-element.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-extension-element.function.ts deleted file mode 100644 index baea42494f..0000000000 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-extension-element.function.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { isManifestElementJSType, isManifestJSType, isManifestLoaderType } from '../type-guards/index.js'; -import type { ManifestWithLoader } from '../types.js'; - -export async function loadExtensionElement(manifest: ManifestWithLoader): Promise { - try { - if (isManifestLoaderType(manifest)) { - return manifest.loader(); - } - - if (isManifestElementJSType(manifest) && manifest.elementJs) { - return await import(/* @vite-ignore */ manifest.elementJs); - } - - if (isManifestJSType(manifest) && manifest.js) { - return await import(/* @vite-ignore */ manifest.js); - } - } catch (err: any) { - console.warn('-- Extension failed to load script', manifest, err); - return Promise.resolve(null); - } - - return Promise.resolve(null); -} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-extension.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-extension.function.ts deleted file mode 100644 index b983826092..0000000000 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-extension.function.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { isManifestJSType, isManifestLoaderType } from '../type-guards/index.js'; -import type { ManifestWithLoader } from '../types.js'; - -export async function loadExtension(manifest: ManifestWithLoader): Promise { - try { - if (isManifestLoaderType(manifest)) { - return await manifest.loader(); - } - - if (isManifestJSType(manifest) && manifest.js) { - return await import(/* @vite-ignore */ manifest.js); - } - } catch (err: any) { - console.warn('-- Extension failed to load script', manifest, err); - return Promise.resolve(null); - } - - return Promise.resolve(null); -} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-manifest-api.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-manifest-api.function.ts new file mode 100644 index 0000000000..ed9db89316 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-manifest-api.function.ts @@ -0,0 +1,31 @@ +import type { UmbApi } from "../models/api.interface.js"; +import type { ApiLoaderExports, ApiLoaderProperty, ClassConstructor, ElementAndApiLoaderProperty, ElementLoaderExports } from "../types/utils.js"; + +export async function loadManifestApi(property: ApiLoaderProperty | ElementAndApiLoaderProperty): Promise | undefined> { + const propType = typeof property + if(propType === 'function') { + if((property as ClassConstructor).prototype) { + // Class Constructor + return property as ClassConstructor; + } else { + // Promise function + const result = await (property as (Exclude, string>, ClassConstructor>))(); + if(typeof result === 'object' && result != null) { + const exportValue = 'api' in result ? result.api : undefined || 'default' in result ? (result as Exclude<(typeof result), ElementLoaderExports>).default : undefined; + if(exportValue && typeof exportValue === 'function') { + return exportValue; + } + } + } + } else if(propType === 'string') { + // Import string + const result = await (import(/* @vite-ignore */ property as string) as unknown as ApiLoaderExports); + if(typeof result === 'object' && result != null) { + const exportValue = 'api' in result ? result.api : undefined || 'default' in result ? result.default : undefined; + if(exportValue && typeof exportValue === 'function') { + return exportValue; + } + } + } + return undefined; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-manifest-element.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-manifest-element.function.ts new file mode 100644 index 0000000000..b1fcd616d8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-manifest-element.function.ts @@ -0,0 +1,31 @@ +import type { ApiLoaderExports, ClassConstructor, ElementAndApiLoaderProperty, ElementLoaderExports, ElementLoaderProperty } from "../types/utils.js"; + + +export async function loadManifestElement(property: ElementLoaderProperty | ElementAndApiLoaderProperty): Promise | undefined> { + const propType = typeof property + if(propType === 'function') { + if((property as ClassConstructor).prototype) { + // Class Constructor + return property as ClassConstructor; + } else { + // Promise function + const result = await (property as (Exclude, ClassConstructor>))(); + if(typeof result === 'object' && result !== null) { + const exportValue = 'element' in result ? result.element : undefined || 'default' in result ? (result as Exclude<(typeof result), ApiLoaderExports>).default : undefined; + if(exportValue && typeof exportValue === 'function') { + return exportValue; + } + } + } + } else if(propType === 'string') { + // Import string + const result = await (import(/* @vite-ignore */ property as string) as unknown as ElementLoaderExports); + if(typeof result === 'object' && result != null) { + const exportValue = 'element' in result ? result.element : undefined || 'default' in result ? result.default : undefined; + if(exportValue && typeof exportValue === 'function') { + return exportValue; + } + } + } + return undefined; +} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-manifest-plain-css.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-manifest-plain-css.function.ts new file mode 100644 index 0000000000..01006b2350 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-manifest-plain-css.function.ts @@ -0,0 +1,18 @@ +import type { JsLoaderProperty } from "../types/utils.js"; + +export async function loadManifestPlainCss(property: JsLoaderProperty): Promise { + const propType = typeof property; + if(propType === 'function') { + const result = await (property as (Exclude<(typeof property), string>))(); + if(result != null) { + return result; + } + } else if(propType === 'string') { + // Import string + const result = await (import(/* @vite-ignore */ property as string) as unknown as CssType); + if(result != null) { + return result; + } + } + return undefined; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-manifest-plain-js.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-manifest-plain-js.function.ts new file mode 100644 index 0000000000..0611d7eb25 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/load-manifest-plain-js.function.ts @@ -0,0 +1,18 @@ +import type { JsLoaderProperty } from "../types/utils.js"; + +export async function loadManifestPlainJs(property: JsLoaderProperty): Promise { + const propType = typeof property; + if(propType === 'function') { + const result = await (property as (Exclude<(typeof property), string>))(); + if(typeof result === 'object' && result != null) { + return result; + } + } else if(propType === 'string') { + // Import string + const result = await (import(/* @vite-ignore */ property as string) as unknown as JsType); + if(typeof result === 'object' && result != null) { + return result; + } + } + return undefined; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/index.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/index.ts index 84366ec12c..c8ce6b7200 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/index.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/index.ts @@ -1,8 +1,8 @@ -export * from './initializers/index.js'; export * from './condition/index.js'; export * from './controller/index.js'; export * from './functions/index.js'; +export * from './initializers/index.js'; export * from './models/index.js'; export * from './registry/extension.registry.js'; export * from './type-guards/index.js'; -export * from './types.js'; +export * from './types/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/bundle-extension-initializer.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/bundle-extension-initializer.ts index 4150e2f0da..68e4a7d6ac 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/bundle-extension-initializer.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/bundle-extension-initializer.ts @@ -1,6 +1,6 @@ -import type { ManifestBase, ManifestBundle } from '../types.js'; +import type { ManifestBase, ManifestBundle } from '../types/index.js'; import { UmbExtensionRegistry } from '../registry/extension.registry.js'; -import { loadExtension } from '../functions/load-extension.function.js'; +import { loadManifestPlainJs } from '../functions/load-manifest-plain-js.function.js'; import { UmbBaseController, UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; export class UmbBundleExtensionInitializer extends UmbBaseController { @@ -29,34 +29,38 @@ export class UmbBundleExtensionInitializer extends UmbBaseController { } async instantiateBundle(manifest: ManifestBundle) { - const js = await loadExtension(manifest); + if(manifest.js) { + const js = await loadManifestPlainJs(manifest.js); - if (js) { - Object.keys(js).forEach((key) => { - const value = js[key]; + if (js) { + Object.keys(js).forEach((key) => { + const value = js[key]; - if (Array.isArray(value)) { - this.#extensionRegistry.registerMany(value); - } else if (typeof value === 'object') { - this.#extensionRegistry.register(value); - } - }); + if (Array.isArray(value)) { + this.#extensionRegistry.registerMany(value); + } else if (typeof value === 'object') { + this.#extensionRegistry.register(value); + } + }); + } } } async unregisterBundle(manifest: ManifestBundle) { - const js = await loadExtension(manifest); + if(manifest.js) { + const js = await loadManifestPlainJs(manifest.js); - if (js) { - Object.keys(js).forEach((key) => { - const value = js[key]; + if (js) { + Object.keys(js).forEach((key) => { + const value = js[key]; - if (Array.isArray(value)) { - this.#extensionRegistry.unregisterMany(value.map((v) => v.alias)); - } else if (typeof value === 'object') { - this.#extensionRegistry.unregister((value as ManifestBase).alias); - } - }); + if (Array.isArray(value)) { + this.#extensionRegistry.unregisterMany(value.map((v) => v.alias)); + } else if (typeof value === 'object') { + this.#extensionRegistry.unregister((value as ManifestBase).alias); + } + }); + } } } } diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/entry-point-extension-initializer.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/entry-point-extension-initializer.ts index ad93574a51..0d4a62585d 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/entry-point-extension-initializer.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/entry-point-extension-initializer.ts @@ -1,7 +1,7 @@ import { UmbBaseController } from '../../controller-api/controller.class.js'; -import type { ManifestEntryPoint } from '../types.js'; +import type { ManifestEntryPoint } from '../types/index.js'; import { UmbExtensionRegistry } from '../registry/extension.registry.js'; -import { hasInitExport, loadExtension } from '../functions/index.js'; +import { hasInitExport, loadManifestPlainJs } from '../functions/index.js'; import { UmbElement } from '@umbraco-cms/backoffice/element-api'; export class UmbEntryPointExtensionInitializer extends UmbBaseController { @@ -25,10 +25,12 @@ export class UmbEntryPointExtensionInitializer extends UmbBaseController { } async instantiateEntryPoint(manifest: ManifestEntryPoint) { - const js = await loadExtension(manifest); - // If the extension has an onInit export, be sure to run that or else let the module handle itself - if (hasInitExport(js)) { - js.onInit(this.#host, this.#extensionRegistry); + if(manifest.js) { + const js = await loadManifestPlainJs(manifest.js); + // If the extension has an onInit export, be sure to run that or else let the module handle itself + if (hasInitExport(js)) { + js.onInit(this.#host, this.#extensionRegistry); + } } } } diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/models/api.interface.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/models/api.interface.ts new file mode 100644 index 0000000000..895fd43f69 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/models/api.interface.ts @@ -0,0 +1,3 @@ +export interface UmbApi { + destroy?(): void; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/models/entry-point.interface.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/models/entry-point.interface.ts index addddad6be..43fe10fa8d 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/models/entry-point.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/models/entry-point.interface.ts @@ -1,5 +1,5 @@ import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; -import { ManifestBase } from '../types.js'; +import { ManifestBase } from '../types/index.js'; import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; export type UmbEntryPointOnInit = (host: UmbElement, extensionRegistry: UmbExtensionRegistry) => void; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/models/index.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/models/index.ts index 09ef59f5ac..643057374c 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/models/index.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/models/index.ts @@ -1 +1,2 @@ export * from './entry-point.interface.js' +export * from './api.interface.js' diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/registry/extension.registry.test.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/registry/extension.registry.test.ts index a7f5b534db..b0492dc21c 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/registry/extension.registry.test.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/registry/extension.registry.test.ts @@ -1,10 +1,14 @@ import { expect } from '@open-wc/testing'; -import type { ManifestElementWithElementName, ManifestKind, ManifestWithMeta } from '../types.js'; +import type { ManifestElementWithElementName, ManifestKind, ManifestBase } from '../types/index.js'; import { UmbExtensionRegistry } from './extension.registry.js'; +interface TestManifestWithMeta extends ManifestBase { + meta: unknown; +} + describe('UmbExtensionRegistry', () => { - let extensionRegistry: UmbExtensionRegistry; - let manifests: Array; + let extensionRegistry: UmbExtensionRegistry; + let manifests: Array; beforeEach(() => { extensionRegistry = new UmbExtensionRegistry(); @@ -170,7 +174,7 @@ describe('UmbExtensionRegistry', () => { describe('UmbExtensionRegistry with kinds', () => { let extensionRegistry: UmbExtensionRegistry; let manifests: Array< - ManifestElementWithElementName | ManifestWithMeta | ManifestKind + ManifestElementWithElementName | TestManifestWithMeta | ManifestKind >; beforeEach(() => { diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/registry/extension.registry.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/registry/extension.registry.ts index 435bc85ed5..42a12a07e0 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/registry/extension.registry.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/registry/extension.registry.ts @@ -1,4 +1,5 @@ -import type { ManifestTypeMap, ManifestBase, SpecificManifestTypeOrManifestBase, ManifestKind } from '../types.js'; +import type { ManifestBase, ManifestKind } from '../types/index.js'; +import { ManifestTypeMap, SpecificManifestTypeOrManifestBase } from '../types/map.types.js'; import { UmbBasicState } from '@umbraco-cms/backoffice/observable-api'; import { map, @@ -11,7 +12,7 @@ import { function extensionArrayMemoization>( previousValue: Array, - currentValue: Array + currentValue: Array, ): boolean { // If length is different, data is different: if (previousValue.length !== currentValue.length) { @@ -24,10 +25,9 @@ function extensionArrayMemoization>( return true; } -function extensionAndKindMatchArrayMemoization & { isMatchedWithKind?: boolean }>( - previousValue: Array, - currentValue: Array -): boolean { +function extensionAndKindMatchArrayMemoization< + T extends Pick & { __isMatchedWithKind?: boolean }, +>(previousValue: Array, currentValue: Array): boolean { // If length is different, data is different: if (previousValue.length !== currentValue.length) { return false; @@ -42,8 +42,8 @@ function extensionAndKindMatchArrayMemoization { const oldValue = previousValue.find((c) => c.alias === newValue.alias); // First check if we found a previous value, matching this alias. - // Then checking isMatchedWithKind, as this is much more performant than checking the whole object. (I assume the only change happening to an extension is the match with a kind, we do not want to watch for other changes) - return oldValue === undefined || newValue.isMatchedWithKind !== oldValue.isMatchedWithKind; + // Then checking __isMatchedWithKind, as this is much more performant than checking the whole object. (I assume the only change happening to an extension is the match with a kind, we do not want to watch for other changes) + return oldValue === undefined || newValue.__isMatchedWithKind !== oldValue.__isMatchedWithKind; }) ) { return false; @@ -53,7 +53,7 @@ function extensionAndKindMatchArrayMemoization>( previousValue: T | undefined, - currentValue: T | undefined + currentValue: T | undefined, ): boolean { if (previousValue && currentValue) { return previousValue.alias === currentValue.alias; @@ -62,11 +62,12 @@ function extensionSingleMemoization>( } function extensionAndKindMatchSingleMemoization< - T extends Pick & { isMatchedWithKind?: boolean } + T extends Pick & { __isMatchedWithKind?: boolean }, >(previousValue: T | undefined, currentValue: T | undefined): boolean { if (previousValue && currentValue) { return ( - previousValue.alias === currentValue.alias && previousValue.isMatchedWithKind === currentValue.isMatchedWithKind + previousValue.alias === currentValue.alias && + previousValue.__isMatchedWithKind === currentValue.__isMatchedWithKind ); } return previousValue === currentValue; @@ -76,7 +77,7 @@ const sortExtensions = (a: ManifestBase, b: ManifestBase) => (b.weight || 0) - ( export class UmbExtensionRegistry< IncomingManifestTypes extends ManifestBase, - ManifestTypes extends ManifestBase = IncomingManifestTypes | ManifestBase + ManifestTypes extends ManifestBase = IncomingManifestTypes | ManifestBase, > { readonly MANIFEST_TYPES: ManifestTypes = undefined as never; @@ -89,7 +90,7 @@ export class UmbExtensionRegistry< defineKind(kind: ManifestKind) { const extensionsValues = this._extensions.getValue(); const extension = extensionsValues.find( - (extension) => extension.alias === (kind as ManifestKind).alias + (extension) => extension.alias === (kind as ManifestKind).alias, ); if (extension) { @@ -104,7 +105,7 @@ export class UmbExtensionRegistry< !( k.matchType === (kind as ManifestKind).matchType && k.matchKind === (kind as ManifestKind).matchKind - ) + ), ); nextData.push(kind as ManifestKind); this._kinds.next(nextData); @@ -185,29 +186,29 @@ export class UmbExtensionRegistry< private _kindsOfType | string>(type: Key) { return this.kinds.pipe( map((kinds) => kinds.filter((kind) => kind.matchType === type)), - distinctUntilChanged(extensionArrayMemoization) + distinctUntilChanged(extensionArrayMemoization), ); } private _extensionsOfType | string>(type: Key) { return this.extensions.pipe( map((exts) => exts.filter((ext) => ext.type === type)), - distinctUntilChanged(extensionArrayMemoization) + distinctUntilChanged(extensionArrayMemoization), ); } private _kindsOfTypes(types: string[]) { return this.kinds.pipe( map((kinds) => kinds.filter((kind) => types.indexOf(kind.matchType) !== -1)), - distinctUntilChanged(extensionArrayMemoization) + distinctUntilChanged(extensionArrayMemoization), ); } // TODO: can we get rid of as unknown here private _extensionsOfTypes( - types: Array + types: Array, ): Observable> { return this.extensions.pipe( map((exts) => exts.filter((ext) => types.indexOf(ext.type) !== -1)), - distinctUntilChanged(extensionArrayMemoization) + distinctUntilChanged(extensionArrayMemoization), ) as unknown as Observable>; } @@ -225,7 +226,7 @@ export class UmbExtensionRegistry< const baseManifest = kinds.find((kind) => kind.matchKind === ext.kind)?.manifest; // TODO: This check can go away when making a find kind based on type and kind. if (baseManifest) { - const merged = { isMatchedWithKind: true, ...baseManifest, ...ext } as any; + const merged = { __isMatchedWithKind: true, ...baseManifest, ...ext } as any; if ((baseManifest as any).meta) { merged.meta = { ...(baseManifest as any).meta, ...(ext as any).meta }; } @@ -233,24 +234,24 @@ export class UmbExtensionRegistry< } } return ext; - }) + }), ); } return of(ext); }), - distinctUntilChanged(extensionAndKindMatchSingleMemoization) + distinctUntilChanged(extensionAndKindMatchSingleMemoization), ) as Observable; } getByTypeAndAlias< Key extends keyof ManifestTypeMap | string, - T extends ManifestBase = SpecificManifestTypeOrManifestBase + T extends ManifestBase = SpecificManifestTypeOrManifestBase, >(type: Key, alias: string) { return combineLatest([ this.extensions.pipe( map((exts) => exts.find((ext) => ext.type === type && ext.alias === alias)), - distinctUntilChanged(extensionSingleMemoization) + distinctUntilChanged(extensionSingleMemoization), ), this._kindsOfType(type), ]).pipe( @@ -260,7 +261,7 @@ export class UmbExtensionRegistry< if (ext) { const baseManifest = kinds.find((kind) => kind.matchKind === ext.kind)?.manifest; if (baseManifest) { - const merged = { isMatchedWithKind: true, ...baseManifest, ...ext } as any; + const merged = { __isMatchedWithKind: true, ...baseManifest, ...ext } as any; if ((baseManifest as any).meta) { merged.meta = { ...(baseManifest as any).meta, ...(ext as any).meta }; } @@ -269,18 +270,18 @@ export class UmbExtensionRegistry< } return ext; }), - distinctUntilChanged(extensionAndKindMatchSingleMemoization) + distinctUntilChanged(extensionAndKindMatchSingleMemoization), ) as Observable; } getByTypeAndAliases< Key extends keyof ManifestTypeMap | string, - T extends ManifestBase = SpecificManifestTypeOrManifestBase + T extends ManifestBase = SpecificManifestTypeOrManifestBase, >(type: Key, aliases: Array) { return combineLatest([ this.extensions.pipe( map((exts) => exts.filter((ext) => ext.type === type && aliases.indexOf(ext.alias) !== -1)), - distinctUntilChanged(extensionArrayMemoization) + distinctUntilChanged(extensionArrayMemoization), ), this._kindsOfType(type), ]).pipe( @@ -290,7 +291,7 @@ export class UmbExtensionRegistry< // Specific Extension Meta merge (does not merge conditions) const baseManifest = kinds.find((kind) => kind.matchKind === ext.kind)?.manifest; if (baseManifest) { - const merged = { isMatchedWithKind: true, ...baseManifest, ...ext } as any; + const merged = { __isMatchedWithKind: true, ...baseManifest, ...ext } as any; if ((baseManifest as any).meta) { merged.meta = { ...(baseManifest as any).meta, ...(ext as any).meta }; } @@ -298,15 +299,15 @@ export class UmbExtensionRegistry< } return ext; }) - .sort(sortExtensions) + .sort(sortExtensions), ), - distinctUntilChanged(extensionAndKindMatchArrayMemoization) + distinctUntilChanged(extensionAndKindMatchArrayMemoization), ) as Observable>; } extensionsOfType< Key extends keyof ManifestTypeMap | string, - T extends ManifestBase = SpecificManifestTypeOrManifestBase + T extends ManifestBase = SpecificManifestTypeOrManifestBase, >(type: Key) { return combineLatest([this._extensionsOfType(type), this._kindsOfType(type)]).pipe( map(([exts, kinds]) => @@ -315,7 +316,7 @@ export class UmbExtensionRegistry< // Specific Extension Meta merge (does not merge conditions) const baseManifest = kinds.find((kind) => kind.matchKind === ext.kind)?.manifest; if (baseManifest) { - const merged = { isMatchedWithKind: true, ...baseManifest, ...ext } as any; + const merged = { __isMatchedWithKind: true, ...baseManifest, ...ext } as any; if ((baseManifest as any).meta) { merged.meta = { ...(baseManifest as any).meta, ...(ext as any).meta }; } @@ -323,14 +324,14 @@ export class UmbExtensionRegistry< } return ext; }) - .sort(sortExtensions) + .sort(sortExtensions), ), - distinctUntilChanged(extensionAndKindMatchArrayMemoization) + distinctUntilChanged(extensionAndKindMatchArrayMemoization), ) as Observable>; } extensionsOfTypes( - types: string[] + types: string[], ): Observable> { return combineLatest([this._extensionsOfTypes(types), this._kindsOfTypes(types)]).pipe( map(([exts, kinds]) => @@ -340,7 +341,7 @@ export class UmbExtensionRegistry< if (ext) { const baseManifest = kinds.find((kind) => kind.matchKind === ext.kind)?.manifest; if (baseManifest) { - const merged = { isMatchedWithKind: true, ...baseManifest, ...ext } as any; + const merged = { __isMatchedWithKind: true, ...baseManifest, ...ext } as any; if ((baseManifest as any).meta) { merged.meta = { ...(baseManifest as any).meta, ...(ext as any).meta }; } @@ -349,9 +350,9 @@ export class UmbExtensionRegistry< } return ext; }) - .sort(sortExtensions) + .sort(sortExtensions), ), - distinctUntilChanged(extensionAndKindMatchArrayMemoization) + distinctUntilChanged(extensionAndKindMatchArrayMemoization), ) as Observable>; } } diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/index.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/index.ts index e435f99db2..22106b469d 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/index.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/index.ts @@ -1,11 +1,5 @@ -export * from './is-manifest-api-instance-type.function.js'; -export * from './is-manifest-apiable-type.function.js'; -export * from './is-manifest-element-name-type.function.js'; -export * from './is-manifest-elementable-type.function.js'; -export * from './is-manifest-js-type.function.js'; -export * from './is-manifest-element-js-type.function.js'; -export * from './is-manifest-api-js-type.function.js'; -export * from './is-manifest-loader-type.function.js'; +export * from './has-api-export.function.js'; export * from './has-default-export.function.js'; export * from './has-element-export.function.js'; -export * from './has-api-export.function.js'; +export * from './is-manifest-base-type.function.js'; +export * from './is-manifest-element-name-type.function.js'; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-api-instance-type.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-api-instance-type.function.ts deleted file mode 100644 index dbaa792374..0000000000 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-api-instance-type.function.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { ClassConstructor, UmbApi, ManifestApi } from '../types.js'; - -export function isManifestApiConstructorType(manifest: unknown): manifest is ManifestApiWithClassConstructor { - return typeof manifest === 'object' && manifest !== null && (manifest as ManifestApi).api !== undefined; -} - -export interface ManifestApiWithClassConstructor extends ManifestApi { - api: ClassConstructor; -} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-api-js-type.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-api-js-type.function.ts deleted file mode 100644 index 36b30d9509..0000000000 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-api-js-type.function.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { ManifestBase, ManifestWithLoader } from '../types.js'; - -export type ManifestApiJSType = ManifestWithLoader & { apiJs: string }; -export function isManifestApiJSType(manifest: ManifestBase | unknown): manifest is ManifestApiJSType { - return (manifest as ManifestApiJSType).apiJs !== undefined; -} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-apiable-type.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-apiable-type.function.ts deleted file mode 100644 index f42ab3e0ca..0000000000 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-apiable-type.function.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { ManifestBase, ManifestApi } from '../types.js'; -import { isManifestJSType } from './is-manifest-js-type.function.js'; -import { isManifestLoaderType } from './is-manifest-loader-type.function.js'; -import { isManifestApiConstructorType } from './is-manifest-api-instance-type.function.js'; -import { isManifestApiJSType } from './is-manifest-api-js-type.function.js'; - -export function isManifestApiType(manifest: ManifestBase): manifest is ManifestApi { - return ( - isManifestApiConstructorType(manifest) || - isManifestLoaderType(manifest) || - isManifestApiJSType(manifest) || - isManifestJSType(manifest) - ); -} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-base-type.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-base-type.function.ts new file mode 100644 index 0000000000..001d355ef2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-base-type.function.ts @@ -0,0 +1,5 @@ +import type { ManifestBase } from "../types/manifest-base.interface.js"; + +export function isManifestBaseType(x: unknown): x is ManifestBase { + return typeof x === 'object' && x !== null && 'alias' in x; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-element-js-type.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-element-js-type.function.ts deleted file mode 100644 index 8332f84ca6..0000000000 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-element-js-type.function.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { ManifestBase, ManifestWithLoader } from '../types.js'; - -export type ManifestElementJSType = ManifestWithLoader & { elementJs: string }; -export function isManifestElementJSType(manifest: ManifestBase | unknown): manifest is ManifestElementJSType { - return (manifest as ManifestElementJSType).elementJs !== undefined; -} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-element-name-type.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-element-name-type.function.ts index 9c02956850..f59f6249e9 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-element-name-type.function.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-element-name-type.function.ts @@ -1,4 +1,4 @@ -import type { ManifestElement, ManifestElementWithElementName } from '../types.js'; +import type { ManifestElement, ManifestElementWithElementName } from '../types/index.js'; export function isManifestElementNameType(manifest: unknown): manifest is ManifestElementWithElementName { return typeof manifest === 'object' && manifest !== null && (manifest as ManifestElement).elementName !== undefined; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-elementable-type.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-elementable-type.function.ts deleted file mode 100644 index d375ec9766..0000000000 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-elementable-type.function.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { ManifestElement, ManifestBase } from '../types.js'; -import { isManifestElementJSType } from './is-manifest-element-js-type.function.js'; -import { isManifestElementNameType } from './is-manifest-element-name-type.function.js'; -import { isManifestJSType } from './is-manifest-js-type.function.js'; -import { isManifestLoaderType } from './is-manifest-loader-type.function.js'; - -export function isManifestElementableType( - manifest: ManifestBase -): manifest is ManifestElement { - return ( - isManifestElementNameType(manifest) || - isManifestLoaderType(manifest) || - isManifestElementJSType(manifest) || - isManifestJSType(manifest) - ); -} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-js-type.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-js-type.function.ts deleted file mode 100644 index 5220d57f83..0000000000 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-js-type.function.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { ManifestBase, ManifestWithLoader } from '../types.js'; - -export type ManifestJSType = ManifestWithLoader & { js: string }; -export function isManifestJSType(manifest: ManifestBase | unknown): manifest is ManifestJSType { - return (manifest as ManifestJSType).js !== undefined; -} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-loader-type.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-loader-type.function.ts deleted file mode 100644 index bf607aa132..0000000000 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/type-guards/is-manifest-loader-type.function.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { ManifestBase, ManifestWithLoader } from '../types.js'; - -export type ManifestLoaderType = ManifestWithLoader & { - loader: () => Promise; -}; - -export function isManifestLoaderType(manifest: ManifestBase): manifest is ManifestLoaderType { - return typeof (manifest as ManifestLoaderType).loader === 'function'; -} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types.ts deleted file mode 100644 index c073a422db..0000000000 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types.ts +++ /dev/null @@ -1,299 +0,0 @@ -import type { UmbExtensionCondition } from './condition/index.js'; -import type { UmbEntryPointModule } from './models/index.js'; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type HTMLElementConstructor = new (...args: any[]) => T; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type ClassConstructor = new (...args: any[]) => T; - -export type ManifestTypeMap = { - [Manifest in ManifestTypes as Manifest['type']]: Manifest; -} & { - [key: string]: ManifestBase; -}; - -export type SpecificManifestTypeOrManifestBase< - ManifestTypes extends ManifestBase, - T extends keyof ManifestTypeMap | string -> = T extends keyof ManifestTypeMap ? ManifestTypeMap[T] : ManifestBase; - -export interface ManifestBase { - /** - * The type of extension such as dashboard etc... - */ - type: string; - - /** - * The alias of the extension, ensure it is unique - */ - alias: string; - - /** - * The kind of the extension, used to group extensions together - * - * @examples ["button"] - */ - kind?: unknown; // I had to add the optional kind property set to undefined. To make the ManifestTypes recognize the Manifest Kind types. Notice that Kinds has to Omit the kind property when extending. - - /** - * The friendly name of the extension - */ - name: string; - - /** - * Extensions such as dashboards are ordered by weight with lower numbers being first in the list - */ - weight?: number; -} - -export interface ManifestKind { - type: 'kind'; - alias: string; - matchType: string; - matchKind: string; - manifest: Partial; -} - -// TODO: Get rid of this type and implements ManifestWithDynamicConditions instead. -export interface ManifestWithConditions { - /** - * Set the conditions for when the extension should be loaded - */ - conditions: ConditionType; -} - -export interface UmbConditionConfigBase { - alias: AliasType; -} - -export type ConditionTypeMap = { - [Condition in ConditionTypes as Condition['alias']]: Condition; -} & { - [key: string]: UmbConditionConfigBase; -}; - -export type SpecificConditionTypeOrUmbConditionConfigBase< - ConditionTypes extends UmbConditionConfigBase, - T extends keyof ConditionTypeMap | string -> = T extends keyof ConditionTypeMap ? ConditionTypeMap[T] : UmbConditionConfigBase; - -export interface ManifestWithDynamicConditions - extends ManifestBase { - /** - * Set the conditions for when the extension should be loaded - */ - conditions?: Array; - /** - * Define one or more extension aliases that this extension should overwrite. - */ - overwrites?: string | Array; -} - -export interface ManifestWithLoader extends ManifestBase { - /** - * @TJS-ignore - */ - loader?(): Promise; -} - -// TODO: Rename this to something more descriptive: -export interface UmbApi { - destroy?(): void; -} - -/** - * The type of extension such as dashboard etc... - */ -export interface ManifestApi - extends ManifestWithLoader<{ default: ClassConstructor } | { api: ClassConstructor }> { - /** - * @TJS-ignore - */ - readonly API_TYPE?: ApiType; - - /** - * The file location of the javascript file to load - * @TJS-required - */ - js?: string; - - /** - * @TJS-ignore - */ - apiName?: string; - - /** - * @TJS-ignore - */ - api?: ClassConstructor; -} - -export interface ManifestWithLoaderIncludingDefaultExport - extends ManifestWithLoader<{ default: T } | { element: T } | Omit> { - /** - * The file location of the javascript file to load - */ - js?: string; -} - -export interface ManifestWithLoaderIncludingApiExport - extends ManifestWithLoader<{ api: ApiType } | Omit> { - /** - * The file location of the javascript file to load - */ - js?: string; - /** - * The file location of the API javascript file to load - */ - apiJs?: string; -} - -export interface ManifestWithLoaderIncludingElementExport - extends ManifestWithLoader<{ element: ElementType } | Omit> { - /** - * The file location of the javascript file to load - */ - js?: string; - /** - * The file location of the element javascript file to load - */ - elementJs?: string; -} -export interface ManifestWithLoaderOptionalApiOrElementExport< - ElementType extends HTMLElement = HTMLElement, - ApiType extends UmbApi = UmbApi, - ClassType = { element: ElementType } | { api: ApiType } | { element: ElementType, api: ApiType } | Omit, 'api'> -> -extends ManifestWithLoader { - /** - * The file location of the javascript file to load - */ - js?: string; - /** - * The file location of the element javascript file to load - */ - elementJs?: string; - /** - * The file location of the API javascript file to load - */ - apiJs?: string; -} - -export interface ManifestElement - extends ManifestWithLoader<{ default: ClassConstructor } | Omit> { - /** - * @TJS-ignore - */ - readonly ELEMENT_TYPE?: ElementType; - - /** - * The file location of the javascript file to load - * - * @TJS-require - */ - js?: string; - - /** - * The HTML web component name to use such as 'my-dashboard' - * Note it is NOT , just the element name. - */ - elementName?: string; - - /** - * This contains properties specific to the type of extension - */ - meta?: unknown; -} - -export interface ManifestElementAndApi - extends ManifestWithLoaderOptionalApiOrElementExport { - /** - * @TJS-ignore - */ - readonly API_TYPE?: ApiType; - /** - * @TJS-ignore - */ - readonly ELEMENT_TYPE?: ElementType; - - - /** - * @TJS-ignore - */ - apiName?: string; - - /** - * @TJS-ignore - */ - api?: ClassConstructor; - - /** - * The HTML web component name to use such as 'my-dashboard' - * Note it is NOT , just the element name. - */ - elementName?: string; -} - -export interface ManifestWithView extends ManifestElement { - meta: MetaManifestWithView; -} - -export interface MetaManifestWithView { - pathname: string; - label: string; - icon: string; -} - -export interface ManifestElementWithElementName extends ManifestElement { - /** - * The HTML web component name to use such as 'my-dashboard' - * Note it is NOT but just the name - */ - elementName: string; -} - -export interface ManifestWithMeta extends ManifestBase { - /** - * This contains properties specific to the type of extension - */ - meta: unknown; -} - -/** - * This type of extension gives full control and will simply load the specified JS file - * You could have custom logic to decide which extensions to load/register by using extensionRegistry - */ -export interface ManifestEntryPoint extends ManifestWithLoader { - type: 'entryPoint'; - - /** - * The file location of the javascript file to load in the backoffice - */ - js?: string; -} - -/** - * This type of extension takes a JS module and registers all exported manifests from the pointed JS file. - */ -export interface ManifestBundle - extends ManifestWithLoader<{ [key: string]: Array }> { - type: 'bundle'; - - /** - * The file location of the javascript file to load in the backoffice - */ - js?: string; -} - -/** - * This type of extension takes a JS module and registers all exported manifests from the pointed JS file. - */ -export interface ManifestCondition extends ManifestApi { - type: 'condition'; - - /** - * The file location of the javascript file to load in the backoffice - */ - js?: string; -} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/base.types.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/base.types.ts new file mode 100644 index 0000000000..e17bc203fc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/base.types.ts @@ -0,0 +1,130 @@ +import type { UmbApi } from '../models/index.js'; +import type { ManifestBase } from './manifest-base.interface.js'; +import type { + ApiLoaderProperty, + CssLoaderProperty, + ElementAndApiLoaderProperty, + ElementLoaderProperty, + JsLoaderProperty, +} from './utils.js'; + +export interface ManifestWithView extends ManifestElement { + meta: MetaManifestWithView; +} + +export interface MetaManifestWithView { + pathname: string; + label: string; + icon: string; +} + +export interface ManifestElementWithElementName extends ManifestElement { + /** + * The HTML web component name to use such as 'my-dashboard' + * Note it is NOT but just the name + */ + elementName: string; +} + +export interface ManifestPlainCss extends ManifestBase { + /** + * The file location of the stylesheet file to load + * @TJS-type string + */ + css?: CssLoaderProperty; +} + +export interface ManifestPlainJs extends ManifestBase { + /** + * The file location of the javascript file to load + * @TJS-type string + */ + js?: JsLoaderProperty; +} + +/** + * The type of extension such as dashboard etc... + */ +export interface ManifestApi extends ManifestBase { + /** + * @TJS-ignore + */ + readonly API_TYPE?: ApiType; + + /** + * The file location of the javascript file to load + * @TJS-type string + */ + js?: ApiLoaderProperty; + + /** + * @TJS-type string + */ + api?: ApiLoaderProperty; +} + +export interface ManifestElement extends ManifestBase { + /** + * @TJS-ignore + */ + readonly ELEMENT_TYPE?: ElementType; + + /** + * The file location of the javascript file to load + * @TJS-type string + */ + js?: ElementLoaderProperty; + + /** + * The file location of the element javascript file to load + * @TJS-type string + */ + element?: ElementLoaderProperty; + + /** + * The HTML web component name to use such as 'my-dashboard' + * Note it is NOT , just the element name. + */ + elementName?: string; + + /** + * This contains properties specific to the type of extension + */ + meta?: unknown; +} + +export interface ManifestElementAndApi + extends ManifestBase { + /** + * @TJS-ignore + */ + readonly API_TYPE?: ApiType; + /** + * @TJS-ignore + */ + readonly ELEMENT_TYPE?: ElementType; + + /** + * The file location of the javascript file to load + * @TJS-type string + */ + js?: ElementAndApiLoaderProperty; + + /** + * The file location of the api javascript file to load + * @TJS-type string + */ + api?: ApiLoaderProperty; + + /** + * The file location of the element javascript file to load + * @TJS-type string + */ + element?: ElementLoaderProperty; + + /** + * The HTML web component name to use such as 'my-dashboard' + * Note it is NOT , just the element name. + */ + elementName?: string; +} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/condition.types.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/condition.types.ts new file mode 100644 index 0000000000..a09b527510 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/condition.types.ts @@ -0,0 +1,28 @@ +import type { ManifestBase } from "./manifest-base.interface.js"; + +export interface UmbConditionConfigBase { + alias: AliasType; +} + +export type ConditionTypeMap = { + [Condition in ConditionTypes as Condition['alias']]: Condition; +} & { + [key: string]: UmbConditionConfigBase; +}; + +export type SpecificConditionTypeOrUmbConditionConfigBase< + ConditionTypes extends UmbConditionConfigBase, + T extends keyof ConditionTypeMap | string +> = T extends keyof ConditionTypeMap ? ConditionTypeMap[T] : UmbConditionConfigBase; + +export interface ManifestWithDynamicConditions + extends ManifestBase { + /** + * Set the conditions for when the extension should be loaded + */ + conditions?: Array; + /** + * Define one or more extension aliases that this extension should overwrite. + */ + overwrites?: string | Array; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/index.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/index.ts new file mode 100644 index 0000000000..14b601b5ce --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/index.ts @@ -0,0 +1,8 @@ +export * from './base.types.js'; +export * from './condition.types.js'; +export * from './manifest-base.interface.js'; +export * from './manifest-bundle.interface.js'; +export * from './manifest-condition.interface.js'; +export * from './manifest-entrypoint.interface.js'; +export * from './manifest-kind.interface.js'; +export * from './utils.js'; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-base.interface.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-base.interface.ts new file mode 100644 index 0000000000..8a89595272 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-base.interface.ts @@ -0,0 +1,29 @@ + +export interface ManifestBase { + /** + * The type of extension such as dashboard etc... + */ + type: string; + + /** + * The alias of the extension, ensure it is unique + */ + alias: string; + + /** + * The kind of the extension, used to group extensions together + * + * @examples ["button"] + */ + kind?: unknown; // I had to add the optional kind property set to undefined. To make the ManifestTypes recognize the Manifest Kind types. Notice that Kinds has to Omit the kind property when extending. + + /** + * The friendly name of the extension + */ + name: string; + + /** + * Extensions such as dashboards are ordered by weight with lower numbers being first in the list + */ + weight?: number; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-bundle.interface.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-bundle.interface.ts new file mode 100644 index 0000000000..25a99091bc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-bundle.interface.ts @@ -0,0 +1,11 @@ +import type { ManifestPlainJs } from "./base.types.js"; +import type { ManifestBase } from "./manifest-base.interface.js"; + +/** + * This type of extension takes a JS module and registers all exported manifests from the pointed JS file. + */ +export interface ManifestBundle + extends ManifestPlainJs<{ [key: string]: Array }> { + type: 'bundle'; +} + diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-condition.interface.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-condition.interface.ts new file mode 100644 index 0000000000..324af0a7b4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-condition.interface.ts @@ -0,0 +1,9 @@ +import type { UmbExtensionCondition } from "../condition/index.js"; +import type { ManifestApi } from "./base.types.js"; + +/** + * This type of extension takes a JS module and registers all exported manifests from the pointed JS file. + */ +export interface ManifestCondition extends ManifestApi { + type: 'condition'; +} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-entrypoint.interface.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-entrypoint.interface.ts new file mode 100644 index 0000000000..76764b321c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-entrypoint.interface.ts @@ -0,0 +1,10 @@ +import type { UmbEntryPointModule } from "../models/index.js"; +import type { ManifestPlainJs } from "./base.types.js"; + +/** + * This type of extension gives full control and will simply load the specified JS file + * You could have custom logic to decide which extensions to load/register by using extensionRegistry + */ +export interface ManifestEntryPoint extends ManifestPlainJs { + type: 'entryPoint'; +} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-kind.interface.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-kind.interface.ts new file mode 100644 index 0000000000..b95853c77c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-kind.interface.ts @@ -0,0 +1,17 @@ +export interface ManifestKind { + type: 'kind'; + alias: string; + matchType: string; + matchKind: string; + /** + * Provide pre defined properties for the extension manifest. + * Define the `type`-property and other properties you like to preset for implementations of this kind. + * + * @example { + * type: 'section', + * weight: 123, + * } + * @TJS-type object + */ + manifest: Partial; +} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/map.types.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/map.types.ts new file mode 100644 index 0000000000..4c5fbb3b43 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/map.types.ts @@ -0,0 +1,12 @@ +import type { ManifestBase } from "./manifest-base.interface.js"; + +export type ManifestTypeMap = { + [Manifest in ManifestTypes as Manifest['type']]: Manifest; +} & { + [key: string]: ManifestBase; +}; + +export type SpecificManifestTypeOrManifestBase< + ManifestTypes extends ManifestBase, + T extends keyof ManifestTypeMap | string +> = T extends keyof ManifestTypeMap ? ManifestTypeMap[T] : ManifestBase; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/utils.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/utils.ts new file mode 100644 index 0000000000..9cf7996dd3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/utils.ts @@ -0,0 +1,86 @@ +import type { UmbApi } from "../models/index.js"; + + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type HTMLElementConstructor = new (...args: any[]) => T; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type ClassConstructor = new (...args: any[]) => T; + + + +// Module Export Types: + +export type ElementLoaderExports< + ElementType extends HTMLElement = HTMLElement +> = ({default: ClassConstructor} | {element: ClassConstructor})// | Omit, 'default'> + +export type ApiLoaderExports< + ApiType extends UmbApi = UmbApi +> = ({default: ClassConstructor} | {api: ClassConstructor})//| Omit, 'default'> + +export type ElementAndApiLoaderExports< + ElementType extends HTMLElement = HTMLElement, + ApiType extends UmbApi = UmbApi +> = ({api: ClassConstructor} | {element: ClassConstructor} | {api: ClassConstructor, element: ClassConstructor})// | Omit, 'api'>, 'default'> + + +// Promise Types: + +export type CssLoaderPromise< + CssType = unknown +> = (() => Promise) + +export type JsLoaderPromise< + JsType +> = (() => Promise) + +export type ElementLoaderPromise< + ElementType extends HTMLElement = HTMLElement +> = (() => Promise>) + +export type ApiLoaderPromise< + ApiType extends UmbApi = UmbApi +> = (() => Promise>) + +export type ElementAndApiLoaderPromise< + ElementType extends HTMLElement = HTMLElement, + ApiType extends UmbApi = UmbApi +> = (() => Promise>) + + +// Property Types: + +export type CssLoaderProperty = ( + string + | + CssLoaderPromise +); +export type JsLoaderProperty = ( + string + | + JsLoaderPromise +); +export type ElementLoaderProperty = ( + string + | + ElementLoaderPromise + | + ClassConstructor +); +export type ApiLoaderProperty = ( + string + | + ApiLoaderPromise + | + ClassConstructor +); +export type ElementAndApiLoaderProperty = ( + string + | + ElementAndApiLoaderPromise + | + ElementLoaderPromise + | + ApiLoaderPromise +); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/libs/observable-api/observer.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/observable-api/observer.controller.ts index 9d812b191f..0ea15ab2e5 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/observable-api/observer.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/observable-api/observer.controller.ts @@ -14,7 +14,7 @@ export class UmbObserverController extends UmbObserver implement host: UmbControllerHost, source: Observable, callback: ObserverCallback, - alias?: UmbControllerAlias + alias?: UmbControllerAlias, ) { super(source, callback); this.#host = host; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/collection/collection.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/collection/collection.context.ts index c00a093f80..14dd632904 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/collection/collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/collection/collection.context.ts @@ -7,10 +7,9 @@ import { UmbNumberState, UmbObjectState, } from '@umbraco-cms/backoffice/observable-api'; -import { createExtensionApi } from '@umbraco-cms/backoffice/extension-api'; +import { UmbExtensionsManifestInitializer, createExtensionApi } from '@umbraco-cms/backoffice/extension-api'; import { ManifestCollectionView, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbCollectionFilterModel } from '@umbraco-cms/backoffice/collection'; -import { map } from '@umbraco-cms/backoffice/external/rxjs'; import { UmbSelectionManager, UmbPaginationManager } from '@umbraco-cms/backoffice/utils'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; @@ -193,15 +192,10 @@ export class UmbCollectionContext { - return extensions.filter((extension) => extension.conditions.entityType === this.getEntityType()); - }), - ), - (views) => { - this.#views.next(views); + return new UmbExtensionsManifestInitializer(this, umbExtensionsRegistry, 'collectionView', null, (views) => { + this.#views.next(views.map(view => view.manifest)); this.#setCurrentView(); - }, 'umbCollectionViewsObserver'); + }); } #onPageChange = (event: UmbChangeEvent) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/extension-slot/extension-slot.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/extension-slot/extension-slot.element.ts index 27411d6b7a..0e3ddae1c8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/extension-slot/extension-slot.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/extension-slot/extension-slot.element.ts @@ -2,7 +2,7 @@ import { type ManifestTypes, umbExtensionsRegistry } from '../../extension-regis import { css, repeat, customElement, property, state, TemplateResult } from '@umbraco-cms/backoffice/external/lit'; import { type UmbExtensionElementInitializer, - UmbExtensionsElementController, + UmbExtensionsElementInitializer, } from '@umbraco-cms/backoffice/extension-api'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -20,7 +20,7 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-extension-slot') export class UmbExtensionSlotElement extends UmbLitElement { #attached = false; - #extensionsController?: UmbExtensionsElementController; + #extensionsController?: UmbExtensionsElementInitializer; @state() private _permittedExts: Array = []; @@ -106,7 +106,7 @@ export class UmbExtensionSlotElement extends UmbLitElement { private _observeExtensions() { this.#extensionsController?.destroy(); if (this.#type) { - this.#extensionsController = new UmbExtensionsElementController( + this.#extensionsController = new UmbExtensionsElementInitializer( this, umbExtensionsRegistry, this.#type, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-tiny-mce/input-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-tiny-mce/input-tiny-mce.element.ts index d167d400c5..baa6f00a5e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-tiny-mce/input-tiny-mce.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-tiny-mce/input-tiny-mce.element.ts @@ -11,7 +11,7 @@ import { } from '@umbraco-cms/backoffice/external/tinymce'; import { UMB_CURRENT_USER_CONTEXT, UmbCurrentUser } from '@umbraco-cms/backoffice/current-user'; import { TinyMcePluginArguments, UmbTinyMcePluginBase } from '@umbraco-cms/backoffice/components'; -import { ClassConstructor, hasDefaultExport, loadExtension } from '@umbraco-cms/backoffice/extension-api'; +import { ClassConstructor, hasDefaultExport, loadManifestApi } from '@umbraco-cms/backoffice/extension-api'; import { ManifestTinyMcePlugin, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { PropertyValueMap, @@ -99,12 +99,12 @@ export class UmbInputTinyMceElement extends FormControlMixin(UmbLitElement) { */ async #loadPlugins() { const observable = umbExtensionsRegistry?.extensionsOfType('tinyMcePlugin'); - const plugins = (await firstValueFrom(observable)) as ManifestTinyMcePlugin[]; + const manifests = (await firstValueFrom(observable)) as ManifestTinyMcePlugin[]; - for (const plugin of plugins) { - const module = await loadExtension(plugin); - if (hasDefaultExport>(module)) { - this.#plugins.push(module.default); + for (const manifest of manifests) { + const plugin = manifest.js ? await loadManifestApi(manifest.js) : manifest.api ? await loadManifestApi(manifest.api) : undefined; + if (plugin) { + this.#plugins.push(plugin); } } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/data-type/components/data-type-flow-input/data-type-flow-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/data-type/components/data-type-flow-input/data-type-flow-input.element.ts index 872d015157..a158eeea71 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/data-type/components/data-type-flow-input/data-type-flow-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/data-type/components/data-type-flow-input/data-type-flow-input.element.ts @@ -28,7 +28,7 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) { * @param {string} dataTypeId * @default [] */ - @property({ attribute: false }) + @property({ type: String, attribute: false }) get value(): string { return super.value?.toString() ?? ''; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/data-type/entity-actions/create/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/data-type/entity-actions/create/manifests.ts index 7a23b7239a..c54c3af8df 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/data-type/entity-actions/create/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/data-type/entity-actions/create/manifests.ts @@ -21,7 +21,7 @@ const entityActions: Array = [ type: 'modal', alias: 'Umb.Modal.DataTypeCreateOptions', name: 'Data Type Create Options Modal', - loader: () => import('./modal/data-type-create-options-modal.element.js'), + js: () => import('./modal/data-type-create-options-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/data-type/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/data-type/modals/manifests.ts index 7abab6de4a..d612586eaa 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/data-type/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/data-type/modals/manifests.ts @@ -5,19 +5,19 @@ const modals: Array = [ type: 'modal', alias: 'Umb.Modal.PropertyEditorUiPicker', name: 'Property Editor UI Picker Modal', - loader: () => import('./property-editor-ui-picker/property-editor-ui-picker-modal.element.js'), + js: () => import('./property-editor-ui-picker/property-editor-ui-picker-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.DataTypePickerFlow', name: 'Data Type Picker Flow Modal', - loader: () => import('./data-type-picker-flow/data-type-picker-flow-modal.element.js'), + js: () => import('./data-type-picker-flow/data-type-picker-flow-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.DataTypePickerFlowDataTypePicker', name: 'Data Type Picker Flow UI Picker Modal', - loader: () => import('./data-type-picker-flow/data-type-picker-flow-data-type-picker-modal.element.js'), + js: () => import('./data-type-picker-flow/data-type-picker-flow-data-type-picker-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/data-type/workspace/data-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/data-type/workspace/data-type-workspace.context.ts index cb84c569bd..7e249b245e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/data-type/workspace/data-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/data-type/workspace/data-type-workspace.context.ts @@ -2,7 +2,7 @@ import { UmbDataTypeDetailRepository } from '../repository/detail/data-type-deta import { UmbDataTypeVariantContext } from '../variant-context/data-type-variant-context.js'; import { UmbInvariantableWorkspaceContextInterface, - UmbWorkspaceContext, + UmbEditableWorkspaceContextBase, UmbWorkspaceContextInterface, } from '@umbraco-cms/backoffice/workspace'; import type { DataTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; @@ -23,7 +23,7 @@ import { import { UMB_PROPERTY_EDITOR_SCHEMA_ALIAS_DEFAULT } from '@umbraco-cms/backoffice/property-editor'; export class UmbDataTypeWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbInvariantableWorkspaceContextInterface { // TODO: revisit. temp solution because the create and response models are different. diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/data-type/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/data-type/workspace/manifests.ts index 738944b871..95535d4ec1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/data-type/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/data-type/workspace/manifests.ts @@ -11,7 +11,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: DATA_TYPE_WORKSPACE_ALIAS, name: 'Data Type Workspace', - loader: () => import('./data-type-workspace.element.js'), + js: () => import('./data-type-workspace.element.js'), meta: { entityType: 'data-type', }, @@ -22,7 +22,7 @@ const workspaceViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.DataType.Edit', name: 'Data Type Workspace Edit View', - loader: () => import('./views/details/data-type-details-workspace-view.element.js'), + js: () => import('./views/details/data-type-details-workspace-view.element.js'), weight: 90, meta: { label: 'Details', @@ -40,7 +40,7 @@ const workspaceViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.DataType.Info', name: 'Data Type Workspace Info View', - loader: () => import('./views/info/workspace-view-data-type-info.element.js'), + js: () => import('./views/info/workspace-view-data-type-info.element.js'), weight: 90, meta: { label: 'Info', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/debug/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/debug/manifests.ts index f1f7b08c1c..ff35dfe66d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/debug/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/debug/manifests.ts @@ -5,7 +5,7 @@ const modals: Array = [ type: 'modal', alias: 'Umb.Modal.ContextDebugger', name: 'Context Debugger Modal', - loader: () => import('./modals/debug/debug-modal.element.js'), + js: () => import('./modals/debug/debug-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/entity-bulk-action.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/entity-bulk-action.element.ts index 406e75ac84..855a95bb9d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/entity-bulk-action.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/entity-bulk-action.element.ts @@ -1,4 +1,4 @@ -import { UmbEntityBulkAction, UmbEntityBulkActionBase } from './entity-bulk-action.js'; +import { UmbEntityBulkActionBase } from './entity-bulk-action.js'; import { UmbActionExecutedEvent } from '@umbraco-cms/backoffice/event'; import { html, ifDefined, customElement, property } from '@umbraco-cms/backoffice/external/lit'; import { ManifestEntityBulkAction } from '@umbraco-cms/backoffice/extension-registry'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/entity-bulk-action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/entity-bulk-action.ts index d49529a25f..e723c079fe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/entity-bulk-action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/entity-bulk-action.ts @@ -1,12 +1,15 @@ import { UmbAction, UmbActionBase } from '@umbraco-cms/backoffice/action'; -import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; export interface UmbEntityBulkAction extends UmbAction { selection: Array; setSelection(selection: Array): void; } -export abstract class UmbEntityBulkActionBase extends UmbActionBase implements UmbEntityBulkAction { +export abstract class UmbEntityBulkActionBase + extends UmbActionBase + implements UmbEntityBulkAction +{ selection: Array; constructor(host: UmbControllerHostElement, repositoryAlias: string, selection: Array) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/conditions/section-alias.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/conditions/section-alias.condition.ts index a057ad751a..036f4dbe5a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/conditions/section-alias.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/conditions/section-alias.condition.ts @@ -29,8 +29,7 @@ export type SectionAliasConditionConfig = UmbConditionConfigBase<'Umb.Condition. /** * Define the section that this extension should be available in * - * @example - * "Umb.Section.Content" + * @example "Umb.Section.Content" */ match: string; }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/property-editor-ui-element.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/property-editor-ui-element.interface.ts index a4f63ffbb2..8043f4d9b6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/property-editor-ui-element.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/property-editor-ui-element.interface.ts @@ -1,6 +1,6 @@ import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; export interface UmbPropertyEditorUiElement extends HTMLElement { - value: unknown; + value?: unknown; config?: UmbPropertyEditorConfigCollection; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/collection-view.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/collection-view.model.ts index eb30c86e59..6e7643e4e9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/collection-view.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/collection-view.model.ts @@ -1,6 +1,7 @@ -import type { ManifestElement, ManifestWithConditions } from '@umbraco-cms/backoffice/extension-api'; +import type { ConditionTypes } from '../conditions/types.js'; +import type { ManifestElement, ManifestWithDynamicConditions } from '@umbraco-cms/backoffice/extension-api'; -export interface ManifestCollectionView extends ManifestElement, ManifestWithConditions { +export interface ManifestCollectionView extends ManifestElement, ManifestWithDynamicConditions { type: 'collectionView'; meta: MetaCollectionView; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts index 65ba8fdb27..467f5c92c1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts @@ -26,6 +26,7 @@ import type { ManifestTreeItem } from './tree-item.model.js'; import type { ManifestUserProfileApp } from './user-profile-app.model.js'; import type { ManifestWorkspace } from './workspace.model.js'; import type { ManifestWorkspaceAction } from './workspace-action.model.js'; +import type { ManifestWorkspaceContext } from './workspace-context.model.js'; import type { ManifestWorkspaceEditorView } from './workspace-editor-view.model.js'; import type { ManifestWorkspaceViewCollection } from './workspace-view-collection.model.js'; import type { ManifestUserPermission } from './user-permission.model.js'; @@ -46,6 +47,7 @@ export * from './external-login-provider.model.js'; export * from './global-context.model.js'; export * from './header-app.model.js'; export * from './health-check.model.js'; +export * from './localization.model.js'; export * from './menu-item.model.js'; export * from './menu.model.js'; export * from './modal.model.js'; @@ -59,16 +61,16 @@ export * from './section.model.js'; export * from './store.model.js'; export * from './theme.model.js'; export * from './tinymce-plugin.model.js'; -export * from './localization.model.js'; export * from './tree-item.model.js'; export * from './tree.model.js'; +export * from './user-granular-permission.model.js'; +export * from './user-permission.model.js'; export * from './user-profile-app.model.js'; export * from './workspace-action.model.js'; -export * from './workspace-view-collection.model.js'; +export * from './workspace-context.model.js'; export * from './workspace-editor-view.model.js'; +export * from './workspace-view-collection.model.js'; export * from './workspace.model.js'; -export * from './user-permission.model.js'; -export * from './user-granular-permission.model.js'; export type ManifestTypes = | ManifestBundle @@ -108,6 +110,7 @@ export type ManifestTypes = | ManifestUserProfileApp | ManifestWorkspace | ManifestWorkspaceAction + | ManifestWorkspaceContext | ManifestWorkspaceEditorView | ManifestWorkspaceViewCollection | ManifestUserPermission diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/localization.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/localization.model.ts index f1ca9eec56..c51b4f7c7d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/localization.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/localization.model.ts @@ -1,7 +1,7 @@ -import type { ManifestWithLoaderIncludingDefaultExport } from '@umbraco-cms/backoffice/extension-api'; +import type { ManifestPlainJs } from '@umbraco-cms/backoffice/extension-api'; import type { UmbLocalizationDictionary } from '@umbraco-cms/backoffice/localization-api'; -export interface ManifestLocalization extends ManifestWithLoaderIncludingDefaultExport { +export interface ManifestLocalization extends ManifestPlainJs<{default: UmbLocalizationDictionary}> { type: 'localization'; meta: MetaLocalization; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/modal.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/modal.model.ts index fde2db540e..b3826268f0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/modal.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/modal.model.ts @@ -1,5 +1,6 @@ +import type { UmbModalExtensionElement } from '../interfaces/modal-extension-element.interface.js'; import type { ManifestElement } from '@umbraco-cms/backoffice/extension-api'; -export interface ManifestModal extends ManifestElement { +export interface ManifestModal extends ManifestElement | UmbModalExtensionElement> { type: 'modal'; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/property-action.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/property-action.model.ts index b967004bb1..1f5b6c9327 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/property-action.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/property-action.model.ts @@ -1,7 +1,7 @@ import type { ConditionTypes } from '../conditions/types.js'; import type { ManifestElement, ManifestWithDynamicConditions } from '@umbraco-cms/backoffice/extension-api'; -export interface ManifestPropertyAction extends ManifestElement, ManifestWithDynamicConditions { +export interface ManifestPropertyAction extends ManifestElement, ManifestWithDynamicConditions { type: 'propertyAction'; meta: MetaPropertyAction; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/property-editor.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/property-editor.model.ts index 8074f6b027..574ec02770 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/property-editor.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/property-editor.model.ts @@ -14,21 +14,13 @@ export interface MetaPropertyEditorUi { * The group that this property editor UI belongs to, which will be used to group the property editor UIs in the property editor picker. * If not specified, the property editor UI will be grouped under "Common". * @default "Common" - * @examples [ - * "Common", - * "Content", - * "Media" - * ] + * @examples ["Common", "Content", "Media"] */ group: string; /** * The alias of the property editor schema that this property editor UI is for. * If not specified, the property editor UI can only be used to configure other property editors. - * @examples [ - * "Umbraco.TextBox", - * "Umbraco.TextArea", - * "Umbraco.Label", - * ] + * @examples ["Umbraco.TextBox", "Umbraco.TextArea", "Umbraco.Label"] */ propertyEditorSchemaAlias?: string; settings?: PropertyEditorSettings; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/theme.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/theme.model.ts index ee728f893a..37cd90d2a9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/theme.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/theme.model.ts @@ -1,17 +1,7 @@ -import type { ManifestWithLoader } from '@umbraco-cms/backoffice/extension-api'; - -// TODO: make or find type for JS Module with default export: Would be nice to support css file directly. - +import type { ManifestPlainCss } from '@umbraco-cms/backoffice/extension-api'; /** * Theme manifest for styling the backoffice of Umbraco such as dark, high contrast etc */ -export interface ManifestTheme extends ManifestWithLoader { +export interface ManifestTheme extends ManifestPlainCss { type: 'theme'; - - /** - * File location of the CSS file of the theme - * - * @examples ["themes/dark.theme.css"] - */ - css?: string; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tinymce-plugin.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tinymce-plugin.model.ts index 41cbe79037..6e8688f920 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tinymce-plugin.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tinymce-plugin.model.ts @@ -1,4 +1,5 @@ -import { ManifestApi } from '@umbraco-cms/backoffice/extension-api'; +import type { UmbTinyMcePluginBase } from '@umbraco-cms/backoffice/components'; +import type { ManifestApi } from '@umbraco-cms/backoffice/extension-api'; export interface MetaTinyMcePlugin { /** @@ -36,7 +37,7 @@ export interface MetaTinyMcePlugin { * * @see [TinyMCE Plugin](https://www.tiny.cloud/docs/tinymce/6/apis/tinymce.plugin/) for more information. */ -export interface ManifestTinyMcePlugin extends ManifestApi { +export interface ManifestTinyMcePlugin extends ManifestApi { type: 'tinyMcePlugin'; meta?: MetaTinyMcePlugin; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace-context.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace-context.model.ts new file mode 100644 index 0000000000..d4b2a7111a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace-context.model.ts @@ -0,0 +1,6 @@ +import { ConditionTypes } from '../conditions/types.js'; +import type { ManifestApi, ManifestWithDynamicConditions, UmbApi } from '@umbraco-cms/backoffice/extension-api'; + +export interface ManifestWorkspaceContext extends ManifestWithDynamicConditions, ManifestApi { + type: 'workspaceContext'; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace.model.ts index 00264692a7..83cc8b9ac2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace.model.ts @@ -1,10 +1,11 @@ -import type { ManifestElement } from '@umbraco-cms/backoffice/extension-api'; +import type { ManifestElementAndApi, UmbApi } from '@umbraco-cms/backoffice/extension-api'; -export interface ManifestWorkspace extends ManifestElement { +// TODO: Missing Extension API Interface: +export interface ManifestWorkspace extends ManifestElementAndApi { type: 'workspace'; - meta: MetaEditor; + meta: MetaWorkspace; } -export interface MetaEditor { +export interface MetaWorkspace { entityType: string; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/localization/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/localization/manifests.ts index bbbab0b8fb..1b8eb631d7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/localization/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/localization/manifests.ts @@ -10,7 +10,7 @@ const localizationManifests: Array = [ meta: { culture: 'en-us', }, - loader: () => import('../../../assets/lang/en-us.js'), + js: () => import('../../../assets/lang/en-us.js'), }, { type: 'localization', @@ -20,7 +20,7 @@ const localizationManifests: Array = [ meta: { culture: 'da-dk', }, - loader: () => import('../../../assets/lang/da-dk.js'), + js: () => import('../../../assets/lang/da-dk.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/localization/registry/localization.registry.ts b/src/Umbraco.Web.UI.Client/src/packages/core/localization/registry/localization.registry.ts index 7b8b83bd24..7b2ece24f5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/localization/registry/localization.registry.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/localization/registry/localization.registry.ts @@ -5,7 +5,7 @@ import { registerLocalization, localizations, } from '@umbraco-cms/backoffice/localization-api'; -import { hasDefaultExport, loadExtension } from '@umbraco-cms/backoffice/extension-api'; +import { hasDefaultExport, loadManifestPlainJs } from '@umbraco-cms/backoffice/extension-api'; import { UmbBackofficeExtensionRegistry, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { BehaviorSubject, @@ -64,11 +64,13 @@ export class UmbLocalizationRegistry { } // If extension contains a js file, load it and add the default dictionary to the inner dictionary. - const loadedExtension = await loadExtension(extension); + if(extension.js) { + const loadedExtension = await loadManifestPlainJs(extension.js); - if (loadedExtension && hasDefaultExport(loadedExtension)) { - for (const [dictionaryName, dictionary] of Object.entries(loadedExtension.default)) { - this.#addOrUpdateDictionary(innerDictionary, dictionaryName, dictionary); + if (loadedExtension && hasDefaultExport(loadedExtension)) { + for (const [dictionaryName, dictionary] of Object.entries(loadedExtension.default)) { + this.#addOrUpdateDictionary(innerDictionary, dictionaryName, dictionary); + } } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.element.ts index 23543deef9..2a04dbe3e9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.element.ts @@ -5,10 +5,11 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-confirm-modal') export class UmbConfirmModalElement extends UmbLitElement { + @property({ attribute: false }) modalContext?: UmbModalContext; - @property({ type: Object }) + @property({ type: Object, attribute: false }) data?: UmbConfirmModalData; private _handleConfirm() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts index 6d7044fd52..033f818926 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts @@ -5,61 +5,61 @@ const modals: Array = [ type: 'modal', alias: 'Umb.Modal.Confirm', name: 'Confirm Modal', - loader: () => import('./confirm/confirm-modal.element.js'), + js: () => import('./confirm/confirm-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.Folder', name: 'Folder Modal', - loader: () => import('./folder/folder-modal.element.js'), + js: () => import('./folder/folder-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.IconPicker', name: 'Icon Picker Modal', - loader: () => import('./icon-picker/icon-picker-modal.element.js'), + js: () => import('./icon-picker/icon-picker-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.LinkPicker', name: 'Link Picker Modal', - loader: () => import('./link-picker/link-picker-modal.element.js'), + js: () => import('./link-picker/link-picker-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.PropertySettings', name: 'Property Settings Modal', - loader: () => import('./property-settings/property-settings-modal.element.js'), + js: () => import('./property-settings/property-settings-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.SectionPicker', name: 'Section Picker Modal', - loader: () => import('./section-picker/section-picker-modal.element.js'), + js: () => import('./section-picker/section-picker-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.Template', name: 'Template Modal', - loader: () => import('./template/template-modal.element.js'), + js: () => import('./template/template-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.CodeEditor', name: 'Code Editor Modal', - loader: () => import('./code-editor/code-editor-modal.element.js'), + js: () => import('./code-editor/code-editor-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.EmbeddedMedia', name: 'Embedded Media Modal', - loader: () => import('./embedded-media/embedded-media-modal.element.js'), + js: () => import('./embedded-media/embedded-media-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.TreePicker', name: 'Tree Picker Modal', - loader: () => import('./tree-picker/tree-picker-modal.element.js'), + js: () => import('./tree-picker/tree-picker-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/property-settings/property-settings-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/property-settings/property-settings-modal.element.ts index cefa74d5ee..a3d04cb499 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/property-settings/property-settings-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/property-settings/property-settings-modal.element.ts @@ -237,7 +237,7 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement< .value=${this._returnData.description}>
diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/modal-element.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/modal-element.element.ts index e544d55708..12c47a89c2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/modal-element.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/modal-element.element.ts @@ -11,7 +11,7 @@ export abstract class UmbModalBaseElement< extends UmbLitElement implements UmbModalExtensionElement { - @property({ type: Array, attribute: false }) + @property({ type: Object, attribute: false }) public manifest?: ModalManifestType; @property({ attribute: false }) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/modal.element.ts index 3b4a5e209a..5af2670e08 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/modal.element.ts @@ -104,7 +104,7 @@ export class UmbModalElement extends UmbLitElement { async #createInnerElement(manifest: ManifestModal) { // TODO: add inner fallback element if no extension element is found - const innerElement = (await createExtensionElement(manifest)) as any; + const innerElement = (await createExtensionElement(manifest)); if (innerElement) { innerElement.data = this.#modalContext!.data; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/common/clear/property-action-clear.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/common/clear/property-action-clear.element.ts index 77b667137e..3dde846b72 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/common/clear/property-action-clear.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/common/clear/property-action-clear.element.ts @@ -1,4 +1,4 @@ -import { UmbPropertyAction } from '../../shared/property-action/property-action.model.js'; +import type { UmbPropertyAction } from '../../shared/property-action/property-action.interface.js'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbWorkspacePropertyContext, UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/workspace'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/common/copy/property-action-copy.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/common/copy/property-action-copy.element.ts index f3b7011b46..a253d588ca 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/common/copy/property-action-copy.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/common/copy/property-action-copy.element.ts @@ -1,11 +1,11 @@ -import type { UmbPropertyAction } from '../../shared/property-action/property-action.model.js'; +import type { UmbPropertyAction } from '../../shared/property-action/property-action.interface.js'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbNotificationDefaultData, UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN, } from '@umbraco-cms/backoffice/notification'; -import { UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/workspace'; +//import { UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/workspace'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-property-action-copy') @@ -18,10 +18,10 @@ export class UmbPropertyActionCopyElement extends UmbLitElement implements UmbPr constructor() { super(); - this.consumeContext(UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN, (property) => { + //this.consumeContext(UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN, (property) => { //console.log('Got a reference to the editor element', property.getEditor()); // Be aware that the element might switch, so using the direct reference is not recommended, instead observe the .element Observable() - }); + //}); this.consumeContext(UMB_NOTIFICATION_CONTEXT_TOKEN, (instance) => { this._notificationContext = instance; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/manifests.ts index 4286bb2125..1d9096fd90 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/manifests.ts @@ -5,7 +5,7 @@ export const manifests: Array = [ type: 'propertyAction', alias: 'Umb.PropertyAction.Copy', name: 'Copy Property Action', - loader: () => import('./common/copy/property-action-copy.element.js'), + js: () => import('./common/copy/property-action-copy.element.js'), meta: { propertyEditors: ['Umb.PropertyEditorUi.TextBox'], }, @@ -14,7 +14,7 @@ export const manifests: Array = [ type: 'propertyAction', alias: 'Umb.PropertyAction.Clear', name: 'Clear Property Action', - loader: () => import('./common/clear/property-action-clear.element.js'), + js: () => import('./common/clear/property-action-clear.element.js'), meta: { propertyEditors: ['Umb.PropertyEditorUi.TextBox'], }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/index.ts index 96afbaf039..ae6d8e978f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/index.ts @@ -1,2 +1,2 @@ -export * from './property-action/property-action.element.js'; -export * from './property-action-menu/property-action-menu.element.js'; +export * from './property-action/index.js'; +export * from './property-action-menu/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action-menu/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action-menu/index.ts new file mode 100644 index 0000000000..ef20a285be --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action-menu/index.ts @@ -0,0 +1,2 @@ +export * from './property-action-menu.context.js' +export * from './property-action-menu.element.js' \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action-menu/property-action-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action-menu/property-action-menu.element.ts index e95f786867..940d0b8b32 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action-menu/property-action-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action-menu/property-action-menu.element.ts @@ -1,27 +1,38 @@ import { UmbPropertyActionMenuContext } from './property-action-menu.context.js'; -import { css, CSSResultGroup, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; -import { map } from '@umbraco-cms/backoffice/external/rxjs'; +import { css, CSSResultGroup, html, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; -import { ManifestPropertyAction, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { ManifestPropertyAction, ManifestTypes, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbExtensionElementInitializer, UmbExtensionsElementInitializer } from '@umbraco-cms/backoffice/extension-api'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; - -import '../property-action/property-action.element.js'; @customElement('umb-property-action-menu') export class UmbPropertyActionMenuElement extends UmbLitElement { + + #actionsInitializer?: UmbExtensionsElementInitializer; + + @property({ attribute: false }) - public value?: unknown; + public get value(): unknown { + return this._value; + } + public set value(value: unknown) { + this._value = value; + if(this.#actionsInitializer) { + this.#actionsInitializer.properties = { value }; + } + } + private _value?: unknown; @property() set propertyEditorUiAlias(alias: string) { - this._observeActions(alias); + + // TODO: Align property actions with entity actions. + this.#actionsInitializer = new UmbExtensionsElementInitializer(this, umbExtensionsRegistry, 'propertyAction', (propertyAction) => propertyAction.meta.propertyEditors.includes(alias), (ctrls) => { + this._actions = ctrls; + }); } - - private _actionsObserver?: UmbObserverController; - @state() - private _actions: Array = []; + private _actions: Array> = []; @state() private _open = false; @@ -31,8 +42,8 @@ export class UmbPropertyActionMenuElement extends UmbLitElement { constructor() { super(); - this.observe(this._propertyActionMenuContext.isOpen, (value) => { - this._open = value; + this.observe(this._propertyActionMenuContext.isOpen, (isOpen) => { + this._open = isOpen; }); this.addEventListener('close', (e) => { @@ -41,21 +52,6 @@ export class UmbPropertyActionMenuElement extends UmbLitElement { }); } - private _observeActions(alias: string) { - this._actionsObserver?.destroy(); - // TODO: Align property actions with entity actions. - this._actionsObserver = this.observe( - umbExtensionsRegistry.extensionsOfType('propertyAction').pipe( - map((propertyActions) => { - return propertyActions.filter((propertyAction) => propertyAction.meta.propertyEditors.includes(alias)); - }) - ), - (manifests) => { - this._actions = manifests; - } - ); - } - private _toggleMenu() { this._propertyActionMenuContext.toggle(); } @@ -65,7 +61,6 @@ export class UmbPropertyActionMenuElement extends UmbLitElement { event.stopPropagation(); } - // TODO: Implement extension-slot on change event. And use the extension slot instead of custom implementation. render() { return this._actions.length > 0 ? html` @@ -81,10 +76,8 @@ export class UmbPropertyActionMenuElement extends UmbLitElement { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action/index.ts new file mode 100644 index 0000000000..ec46a53aaa --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action/index.ts @@ -0,0 +1 @@ +export * from './property-action.interface.js'; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action/property-action.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action/property-action.element.ts deleted file mode 100644 index e15cff0b58..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action/property-action.element.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { UmbPropertyAction } from './property-action.model.js'; -import { CSSResultGroup, html, LitElement, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; -import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; -import { createExtensionElement } from '@umbraco-cms/backoffice/extension-api'; - -import type { ManifestPropertyAction } from '@umbraco-cms/backoffice/extension-registry'; - -// TODO: Here is a problem. The UmbPropertyActionElement is used for the type of the Extension Element. But is also a component that renders the Extension Element... -@customElement('umb-property-action') -export class UmbPropertyActionElement extends LitElement implements UmbPropertyAction { - private _propertyAction?: ManifestPropertyAction; - @property({ type: Object }) - public get propertyAction(): ManifestPropertyAction | undefined { - return this._propertyAction; - } - public set propertyAction(value: ManifestPropertyAction | undefined) { - this._propertyAction = value; - this._createElement(); - } - - // TODO: we need to investigate context api vs values props and events - @property({ attribute: false }) - public value?: unknown; - - @state() - private _element?: UmbPropertyActionElement; - - private async _createElement() { - if (!this.propertyAction) return; - - try { - // TODO: Here is a problem. The UmbPropertyActionElement is used for the type of the Extension Element. But is also a component that renders the Extension Element... - this._element = (await createExtensionElement(this.propertyAction)) as UmbPropertyActionElement | undefined; - if (!this._element) return; - - this._element.value = this.value; - } catch (error) { - // TODO: loading JS failed so we should do some nice UI. (This does only happen if extension has a js prop, otherwise we concluded that no source was needed resolved the load.) - } - } - - render() { - return html`${this._element}`; - } - - static styles: CSSResultGroup = [UmbTextStyles]; -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action/property-action.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action/property-action.interface.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action/property-action.model.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/property-action/shared/property-action/property-action.interface.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/block-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/block-configuration/manifests.ts index 7da7aba7b7..a637ea4ba1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/block-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/block-configuration/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.BlockGrid.BlockConfiguration', name: 'Block Grid Block Configuration Property Editor UI', - loader: () => import('./property-editor-ui-block-grid-block-configuration.element.js'), + js: () => import('./property-editor-ui-block-grid-block-configuration.element.js'), meta: { label: 'Block Grid Block Configuration', propertyEditorSchemaAlias: 'Umbraco.BlockGrid.BlockConfiguration', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/block-configuration/property-editor-ui-block-grid-block-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/block-configuration/property-editor-ui-block-grid-block-configuration.element.ts index 70e019d388..3d23986281 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/block-configuration/property-editor-ui-block-grid-block-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/block-configuration/property-editor-ui-block-grid-block-configuration.element.ts @@ -1,4 +1,6 @@ +import { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -6,12 +8,12 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; * @element umb-property-editor-ui-block-grid-block-configuration */ @customElement('umb-property-editor-ui-block-grid-block-configuration') -export class UmbPropertyEditorUIBlockGridBlockConfigurationElement extends UmbLitElement { +export class UmbPropertyEditorUIBlockGridBlockConfigurationElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property() value = ''; - @property({ type: Array, attribute: false }) - public config = []; + @property({ type: Object, attribute: false }) + public config?: UmbPropertyEditorConfigCollection; render() { return html`
umb-property-editor-ui-block-grid-block-configuration
`; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/group-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/group-configuration/manifests.ts index 13eb55ca48..67ddcf4f8a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/group-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/group-configuration/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.BlockGrid.GroupConfiguration', name: 'Block Grid Group Configuration Property Editor UI', - loader: () => import('./property-editor-ui-block-grid-group-configuration.element.js'), + js: () => import('./property-editor-ui-block-grid-group-configuration.element.js'), meta: { label: 'Block Grid Group Configuration', propertyEditorSchemaAlias: 'Umbraco.BlockGrid.GroupConfiguration', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/group-configuration/property-editor-ui-block-grid-group-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/group-configuration/property-editor-ui-block-grid-group-configuration.element.ts index 63576d4ffc..3be87591bb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/group-configuration/property-editor-ui-block-grid-group-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/group-configuration/property-editor-ui-block-grid-group-configuration.element.ts @@ -1,4 +1,6 @@ +import { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -6,12 +8,12 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; * @element umb-property-editor-ui-block-grid-group-configuration */ @customElement('umb-property-editor-ui-block-grid-group-configuration') -export class UmbPropertyEditorUIBlockGridGroupConfigurationElement extends UmbLitElement { +export class UmbPropertyEditorUIBlockGridGroupConfigurationElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property() value = ''; - @property({ type: Array, attribute: false }) - public config = []; + @property({ type: Object, attribute: false }) + public config?: UmbPropertyEditorConfigCollection render() { return html`
umb-property-editor-ui-block-grid-group-configuration
`; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/stylesheet-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/stylesheet-picker/manifests.ts index 23a1bbc221..80dee40314 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/stylesheet-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/stylesheet-picker/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.BlockGrid.StylesheetPicker', name: 'Block Grid Stylesheet Picker Property Editor UI', - loader: () => import('./property-editor-ui-block-grid-stylesheet-picker.element.js'), + js: () => import('./property-editor-ui-block-grid-stylesheet-picker.element.js'), meta: { label: 'Block Grid Stylesheet Picker', propertyEditorSchemaAlias: '', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/stylesheet-picker/property-editor-ui-block-grid-stylesheet-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/stylesheet-picker/property-editor-ui-block-grid-stylesheet-picker.element.ts index 36824c3075..c4984ccafc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/stylesheet-picker/property-editor-ui-block-grid-stylesheet-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/config/stylesheet-picker/property-editor-ui-block-grid-stylesheet-picker.element.ts @@ -1,4 +1,6 @@ +import { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -6,12 +8,12 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; * @element umb-property-editor-ui-block-grid-stylesheet-picker */ @customElement('umb-property-editor-ui-block-grid-stylesheet-picker') -export class UmbPropertyEditorUIBlockGridStylesheetPickerElement extends UmbLitElement { +export class UmbPropertyEditorUIBlockGridStylesheetPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property() value = ''; - @property({ type: Array, attribute: false }) - public config = []; + @property({ type: Object, attribute: false }) + public config?: UmbPropertyEditorConfigCollection; render() { return html`
umb-property-editor-ui-block-grid-stylesheet-picker
`; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/manifests.ts index 08c0dab0e7..039cfdceed 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-grid/manifests.ts @@ -7,11 +7,11 @@ const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.BlockGrid', name: 'Block Grid Property Editor UI', - loader: () => import('./property-editor-ui-block-grid.element.js'), + js: () => import('./property-editor-ui-block-grid.element.js'), meta: { label: 'Block Grid', propertyEditorSchemaAlias: 'Umbraco.BlockGrid', - icon: 'icon-icon-layout', + icon: 'icon-layout', group: 'richContent', settings: { properties: [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-list/config/block-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-list/config/block-configuration/manifests.ts index 85a68d98d4..0de60e5cc9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-list/config/block-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-list/config/block-configuration/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.BlockList.BlockConfiguration', name: 'Block List Block Configuration Property Editor UI', - loader: () => import('./property-editor-ui-block-list-block-configuration.element.js'), + js: () => import('./property-editor-ui-block-list-block-configuration.element.js'), meta: { label: 'Block List Block Configuration', propertyEditorSchemaAlias: '', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-list/config/block-configuration/property-editor-ui-block-list-block-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-list/config/block-configuration/property-editor-ui-block-list-block-configuration.element.ts index 69d2a89a67..36b7c0b63f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-list/config/block-configuration/property-editor-ui-block-list-block-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-list/config/block-configuration/property-editor-ui-block-list-block-configuration.element.ts @@ -1,4 +1,6 @@ +import { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -6,12 +8,12 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; * @element umb-property-editor-ui-block-list-block-configuration */ @customElement('umb-property-editor-ui-block-list-block-configuration') -export class UmbPropertyEditorUIBlockListBlockConfigurationElement extends UmbLitElement { +export class UmbPropertyEditorUIBlockListBlockConfigurationElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property() value = ''; - @property({ type: Array, attribute: false }) - public config = []; + @property({ type: Object, attribute: false }) + public config?: UmbPropertyEditorConfigCollection; render() { return html`
umb-property-editor-ui-block-list-block-configuration
`; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-list/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-list/manifests.ts index fc0f2c0e9d..67c0b50a8e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-list/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/block-list/manifests.ts @@ -5,7 +5,7 @@ const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.BlockList', name: 'Block List Property Editor UI', - loader: () => import('./property-editor-ui-block-list.element.js'), + js: () => import('./property-editor-ui-block-list.element.js'), meta: { label: 'Block List', propertyEditorSchemaAlias: 'Umbraco.BlockList', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/checkbox-list/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/checkbox-list/manifests.ts index 9f8ed56769..1bf1a190e7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/checkbox-list/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/checkbox-list/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.CheckboxList', name: 'Checkbox List Property Editor UI', - loader: () => import('./property-editor-ui-checkbox-list.element.js'), + js: () => import('./property-editor-ui-checkbox-list.element.js'), meta: { label: 'Checkbox List', propertyEditorSchemaAlias: 'Umbraco.CheckboxList', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts index 58b180eab1..b7eab89120 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.CollectionView.BulkActionPermissions', name: 'Collection View Bulk Action Permissions Property Editor UI', - loader: () => import('./property-editor-ui-collection-view-bulk-action-permissions.element.js'), + js: () => import('./property-editor-ui-collection-view-bulk-action-permissions.element.js'), meta: { label: 'Collection View Bulk Action Permissions', propertyEditorSchemaAlias: '', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-bulk-action-permissions.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-bulk-action-permissions.element.ts index a972802992..4377385649 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-bulk-action-permissions.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/bulk-action-permissions/property-editor-ui-collection-view-bulk-action-permissions.element.ts @@ -1,4 +1,6 @@ +import { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -6,12 +8,12 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; * @element umb-property-editor-ui-collection-view-bulk-action-permissions */ @customElement('umb-property-editor-ui-collection-view-bulk-action-permissions') -export class UmbPropertyEditorUICollectionViewBulkActionPermissionsElement extends UmbLitElement { +export class UmbPropertyEditorUICollectionViewBulkActionPermissionsElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property() value = ''; - @property({ type: Array, attribute: false }) - public config = []; + @property({ type: Object, attribute: false }) + public config?: UmbPropertyEditorConfigCollection; render() { return html`
umb-property-editor-ui-collection-view-bulk-action-permissions
`; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/manifests.ts index 4f64a2e993..f620db5289 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.CollectionView.ColumnConfiguration', name: 'Collection View Column Configuration Property Editor UI', - loader: () => import('./property-editor-ui-collection-view-column-configuration.element.js'), + js: () => import('./property-editor-ui-collection-view-column-configuration.element.js'), meta: { label: 'Collection View Column Configuration', propertyEditorSchemaAlias: '', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/property-editor-ui-collection-view-column-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/property-editor-ui-collection-view-column-configuration.element.ts index e8a4e43649..951d1956ca 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/property-editor-ui-collection-view-column-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/column-configuration/property-editor-ui-collection-view-column-configuration.element.ts @@ -1,4 +1,6 @@ +import { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -6,12 +8,12 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; * @element umb-property-editor-ui-collection-view-column-configuration */ @customElement('umb-property-editor-ui-collection-view-column-configuration') -export class UmbPropertyEditorUICollectionViewColumnConfigurationElement extends UmbLitElement { +export class UmbPropertyEditorUICollectionViewColumnConfigurationElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property() value = ''; - @property({ type: Array, attribute: false }) - public config = []; + @property({ type: Object, attribute: false }) + public config?: UmbPropertyEditorConfigCollection; render() { return html`
umb-property-editor-ui-collection-view-column-configuration
`; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/manifests.ts index ff82f4f963..e7b676efaf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.CollectionView.LayoutConfiguration', name: 'Collection View Column Configuration Property Editor UI', - loader: () => import('./property-editor-ui-collection-view-layout-configuration.element.js'), + js: () => import('./property-editor-ui-collection-view-layout-configuration.element.js'), meta: { label: 'Collection View Layout Configuration', propertyEditorSchemaAlias: '', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.element.ts index da22f536e2..b0cb58095f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/layout-configuration/property-editor-ui-collection-view-layout-configuration.element.ts @@ -1,4 +1,6 @@ +import { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -6,12 +8,12 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; * @element umb-property-editor-ui-collection-view-layout-configuration */ @customElement('umb-property-editor-ui-collection-view-layout-configuration') -export class UmbPropertyEditorUICollectionViewLayoutConfigurationElement extends UmbLitElement { +export class UmbPropertyEditorUICollectionViewLayoutConfigurationElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property() value = ''; - @property({ type: Array, attribute: false }) - public config = []; + @property({ type: Object, attribute: false }) + public config?: UmbPropertyEditorConfigCollection; render() { return html`
umb-property-editor-ui-collection-view-layout-configuration
`; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/manifests.ts index ca7e12e238..5e2e4a331f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.CollectionView.OrderBy', name: 'Collection View Column Configuration Property Editor UI', - loader: () => import('./property-editor-ui-collection-view-order-by.element.js'), + js: () => import('./property-editor-ui-collection-view-order-by.element.js'), meta: { label: 'Collection View Order By', propertyEditorSchemaAlias: '', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/property-editor-ui-collection-view-order-by.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/property-editor-ui-collection-view-order-by.element.ts index 67b0c23c4f..507c8ab67b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/property-editor-ui-collection-view-order-by.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/config/order-by/property-editor-ui-collection-view-order-by.element.ts @@ -1,4 +1,6 @@ +import { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -6,12 +8,12 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; * @element umb-property-editor-ui-collection-view-order-by */ @customElement('umb-property-editor-ui-collection-view-order-by') -export class UmbPropertyEditorUICollectionViewOrderByElement extends UmbLitElement { +export class UmbPropertyEditorUICollectionViewOrderByElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property() value = ''; - @property({ type: Array, attribute: false }) - public config = []; + @property({ type: Object, attribute: false }) + public config?: UmbPropertyEditorConfigCollection; render() { return html`
umb-property-editor-ui-collection-view-order-by
`; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/manifests.ts index 4f9ec72e14..ee2acaff38 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/manifests.ts @@ -8,7 +8,7 @@ const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.CollectionView', name: 'Collection View Property Editor UI', - loader: () => import('./property-editor-ui-collection-view.element.js'), + js: () => import('./property-editor-ui-collection-view.element.js'), meta: { label: 'Collection View', propertyEditorSchemaAlias: 'Umbraco.ListView', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/property-editor-ui-collection-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/property-editor-ui-collection-view.element.ts index 2a9d44e8af..fc478d0d94 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/property-editor-ui-collection-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/property-editor-ui-collection-view.element.ts @@ -1,3 +1,5 @@ +import { UmbPropertyEditorConfigCollection } from '../../config/index.js'; +import { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -6,12 +8,12 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; * @element umb-property-editor-ui-collection-view */ @customElement('umb-property-editor-ui-collection-view') -export class UmbPropertyEditorUICollectionViewElement extends UmbLitElement { +export class UmbPropertyEditorUICollectionViewElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property() value = ''; - @property({ type: Array, attribute: false }) - public config = []; + @property({ type: Object, attribute: false }) + public config?: UmbPropertyEditorConfigCollection; render() { return html`
umb-property-editor-ui-collection-view
`; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/color-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/color-picker/manifests.ts index f057cf0261..7b2fff99ba 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/color-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/color-picker/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.ColorPicker', name: 'Color Picker Property Editor UI', - loader: () => import('./property-editor-ui-color-picker.element.js'), + js: () => import('./property-editor-ui-color-picker.element.js'), meta: { label: 'Color Picker', propertyEditorSchemaAlias: 'Umbraco.ColorPicker', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/date-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/date-picker/manifests.ts index 9facbbc6ba..1794af37be 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/date-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/date-picker/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.DatePicker', name: 'Date Picker Property Editor UI', - loader: () => import('./property-editor-ui-date-picker.element.js'), + js: () => import('./property-editor-ui-date-picker.element.js'), meta: { label: 'Date Picker', propertyEditorSchemaAlias: 'Umbraco.DateTime', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/dropdown/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/dropdown/manifests.ts index 17fa07930a..912512ed48 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/dropdown/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/dropdown/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.Dropdown', name: 'Dropdown Property Editor UI', - loader: () => import('./property-editor-ui-dropdown.element.js'), + js: () => import('./property-editor-ui-dropdown.element.js'), meta: { label: 'Dropdown', propertyEditorSchemaAlias: 'Umbraco.Dropdown', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/eye-dropper/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/eye-dropper/manifests.ts index d6e0676f30..0dd8bfbc18 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/eye-dropper/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/eye-dropper/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.EyeDropper', name: 'Eye Dropper Color Picker Property Editor UI', - loader: () => import('./property-editor-ui-eye-dropper.element.js'), + js: () => import('./property-editor-ui-eye-dropper.element.js'), meta: { label: 'Eye Dropper Color Picker', icon: 'icon-colorpicker', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/icon-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/icon-picker/manifests.ts index 31590a83cc..7c97194a4c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/icon-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/icon-picker/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.IconPicker', name: 'Icon Picker Property Editor UI', - loader: () => import('./property-editor-ui-icon-picker.element.js'), + js: () => import('./property-editor-ui-icon-picker.element.js'), meta: { label: 'Icon Picker', propertyEditorSchemaAlias: 'Umbraco.IconPicker', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/image-cropper/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/image-cropper/manifests.ts index fd615a5f62..0a3eb6db6f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/image-cropper/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/image-cropper/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.ImageCropper', name: 'Image Cropper Property Editor UI', - loader: () => import('./property-editor-ui-image-cropper.element.js'), + js: () => import('./property-editor-ui-image-cropper.element.js'), meta: { label: 'Image Cropper', icon: 'icon-colorpicker', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/image-crops-configuration/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/image-crops-configuration/manifests.ts index e54c89a0f8..691e2da56f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/image-crops-configuration/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/image-crops-configuration/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.ImageCropsConfiguration', name: 'Image Crops Configuration Property Editor UI', - loader: () => import('./property-editor-ui-image-crops-configuration.element.js'), + js: () => import('./property-editor-ui-image-crops-configuration.element.js'), meta: { label: 'Image Crops Configuration', icon: 'icon-autofill', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/label/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/label/manifests.ts index 89eb715670..438b91fe4f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/label/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/label/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.Label', name: 'Label Property Editor UI', - loader: () => import('./property-editor-ui-label.element.js'), + js: () => import('./property-editor-ui-label.element.js'), meta: { label: 'Label', icon: 'icon-readonly', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/manifests.ts index 23c901afe7..d24e002b7e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/manifests.ts @@ -69,7 +69,7 @@ export const manifests: Array = [ type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.Number', name: 'Number Property Editor UI', - loader: () => import('./number/property-editor-ui-number.element.js'), + js: () => import('./number/property-editor-ui-number.element.js'), meta: { label: 'Number', icon: 'icon-autofill', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/markdown-editor/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/markdown-editor/manifests.ts index 39edd94f12..ba1a71a494 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/markdown-editor/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/markdown-editor/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.MarkdownEditor', name: 'Markdown Editor Property Editor UI', - loader: () => import('./property-editor-ui-markdown-editor.element.js'), + js: () => import('./property-editor-ui-markdown-editor.element.js'), meta: { label: 'Markdown Editor', propertyEditorSchemaAlias: 'Umbraco.MarkdownEditor', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/media-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/media-picker/manifests.ts index 716e7f1c47..43f21ff442 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/media-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/media-picker/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.MediaPicker', name: 'Markdown Editor Property Editor UI', - loader: () => import('./property-editor-ui-media-picker.element.js'), + js: () => import('./property-editor-ui-media-picker.element.js'), meta: { label: 'Media Picker', propertyEditorSchemaAlias: 'Umbraco.MediaPicker3', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/member-group-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/member-group-picker/manifests.ts index 0788efba6b..d2ffb94f7c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/member-group-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/member-group-picker/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.MemberGroupPicker', name: 'Member Group Picker Property Editor UI', - loader: () => import('./property-editor-ui-member-group-picker.element.js'), + js: () => import('./property-editor-ui-member-group-picker.element.js'), meta: { label: 'Member Group Picker', propertyEditorSchemaAlias: 'Umbraco.MemberGroupPicker', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/member-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/member-picker/manifests.ts index 079e333aff..875ecd4bc3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/member-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/member-picker/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.MemberPicker', name: 'Member Picker Property Editor UI', - loader: () => import('./property-editor-ui-member-picker.element.js'), + js: () => import('./property-editor-ui-member-picker.element.js'), meta: { label: 'Member Picker', propertyEditorSchemaAlias: 'Umbraco.MemberPicker', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/multi-url-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/multi-url-picker/manifests.ts index 246b61451d..98c0e38aa2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/multi-url-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/multi-url-picker/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.MultiUrlPicker', name: 'Multi URL Picker Property Editor UI', - loader: () => import('./property-editor-ui-multi-url-picker.element.js'), + js: () => import('./property-editor-ui-multi-url-picker.element.js'), meta: { label: 'Multi URL Picker', propertyEditorSchemaAlias: 'Umbraco.MultiUrlPicker', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/multiple-text-string/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/multiple-text-string/manifests.ts index 96285b9b16..acc4f72fe6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/multiple-text-string/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/multiple-text-string/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.MultipleTextString', name: 'Multiple Text String Property Editor UI', - loader: () => import('./property-editor-ui-multiple-text-string.element.js'), + js: () => import('./property-editor-ui-multiple-text-string.element.js'), meta: { label: 'Multiple Text String', propertyEditorSchemaAlias: 'Umbraco.MultipleTextString', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/number-range/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/number-range/manifests.ts index d8a7741082..c09a91473b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/number-range/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/number-range/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.NumberRange', name: 'Number Range Property Editor UI', - loader: () => import('./property-editor-ui-number-range.element.js'), + js: () => import('./property-editor-ui-number-range.element.js'), meta: { label: 'Number Range', propertyEditorSchemaAlias: '', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/number/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/number/manifests.ts index f1ea28e1d6..91e182c646 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/number/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/number/manifests.ts @@ -12,7 +12,7 @@ export const manifests: Array = [ type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.Integer', name: 'Integer Property Editor UI', - loader: () => import('./property-editor-ui-number.element.js'), + js: () => import('./property-editor-ui-number.element.js'), meta: { label: 'Integer', propertyEditorSchemaAlias: 'Umbraco.Integer', @@ -33,7 +33,7 @@ export const manifests: Array = [ type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.Decimal', name: 'Decimal Property Editor UI', - loader: () => import('./property-editor-ui-number.element.js'), + js: () => import('./property-editor-ui-number.element.js'), meta: { label: 'Decimal', propertyEditorSchemaAlias: 'Umbraco.Decimal', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/order-direction/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/order-direction/manifests.ts index 73a017f20c..e127106166 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/order-direction/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/order-direction/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.OrderDirection', name: 'Order Direction Property Editor UI', - loader: () => import('./property-editor-ui-order-direction.element.js'), + js: () => import('./property-editor-ui-order-direction.element.js'), meta: { label: 'Order Direction', propertyEditorSchemaAlias: '', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/overlay-size/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/overlay-size/manifests.ts index b238cd9645..4dfdfcfd0f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/overlay-size/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/overlay-size/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.OverlaySize', name: 'Overlay Size Property Editor UI', - loader: () => import('./property-editor-ui-overlay-size.element.js'), + js: () => import('./property-editor-ui-overlay-size.element.js'), meta: { label: 'Overlay Size', propertyEditorSchemaAlias: '', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/radio-button-list/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/radio-button-list/manifests.ts index 353e21d338..b542fbebb4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/radio-button-list/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/radio-button-list/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.RadioButtonList', name: 'Radio Button List Property Editor UI', - loader: () => import('./property-editor-ui-radio-button-list.element.js'), + js: () => import('./property-editor-ui-radio-button-list.element.js'), meta: { label: 'Radio Button List', propertyEditorSchemaAlias: 'Umbraco.RadioButtonList', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/slider/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/slider/manifests.ts index 0f67cba70d..24a6fe0a7f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/slider/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/slider/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.Slider', name: 'Slider Property Editor UI', - loader: () => import('./property-editor-ui-slider.element.js'), + js: () => import('./property-editor-ui-slider.element.js'), meta: { label: 'Slider', propertyEditorSchemaAlias: 'Umbraco.Slider', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/text-box/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/text-box/manifests.ts index 1a70870708..f67a4fa08e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/text-box/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/text-box/manifests.ts @@ -13,7 +13,7 @@ export const manifests: Array = [ type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.TextBox', name: 'Text Box Property Editor UI', - loader: () => import('./property-editor-ui-text-box.element.js'), + js: () => import('./property-editor-ui-text-box.element.js'), meta: { label: 'Text Box', propertyEditorSchemaAlias: 'Umbraco.TextBox', @@ -34,7 +34,7 @@ export const manifests: Array = [ type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.Email', name: 'Email Property Editor UI', - loader: () => import('./property-editor-ui-text-box.element.js'), + js: () => import('./property-editor-ui-text-box.element.js'), meta: { label: 'Email', propertyEditorSchemaAlias: 'Umbraco.EmailAddress', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/textarea/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/textarea/manifests.ts index eac2bf8605..a660eb9f9f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/textarea/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/textarea/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.TextArea', name: 'Text Area Property Editor UI', - loader: () => import('./property-editor-ui-textarea.element.js'), + js: () => import('./property-editor-ui-textarea.element.js'), meta: { label: 'Text Area', propertyEditorSchemaAlias: 'Umbraco.TextArea', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/config/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/config/manifests.ts index 987898d117..6d473ac5ef 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/config/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/config/manifests.ts @@ -5,7 +5,7 @@ const configurationManifests: Array = [ type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUI.TinyMCE.ToolbarConfiguration', name: 'TinyMCE Toolbar Property Editor UI', - loader: () => import('./toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.js'), + js: () => import('./toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.js'), meta: { label: 'TinyMCE Toolbar Configuration', propertyEditorSchemaAlias: 'Umbraco.RichText.Configuration', @@ -17,7 +17,7 @@ const configurationManifests: Array = [ type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUI.TinyMCE.StylesheetsConfiguration', name: 'TinyMCE Stylesheets Property Editor UI', - loader: () => import('./stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.js'), + js: () => import('./stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.js'), meta: { label: 'TinyMCE Stylesheets Configuration', propertyEditorSchemaAlias: 'Umbraco.RichText.Configuration', @@ -29,7 +29,7 @@ const configurationManifests: Array = [ type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUI.TinyMCE.DimensionsConfiguration', name: 'TinyMCE Dimensions Property Editor UI', - loader: () => import('./dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.js'), + js: () => import('./dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.js'), meta: { label: 'TinyMCE Dimensions Configuration', propertyEditorSchemaAlias: 'Umbraco.RichText.Configuration', @@ -41,7 +41,7 @@ const configurationManifests: Array = [ type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUI.TinyMCE.MaxImageSizeConfiguration', name: 'TinyMCE Max Image Size Property Editor UI', - loader: () => import('./max-image-size/property-editor-ui-tiny-mce-maximagesize-configuration.element.js'), + js: () => import('./max-image-size/property-editor-ui-tiny-mce-maximagesize-configuration.element.js'), meta: { label: 'TinyMCE Max Image Size Configuration', propertyEditorSchemaAlias: 'Umbraco.RichText.Configuration', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/config/max-image-size/property-editor-ui-tiny-mce-maximagesize-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/config/max-image-size/property-editor-ui-tiny-mce-maximagesize-configuration.element.ts index 4a80cbe6d8..2d06aed494 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/config/max-image-size/property-editor-ui-tiny-mce-maximagesize-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/config/max-image-size/property-editor-ui-tiny-mce-maximagesize-configuration.element.ts @@ -1,14 +1,15 @@ import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; import { customElement, html, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; +import { UmbPropertyEditorUiElement } from "@umbraco-cms/backoffice/extension-registry"; /** * @element umb-property-editor-ui-tiny-mce-maximagesize-configuration */ @customElement('umb-property-editor-ui-tiny-mce-maximagesize-configuration') -export class UmbPropertyEditorUITinyMceMaxImageSizeConfigurationElement extends UmbLitElement { - @property() - value?: number; +export class UmbPropertyEditorUITinyMceMaxImageSizeConfigurationElement extends UmbLitElement implements UmbPropertyEditorUiElement { + @property({type: Number}) + value: number = 0; render() { return html``; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/config/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/config/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts index c8db0205f4..b11d7f95b7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/config/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/config/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts @@ -3,17 +3,19 @@ import { css, customElement, html, property, state } from '@umbraco-cms/backoffi import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbStylesheetRepository } from '@umbraco-cms/backoffice/stylesheet'; import { StylesheetOverviewResponseModel } from '@umbraco-cms/backoffice/backend-api'; +import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; +import { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; /** * @element umb-property-editor-ui-tiny-mce-stylesheets-configuration */ @customElement('umb-property-editor-ui-tiny-mce-stylesheets-configuration') -export class UmbPropertyEditorUITinyMceStylesheetsConfigurationElement extends UmbLitElement { +export class UmbPropertyEditorUITinyMceStylesheetsConfigurationElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property({ type: Array }) value: string[] = []; - @property({ type: Array, attribute: false }) - public config = []; + @property({ type: Object, attribute: false }) + public config?: UmbPropertyEditorConfigCollection; @state() stylesheetList: Array> = []; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/manifests.ts index 78a2d8aa38..f9db7b02f7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/manifests.ts @@ -5,7 +5,7 @@ const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.TinyMCE', name: 'Rich Text Editor Property Editor UI', - loader: () => import('./property-editor-ui-tiny-mce.element.js'), + js: () => import('./property-editor-ui-tiny-mce.element.js'), meta: { label: 'Rich Text Editor', propertyEditorSchemaAlias: 'Umbraco.RichText', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/plugins/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/plugins/manifests.ts index be1274b4bf..f4d4718e08 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/plugins/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tiny-mce/plugins/manifests.ts @@ -5,7 +5,7 @@ const pluginManifests: Array = [ type: 'tinyMcePlugin', alias: 'Umb.TinyMcePlugin.CodeEditor', name: 'Code Editor TinyMCE Plugin', - loader: () => import('./tiny-mce-code-editor.plugin.js'), + js: () => import('./tiny-mce-code-editor.plugin.js'), meta: { toolbar: [ { @@ -20,7 +20,7 @@ const pluginManifests: Array = [ type: 'tinyMcePlugin', alias: 'Umb.TinyMcePlugin.LinkPicker', name: 'Link Picker TinyMCE Plugin', - loader: () => import('./tiny-mce-linkpicker.plugin.js'), + js: () => import('./tiny-mce-linkpicker.plugin.js'), meta: { toolbar: [ { @@ -40,7 +40,7 @@ const pluginManifests: Array = [ type: 'tinyMcePlugin', alias: 'Umb.TinyMcePlugin.MediaPicker', name: 'Media Picker TinyMCE Plugin', - loader: () => import('./tiny-mce-mediapicker.plugin.js'), + js: () => import('./tiny-mce-mediapicker.plugin.js'), meta: { toolbar: [ { @@ -55,7 +55,7 @@ const pluginManifests: Array = [ type: 'tinyMcePlugin', alias: 'Umb.TinyMcePlugin.EmbeddedMedia', name: 'Embedded Media TinyMCE Plugin', - loader: () => import('./tiny-mce-embeddedmedia.plugin.js'), + js: () => import('./tiny-mce-embeddedmedia.plugin.js'), meta: { toolbar: [ { @@ -70,7 +70,7 @@ const pluginManifests: Array = [ type: 'tinyMcePlugin', alias: 'Umb.TinyMcePlugin.MacroPicker', name: 'Macro Picker TinyMCE Plugin', - loader: () => import('./tiny-mce-macropicker.plugin.js'), + js: () => import('./tiny-mce-macropicker.plugin.js'), meta: { toolbar: [ { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/toggle/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/toggle/manifests.ts index 2f440b2102..f357dc2079 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/toggle/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/toggle/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.Toggle', name: 'Toggle Property Editor UI', - loader: () => import('./property-editor-ui-toggle.element.js'), + js: () => import('./property-editor-ui-toggle.element.js'), meta: { label: 'Toggle', propertyEditorSchemaAlias: 'Umbraco.TrueFalse', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/start-node/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/start-node/manifests.ts index 910db83801..6f2287e7ab 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/start-node/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/start-node/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.TreePicker.StartNode', name: 'Tree Picker Start Node Property Editor UI', - loader: () => import('./property-editor-ui-tree-picker-start-node.element.js'), + js: () => import('./property-editor-ui-tree-picker-start-node.element.js'), meta: { label: 'Tree Picker Start Node', icon: 'icon-page-add', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/start-node/property-editor-ui-tree-picker-start-node.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/start-node/property-editor-ui-tree-picker-start-node.element.ts index 6700ad85d1..ea993e0e7a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/start-node/property-editor-ui-tree-picker-start-node.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/config/start-node/property-editor-ui-tree-picker-start-node.element.ts @@ -1,5 +1,7 @@ import { StartNode, UmbInputContentTypeElement } from '@umbraco-cms/backoffice/content-type'; +import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -7,10 +9,13 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; * @element umb-property-editor-ui-tree-picker-start-node */ @customElement('umb-property-editor-ui-tree-picker-start-node') -export class UmbPropertyEditorUITreePickerStartNodeElement extends UmbLitElement { +export class UmbPropertyEditorUITreePickerStartNodeElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property({ type: Object }) value?: StartNode; + @property({ type: Object, attribute: false }) + public config?: UmbPropertyEditorConfigCollection; + #onChange(event: CustomEvent) { const target = event.target as UmbInputContentTypeElement; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/manifests.ts index b3bb80feb1..4c81dbda44 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/manifests.ts @@ -5,7 +5,7 @@ const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.TreePicker', name: 'Tree Picker Property Editor UI', - loader: () => import('./property-editor-ui-tree-picker.element.js'), + js: () => import('./property-editor-ui-tree-picker.element.js'), meta: { label: 'Tree Picker', icon: 'icon-page-add', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/upload-field/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/upload-field/manifests.ts index ee3504e95a..8c7c9cf522 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/upload-field/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/upload-field/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.UploadField', name: 'Upload Field Property Editor UI', - loader: () => import('./property-editor-ui-upload-field.element.js'), + js: () => import('./property-editor-ui-upload-field.element.js'), meta: { label: 'Upload Field', propertyEditorSchemaAlias: 'Umbraco.UploadField', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/user-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/user-picker/manifests.ts index f80baede7f..bb0ab0f765 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/user-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/user-picker/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.UserPicker', name: 'User Picker Property Editor UI', - loader: () => import('./property-editor-ui-user-picker.element.js'), + js: () => import('./property-editor-ui-user-picker.element.js'), meta: { label: 'User Picker', propertyEditorSchemaAlias: 'Umbraco.UserPicker', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/value-type/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/value-type/manifests.ts index d6c8ec6ea9..34b1e679ab 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/value-type/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/value-type/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.ValueType', name: 'Value Type Property Editor UI', - loader: () => import('./property-editor-ui-value-type.element.js'), + js: () => import('./property-editor-ui-value-type.element.js'), meta: { label: 'Value Type', icon: 'icon-autofill', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-default.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/section-default.element.ts index 29b95ae1c6..fc54a88ad5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-default.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/section-default.element.ts @@ -1,6 +1,6 @@ import type { UmbWorkspaceElement } from '../workspace/workspace.element.js'; import type { UmbSectionMainViewElement } from './section-main-views/section-main-views.element.js'; -import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, nothing, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; import { ManifestSection, @@ -11,7 +11,7 @@ import { } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbRoute } from '@umbraco-cms/backoffice/router'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UmbExtensionElementInitializer, UmbExtensionsElementController } from '@umbraco-cms/backoffice/extension-api'; +import { UmbExtensionElementInitializer, UmbExtensionsElementInitializer } from '@umbraco-cms/backoffice/extension-api'; /** * @export @@ -45,7 +45,7 @@ export class UmbSectionDefaultElement extends UmbLitElement implements UmbSectio constructor() { super(); - new UmbExtensionsElementController(this, umbExtensionsRegistry, 'sectionSidebarApp', null, (sidebarApps) => { + new UmbExtensionsElementInitializer(this, umbExtensionsRegistry, 'sectionSidebarApp', null, (sidebarApps) => { const oldValue = this._sidebarApps; this._sidebarApps = sidebarApps; this.requestUpdate('_sidebarApps', oldValue); @@ -82,7 +82,7 @@ export class UmbSectionDefaultElement extends UmbLitElement implements UmbSectio ${repeat( this._sidebarApps, (app) => app.alias, - (app) => app.component + (app) => app.component, )} ` diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-main-views/section-main-views.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/section-main-views/section-main-views.element.ts index d9323dd5be..f0d04ff14e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-main-views/section-main-views.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/section-main-views/section-main-views.element.ts @@ -8,7 +8,7 @@ import { UmbSectionViewExtensionElement, umbExtensionsRegistry, } from '@umbraco-cms/backoffice/extension-registry'; -import { UmbExtensionsManifestController, createExtensionElement } from '@umbraco-cms/backoffice/extension-api'; +import { UmbExtensionsManifestInitializer, createExtensionElement } from '@umbraco-cms/backoffice/extension-api'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { pathFolderName } from '@umbraco-cms/backoffice/utils'; @@ -36,12 +36,12 @@ export class UmbSectionMainViewElement extends UmbLitElement { constructor() { super(); - new UmbExtensionsManifestController(this, umbExtensionsRegistry, 'dashboard', null, (dashboards) => { + new UmbExtensionsManifestInitializer(this, umbExtensionsRegistry, 'dashboard', null, (dashboards) => { this._dashboards = dashboards.map((dashboard) => dashboard.manifest); this.#createRoutes(); }); - new UmbExtensionsManifestController(this, umbExtensionsRegistry, 'sectionView', null, (views) => { + new UmbExtensionsManifestInitializer(this, umbExtensionsRegistry, 'sectionView', null, (views) => { this._views = views.map((view) => view.manifest); this.#createRoutes(); }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/themes/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/themes/manifests.ts index ca2c0d1285..8754ac2767 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/themes/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/themes/manifests.ts @@ -5,7 +5,7 @@ export const themes: Array = [ type: 'globalContext', alias: 'Umb.GlobalContext.Theme', name: 'Theme Context', - loader: () => import('./theme.context.js'), + js: () => import('./theme.context.js'), }, { type: 'theme', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/themes/theme.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/themes/theme.context.ts index 53b5efb947..917740824d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/themes/theme.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/themes/theme.context.ts @@ -3,6 +3,7 @@ import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbStringState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; import { UmbBaseController, UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { ManifestTheme, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { loadManifestPlainCss } from '@umbraco-cms/backoffice/extension-api'; const LOCAL_STORAGE_KEY = 'umb-theme-alias'; @@ -37,31 +38,34 @@ export class UmbThemeContext extends UmbBaseController { .pipe(map((extensions) => extensions.filter((extension) => extension.alias === themeAlias))), async (themes) => { this.#styleElement?.remove(); - if (themes.length > 0) { - if (themes[0].loader) { + if (themes.length > 0 && themes[0].css) { + const activeTheme = themes[0]; + if (typeof activeTheme.css === 'function') { const styleEl = (this.#styleElement = document.createElement('style')); styleEl.setAttribute('type', 'text/css'); document.head.appendChild(styleEl); - const result = await themes[0].loader(); + const result = await loadManifestPlainCss(activeTheme.css); // Checking that this is still our styleElement, it has not been replaced with another theme in between. - if (styleEl === this.#styleElement) { - (styleEl as any).appendChild(document.createTextNode(result)); + if (result && styleEl === this.#styleElement) { + styleEl.appendChild(document.createTextNode(result)); } - } else if (themes[0].css) { + } else if (typeof activeTheme.css === 'string') { this.#styleElement = document.createElement('link'); this.#styleElement.setAttribute('rel', 'stylesheet'); - this.#styleElement.setAttribute('href', themes[0].css); + this.#styleElement.setAttribute('href', activeTheme.css); document.head.appendChild(this.#styleElement); } } else { localStorage.removeItem(LOCAL_STORAGE_KEY); + this.#styleElement?.childNodes.forEach((node) => node.remove()); this.#styleElement?.setAttribute('href', ''); } } ); } else { localStorage.removeItem(LOCAL_STORAGE_KEY); + this.#styleElement?.childNodes.forEach((node) => node.remove()); this.#styleElement?.setAttribute('href', ''); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/core/umbraco-package.ts index 5a4b160bb8..7029aa3be3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/umbraco-package.ts @@ -7,6 +7,6 @@ export const extensions: Array = [ name: 'Core Entry Point', alias: 'Umb.EntryPoint.Core', type: 'entryPoint', - loader: () => import('./index.js'), + js: () => import('./index.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-action/workspace-action-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-action/workspace-action-base.ts index 58d7bf467a..1251b1c334 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-action/workspace-action-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-action/workspace-action-base.ts @@ -1,25 +1,25 @@ import { UmbWorkspaceContextInterface, UMB_WORKSPACE_CONTEXT } from '../workspace-context/index.js'; -import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; export interface UmbWorkspaceAction extends UmbApi { - host: UmbControllerHostElement; + host: UmbControllerHost; workspaceContext?: WorkspaceType; execute(): Promise; } -export abstract class UmbWorkspaceActionBase - implements UmbWorkspaceAction +export abstract class UmbWorkspaceActionBase + implements UmbWorkspaceAction { - host: UmbControllerHostElement; - workspaceContext?: WorkspaceType; - constructor(host: UmbControllerHostElement) { + host: UmbControllerHost; + workspaceContext?: WorkspaceContextType; + constructor(host: UmbControllerHost) { this.host = host; new UmbContextConsumerController(this.host, UMB_WORKSPACE_CONTEXT, (instance) => { // TODO: Be aware we are casting here. We should consider a better solution for typing the contexts. (But notice we still want to capture the first workspace...) - this.workspaceContext = instance as unknown as WorkspaceType; + this.workspaceContext = instance as unknown as WorkspaceContextType; }); } abstract execute(): Promise; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/workspace-context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/editable-workspace-context-base.ts similarity index 80% rename from src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/workspace-context.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/editable-workspace-context-base.ts index d9ec50b0ad..84decb5fcc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/workspace-context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/editable-workspace-context-base.ts @@ -5,12 +5,7 @@ import type { UmbEntityBase } from '@umbraco-cms/backoffice/models'; import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UMB_MODAL_CONTEXT_TOKEN, UmbModalContext } from '@umbraco-cms/backoffice/modal'; -/* - -TODO: We need to figure out if we like to keep using same alias for all workspace contexts. -If so we need to align on a interface that all of these implements. otherwise consumers cant trust the workspace-context. -*/ -export abstract class UmbWorkspaceContext +export abstract class UmbEditableWorkspaceContextBase extends UmbBaseController implements UmbSaveableWorkspaceContextInterface { @@ -56,8 +51,8 @@ export abstract class UmbWorkspaceContext; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/index.ts index 776e5ce113..3b47618ca4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/index.ts @@ -4,7 +4,7 @@ export * from './publishable-workspace-context.interface.js'; export * from './saveable-workspace-context.interface.js'; export * from './variant-workspace-context.token.js'; export * from './workspace-context.interface.js'; -export * from './workspace-context.js'; +export * from './editable-workspace-context-base.js'; export * from './workspace-context.token.js'; export * from './workspace-invariantable-context.interface.js'; export * from './workspace-variantable-context.interface.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-editor/workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-editor/workspace-editor.element.ts index 9ff49f64a2..0590328ffe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-editor/workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-editor/workspace-editor.element.ts @@ -1,4 +1,4 @@ -import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, nothing, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; import type { UmbRoute, UmbRouterSlotInitEvent, UmbRouterSlotChangeEvent } from '@umbraco-cms/backoffice/router'; import { @@ -6,7 +6,7 @@ import { ManifestWorkspaceViewCollection, umbExtensionsRegistry, } from '@umbraco-cms/backoffice/extension-registry'; -import { UmbExtensionsManifestController, createExtensionElement } from '@umbraco-cms/backoffice/extension-api'; +import { UmbExtensionsManifestInitializer, createExtensionElement } from '@umbraco-cms/backoffice/extension-api'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { componentHasManifestProperty } from '@umbraco-cms/backoffice/utils'; @@ -21,10 +21,10 @@ import { componentHasManifestProperty } from '@umbraco-cms/backoffice/utils'; * @slot actions - Slot for workspace footer actions * @slot default - slot for main content * @export - * @class UmbWorkspaceLayout + * @class UmbWorkspaceEditor * @extends {UmbLitElement} */ -// TODO: This element has a bug in the tabs. After the url changes - for example a new entity/file is chosen in the tree and loaded to the workspace the links in the tabs still point to the previous url and therefore views do not change correctly +// TODO: This element has a bug in the tabs. After the url changes - for example a new entity/file is chosen in the tree and loaded to the workspace the links in the tabs still point to the previous url and therefore views do not change correctly @customElement('umb-workspace-editor') export class UmbWorkspaceEditorElement extends UmbLitElement { @property() @@ -36,16 +36,6 @@ export class UmbWorkspaceEditorElement extends UmbLitElement { @property({ type: Boolean }) public enforceNoFooter = false; - // TODO: Revisit if we can remove the alias from the workspace-editor. Its not used for anything, as the context now takes care of it. - /** - * Alias of the workspace. Currently not used for anything. - * @public - * @type {string | undefined} - * @attr - */ - @property() - public alias?: string; - @state() private _workspaceViews: Array = []; @@ -60,7 +50,7 @@ export class UmbWorkspaceEditorElement extends UmbLitElement { constructor() { super(); - new UmbExtensionsManifestController( + new UmbExtensionsManifestInitializer( this, umbExtensionsRegistry, ['workspaceEditorView', 'workspaceViewCollection'], @@ -68,7 +58,7 @@ export class UmbWorkspaceEditorElement extends UmbLitElement { (workspaceViews) => { this._workspaceViews = workspaceViews.map((view) => view.manifest); this._createRoutes(); - } + }, ); } @@ -142,7 +132,7 @@ export class UmbWorkspaceEditorElement extends UmbLitElement { ${view.meta.label || view.name} - ` + `, )} ` diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-is-new-redirect-controller/workspace-is-new-redirect-controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-is-new-redirect-controller/workspace-is-new-redirect-controller.ts index e16bbc73bb..74a4192315 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-is-new-redirect-controller/workspace-is-new-redirect-controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-is-new-redirect-controller/workspace-is-new-redirect-controller.ts @@ -1,4 +1,4 @@ -import { UmbWorkspaceContext } from '../workspace-context/index.js'; +import { UmbEditableWorkspaceContextBase } from '../workspace-context/index.js'; import { UmbBaseController, type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbEntityBase } from '@umbraco-cms/backoffice/models'; import { createRoutePathBuilder, type UmbRouterSlotElement } from '@umbraco-cms/backoffice/router'; @@ -16,10 +16,10 @@ import { ensurePathEndsWithSlash } from '@umbraco-cms/backoffice/utils'; export class UmbWorkspaceIsNewRedirectController extends UmbBaseController { constructor( host: UmbControllerHost, - workspaceContext: UmbWorkspaceContext, + workspaceContext: UmbEditableWorkspaceContextBase, router: UmbRouterSlotElement, ) { - super(host); + super(host, 'isNewRedirectController'); // Navigate to edit route when language is created: this.observe( diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-modal/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-modal/manifests.ts index 4d7e841c95..f0f2d124b8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-modal/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-modal/manifests.ts @@ -4,7 +4,7 @@ const workspaceModal: ManifestModal = { type: 'modal', alias: 'Umb.Modal.Workspace', name: 'Workspace Modal', - loader: () => import('./workspace-modal.element.js'), + js: () => import('./workspace-modal.element.js'), }; export const manifests = [workspaceModal]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-property/workspace-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-property/workspace-property.element.ts index 24ef4a841a..cb71042839 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-property/workspace-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-property/workspace-property.element.ts @@ -135,7 +135,7 @@ export class UmbWorkspacePropertyElement extends UmbLitElement { ); } - private _gotEditorUI(manifest?: ManifestPropertyEditorUi | null) { + private async _gotEditorUI(manifest?: ManifestPropertyEditorUi | null) { this._propertyContext.setEditor(undefined); if (!manifest) { @@ -143,48 +143,45 @@ export class UmbWorkspacePropertyElement extends UmbLitElement { return; } - createExtensionElement(manifest) - .then((el) => { - const oldValue = this._element; + const el = await createExtensionElement(manifest); + if(el) { + const oldValue = this._element; - oldValue?.removeEventListener('change', this._onPropertyEditorChange as any as EventListener); + oldValue?.removeEventListener('change', this._onPropertyEditorChange as any as EventListener); - this._element = el as ManifestPropertyEditorUi['ELEMENT_TYPE']; + this._element = el as ManifestPropertyEditorUi['ELEMENT_TYPE']; - this._propertyContext.setEditor(this._element); + this._propertyContext.setEditor(this._element); - this._valueObserver?.destroy(); - this._configObserver?.destroy(); + this._valueObserver?.destroy(); + this._configObserver?.destroy(); - if (this._element) { - this._element.addEventListener('property-value-change', this._onPropertyEditorChange as any as EventListener); + 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; - } - }, - '_observePropertyValue' - ); - this._configObserver = this.observe( - this._propertyContext.config, - (config) => { - if (this._element && config) { - this._element.config = config; - } - }, - '_observePropertyConfig' - ); - } + 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); - }) - .catch(() => { - // TODO: loading JS failed so we should do some nice UI. (This does only happen if extension has a js prop, otherwise we concluded that no source was needed resolved the load.) - }); + this.requestUpdate('element', oldValue); + } } render() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/entity-actions/manifests.ts index cd83e8f7e2..0173032ee9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/entity-actions/manifests.ts @@ -93,19 +93,19 @@ const modals: Array = [ type: 'modal', alias: 'Umb.Modal.CreateDictionary', name: 'Create Dictionary Modal', - loader: () => import('./create/create-dictionary-modal.element.js'), + js: () => import('./create/create-dictionary-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.ExportDictionary', name: 'Export Dictionary Modal', - loader: () => import('./export/export-dictionary-modal.element.js'), + js: () => import('./export/export-dictionary-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.ImportDictionary', name: 'Import Dictionary Modal', - loader: () => import('./import/import-dictionary-modal.element.js'), + js: () => import('./import/import-dictionary-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.context.ts index 0bb29045ad..492b157e66 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.context.ts @@ -1,12 +1,12 @@ import { UmbDictionaryRepository } from '../repository/dictionary.repository.js'; -import { UmbSaveableWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { UmbSaveableWorkspaceContextInterface, UmbEditableWorkspaceContextBase } from '@umbraco-cms/backoffice/workspace'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import { DictionaryItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbDictionaryWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbSaveableWorkspaceContextInterface { #data = new UmbObjectState(undefined); diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/manifests.ts index fe24ca792a..76ff32fd89 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/manifests.ts @@ -9,7 +9,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.Dictionary', name: 'Dictionary Workspace', - loader: () => import('./dictionary-workspace.element.js'), + js: () => import('./dictionary-workspace.element.js'), meta: { entityType: 'dictionary-item', }, @@ -20,7 +20,7 @@ const workspaceViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.Dictionary.Edit', name: 'Dictionary Workspace Edit View', - loader: () => import('./views/editor/workspace-view-dictionary-editor.element.js'), + js: () => import('./views/editor/workspace-view-dictionary-editor.element.js'), weight: 100, meta: { label: 'Edit', diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/section.manifest.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/section.manifest.ts index 53f963ca62..eefc5e1c62 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/section.manifest.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/section.manifest.ts @@ -37,7 +37,7 @@ const dashboards: Array = [ alias: 'Umb.Dashboard.LocalizationDictionary', name: 'Dictionary localization Dashboard', elementName: 'umb-dashboard-translation-dictionary', - loader: () => import('./dashboards/dictionary/dashboard-localization-dictionary.element.js'), + js: () => import('./dashboards/dictionary/dashboard-localization-dictionary.element.js'), meta: { label: 'Dictionary overview', pathname: '', diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/umbraco-package.ts index 270c7257e8..decfec4787 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/umbraco-package.ts @@ -4,6 +4,6 @@ export const extensions = [ name: 'Dictionary Management Bundle', alias: 'Umb.Dictionary.TranslationManagement', type: 'bundle', - loader: () => import('./manifests.js'), + js: () => import('./manifests.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/dashboards/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/dashboards/manifests.ts index 1a4b46c347..4d156accdd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/dashboards/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/dashboards/manifests.ts @@ -5,7 +5,7 @@ const dashboards: Array = [ type: 'dashboard', alias: 'Umb.Dashboard.RedirectManagement', name: 'Redirect Management Dashboard', - loader: () => import('./redirect-management/dashboard-redirect-management.element.js'), + js: () => import('./redirect-management/dashboard-redirect-management.element.js'), weight: 10, meta: { label: 'Redirect Management', diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/workspace/manifests.ts index 944e219ec5..895fa704a4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/workspace/manifests.ts @@ -8,7 +8,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.DocumentBlueprint.Root', name: 'Document Blueprint Root Workspace', - loader: () => import('./document-blueprint-root-workspace.element.js'), + js: () => import('./document-blueprint-root-workspace.element.js'), meta: { entityType: 'document-blueprint-root', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/create/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/create/manifests.ts index 2daaa4bee8..3d41552178 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/create/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/create/manifests.ts @@ -25,7 +25,7 @@ const entityActions: Array = [ type: 'modal', alias: 'Umb.Modal.DocumentTypeCreateOptions', name: 'Document Type Create Options Modal', - loader: () => import('./modal/document-type-create-options-modal.element.js'), + js: () => import('./modal/document-type-create-options-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts index 6e8b4384b5..82b49c3a00 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts @@ -1,6 +1,9 @@ import { UmbDocumentTypeDetailRepository } from '../repository/detail/document-type-detail.repository.js'; import { UmbContentTypePropertyStructureManager } from '@umbraco-cms/backoffice/content-type'; -import { UmbWorkspaceContext, UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import { + UmbEditableWorkspaceContextBase, + UmbSaveableWorkspaceContextInterface, +} from '@umbraco-cms/backoffice/workspace'; import type { ContentTypeCompositionModel, ContentTypeSortModel, @@ -12,7 +15,7 @@ import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api'; type EntityType = DocumentTypeResponseModel; export class UmbDocumentTypeWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbSaveableWorkspaceContextInterface { // Draft is located in structure manager @@ -135,7 +138,6 @@ export class UmbDocumentTypeWorkspaceContext this.setIsSorting(false); //this.#draft.next(data); return { data } || undefined; - // TODO: Is this wrong? should we return { data }?? } async load(entityId: string) { @@ -146,7 +148,6 @@ export class UmbDocumentTypeWorkspaceContext this.setIsSorting(false); //this.#draft.next(data); return { data } || undefined; - // TODO: Is this wrong? should we return { data }?? } /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/manifests.ts index 7d6841ff51..1ee9fee6f6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/manifests.ts @@ -9,7 +9,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.DocumentType', name: 'Document Type Workspace', - loader: () => import('./document-type-workspace.element.js'), + js: () => import('./document-type-workspace.element.js'), meta: { entityType: 'document-type', }, @@ -20,7 +20,7 @@ const workspaceEditorViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.DocumentType.Design', name: 'Document Type Workspace Design View', - loader: () => import('./views/design/document-type-workspace-view-edit.element.js'), + js: () => import('./views/design/document-type-workspace-view-edit.element.js'), weight: 1000, meta: { label: 'Design', @@ -38,7 +38,7 @@ const workspaceEditorViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.DocumentType.Structure', name: 'Document Type Workspace Structure View', - loader: () => import('./views/structure/document-type-workspace-view-structure.element.js'), + js: () => import('./views/structure/document-type-workspace-view-structure.element.js'), weight: 800, meta: { label: 'Structure', @@ -56,7 +56,7 @@ const workspaceEditorViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.DocumentType.Settings', name: 'Document Type Workspace Settings View', - loader: () => import('./views/settings/document-type-workspace-view-settings.element.js'), + js: () => import('./views/settings/document-type-workspace-view-settings.element.js'), weight: 600, meta: { label: 'Settings', @@ -74,7 +74,7 @@ const workspaceEditorViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.DocumentType.Templates', name: 'Document Type Workspace Templates View', - loader: () => import('./views/templates/document-type-workspace-view-templates.element.js'), + js: () => import('./views/templates/document-type-workspace-view-templates.element.js'), weight: 400, meta: { label: 'Templates', diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/manifests.ts index 944c3e369c..14a2951b5e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/manifests.ts @@ -5,11 +5,12 @@ export const manifests: Array = [ type: 'collectionView', alias: 'Umb.CollectionView.Document.Table', name: 'Document Table Collection View', - loader: () => import('./views/table/document-table-collection-view.element.js'), + js: () => import('./views/table/document-table-collection-view.element.js'), weight: 200, - conditions: { - entityType: 'document', - }, + conditions: [{ + alias: 'Umb.Condition.WorkspaceEntityType', + match: 'document', + }], meta: { label: 'Table', icon: 'icon-box', diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts index 3ba8aac885..fca3f39eac 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/manifests.ts @@ -35,7 +35,7 @@ const modals: Array = [ type: 'modal', alias: 'Umb.Modal.CreateDocument', name: 'Create Document Modal', - loader: () => import('./create-document-modal.element.js'), + js: () => import('./create-document-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/permissions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/permissions/manifests.ts index c6b981c133..49e975fc98 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/permissions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/permissions/manifests.ts @@ -23,7 +23,7 @@ const modals: Array = [ type: 'modal', alias: 'Umb.Modal.Permissions', name: 'Permissions Modal', - loader: () => import('./permissions-modal.element.js'), + js: () => import('./permissions-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/manifests.ts index a7016dff88..fc2041a32b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/manifests.ts @@ -4,7 +4,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.DocumentPicker', name: 'Document Picker Property Editor UI', - loader: () => import('./property-editor-ui-document-picker.element.js'), + js: () => import('./property-editor-ui-document-picker.element.js'), meta: { label: 'Document Picker', propertyEditorSchemaAlias: 'Umbraco.ContentPicker', diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/manifests.ts index 3b17cbf88b..cad95f0d15 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/tree/manifests.ts @@ -39,7 +39,7 @@ const treeItem: ManifestTreeItem = { type: 'treeItem', alias: 'Umb.TreeItem.Document', name: 'Document Tree Item', - loader: () => import('./tree-item/document-tree-item.element.js'), + js: () => import('./tree-item/document-tree-item.element.js'), meta: { entityTypes: [UMB_DOCUMENT_ROOT_ENTITY_TYPE, UMB_DOCUMENT_ENTITY_TYPE], }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/manifests.ts index 08f16fa720..b1460257c6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/user-permissions/manifests.ts @@ -195,7 +195,7 @@ export const granularPermissions: Array = [ type: 'userGranularPermission', alias: 'Umb.UserGranularPermission.Document', name: 'Document Granular User Permission', - loader: () => + js: () => import('../components/input-document-granular-permission/input-document-granular-permission.element.js'), meta: { entityType: 'document', diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/variant-context/document-variant-context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/variant-context/document-variant-context.ts index 2e894b719f..1d837afa4c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/variant-context/document-variant-context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/variant-context/document-variant-context.ts @@ -1,4 +1,4 @@ -import { UmbDocumentWorkspaceContext } from "../workspace/index.js"; +import type { UmbDocumentWorkspaceContext } from "../workspace/index.js"; import { DocumentVariantResponseModel, PropertyTypeModelBaseModel } from "@umbraco-cms/backoffice/backend-api"; import { UmbBaseController, UmbControllerHost } from "@umbraco-cms/backoffice/controller-api"; import { map } from "@umbraco-cms/backoffice/external/rxjs"; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-preview.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-preview.action.ts index 97dc4bdc0f..5bbecd1111 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-preview.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-preview.action.ts @@ -1,6 +1,6 @@ -import { UmbDocumentWorkspaceContext } from '../document-workspace.context.js'; +import type { UmbDocumentWorkspaceContext } from '../document-workspace.context.js'; import { UmbWorkspaceActionBase } from '@umbraco-cms/backoffice/workspace'; -import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; export class UmbDocumentSaveAndPreviewWorkspaceAction extends UmbWorkspaceActionBase { constructor(host: UmbControllerHostElement) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-publish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-publish.action.ts index 13ec47b159..622fe6a0f1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-publish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-publish.action.ts @@ -1,9 +1,9 @@ -import { UmbDocumentWorkspaceContext } from '../document-workspace.context.js'; +import type { UmbDocumentWorkspaceContext } from '../document-workspace.context.js'; import { UmbWorkspaceActionBase } from '@umbraco-cms/backoffice/workspace'; -import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; export class UmbDocumentSaveAndPublishWorkspaceAction extends UmbWorkspaceActionBase { - constructor(host: UmbControllerHostElement) { + constructor(host: UmbControllerHost) { super(host); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts index 4b201b7ac5..b9451ee8ad 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts @@ -1,9 +1,9 @@ -import { UmbDocumentWorkspaceContext } from '../document-workspace.context.js'; +import type { UmbDocumentWorkspaceContext } from '../document-workspace.context.js'; import { UmbWorkspaceActionBase } from '@umbraco-cms/backoffice/workspace'; -import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; export class UmbSaveAndScheduleDocumentWorkspaceAction extends UmbWorkspaceActionBase { - constructor(host: UmbControllerHostElement) { + constructor(host: UmbControllerHost) { super(host); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 829a9dd012..ba169380b1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -6,10 +6,9 @@ import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { UmbContentTypePropertyStructureManager } from '@umbraco-cms/backoffice/content-type'; import { UmbSaveableWorkspaceContextInterface, - UmbWorkspaceContext, + UmbEditableWorkspaceContextBase, UmbWorkspaceSplitViewManager, UmbVariantableWorkspaceContextInterface, - type UmbVariantContext, } from '@umbraco-cms/backoffice/workspace'; import type { CreateDocumentRequestModel, DocumentResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { @@ -23,7 +22,7 @@ import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; type EntityType = DocumentResponseModel; export class UmbDocumentWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbVariantableWorkspaceContextInterface { /** @@ -53,6 +52,7 @@ export class UmbDocumentWorkspaceContext readonly splitView; constructor(host: UmbControllerHostElement) { + // TODO: Get Workspace Alias via Manifest. super(host, 'Umb.Workspace.Document', new UmbDocumentRepository(host)); this.structure = new UmbContentTypePropertyStructureManager( diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.element.ts index b5394cb88f..c1d3c4e6e2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.element.ts @@ -1,4 +1,4 @@ -import { UmbDocumentWorkspaceContext } from './document-workspace.context.js'; +import type { UmbDocumentWorkspaceContext } from './document-workspace.context.js'; import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; import { html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import type { UmbRoute } from '@umbraco-cms/backoffice/router'; @@ -6,39 +6,64 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import './document-workspace-editor.element.js'; import { UmbWorkspaceIsNewRedirectController } from '@umbraco-cms/backoffice/workspace'; +import { UmbApi, UmbExtensionsApiInitializer, createExtensionApi } from '@umbraco-cms/backoffice/extension-api'; +import { ManifestWorkspace, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; @customElement('umb-document-workspace') export class UmbDocumentWorkspaceElement extends UmbLitElement { - #workspaceContext = new UmbDocumentWorkspaceContext(this); + + + #workspaceContext?: UmbDocumentWorkspaceContext; + @state() - _routes: UmbRoute[] = [ - { - path: 'create/:parentId/:documentTypeKey', - component: import('./document-workspace-editor.element.js'), - setup: async (_component, info) => { - // TODO: use parent id: - // TODO: Notice the perspective of permissions here, we need to check if the user has access to create a document of this type under this parent? - const parentId = info.match.params.parentId === 'null' ? null : info.match.params.parentId; - const documentTypeKey = info.match.params.documentTypeKey; - this.#workspaceContext.create(documentTypeKey, parentId); + _routes: UmbRoute[] = []; - new UmbWorkspaceIsNewRedirectController( - this, - this.#workspaceContext, - this.shadowRoot!.querySelector('umb-router-slot')! - ); + public set manifest(manifest: ManifestWorkspace) { + + console.log("got manifest", manifest.alias) + // TODO: Make context declaration. + + createExtensionApi(manifest, [this]).then( (context) => { + if(context) { + this.#gotWorkspaceContext(context); + } + }) + + }; + + #gotWorkspaceContext(context: UmbApi) { + this.#workspaceContext = context as UmbDocumentWorkspaceContext; + + this._routes = [ + { + path: 'create/:parentId/:documentTypeKey', + component: import('./document-workspace-editor.element.js'), + setup: async (_component, info) => { + // TODO: Remember the perspective of permissions here, we need to check if the user has access to create a document of this type under this parent? + const parentId = info.match.params.parentId === 'null' ? null : info.match.params.parentId; + const documentTypeKey = info.match.params.documentTypeKey; + this.#workspaceContext!.create(documentTypeKey, parentId); + + new UmbWorkspaceIsNewRedirectController( + this, + this.#workspaceContext!, + this.shadowRoot!.querySelector('umb-router-slot')! + ); + }, }, - }, - { - path: 'edit/:id', - component: import('./document-workspace-editor.element.js'), - setup: (_component, info) => { - const id = info.match.params.id; - this.#workspaceContext.load(id); + { + path: 'edit/:id', + component: import('./document-workspace-editor.element.js'), + setup: (_component, info) => { + const id = info.match.params.id; + this.#workspaceContext!.load(id); + }, }, - }, - ]; + ]; + + new UmbExtensionsApiInitializer(this, umbExtensionsRegistry, 'workspaceContext', [this, this.#workspaceContext]); + } render() { return html``; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts index d0afe90998..9126a710cd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts @@ -14,7 +14,8 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.Document', name: 'Document Workspace', - loader: () => import('./document-workspace.element.js'), + element: () => import('./document-workspace.element.js'), + api: () => import('./document-workspace.context.js'), meta: { entityType: UMB_DOCUMENT_ENTITY_TYPE, }, @@ -25,7 +26,7 @@ const workspaceEditorViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.Document.Edit', name: 'Document Workspace Edit View', - loader: () => import('./views/edit/document-workspace-view-edit.element.js'), + js: () => import('./views/edit/document-workspace-view-edit.element.js'), weight: 200, meta: { label: 'Content', @@ -43,7 +44,7 @@ const workspaceEditorViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.Document.Info', name: 'Document Workspace Info View', - loader: () => import('./views/info/document-info-workspace-view.element.js'), + js: () => import('./views/info/document-info-workspace-view.element.js'), weight: 100, meta: { label: 'Info', diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/umbraco-package.ts index 260460b442..aa0f708133 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/umbraco-package.ts @@ -4,6 +4,6 @@ export const extensions = [ name: 'Document Management Bundle', alias: 'Umb.Bundle.DocumentManagement', type: 'bundle', - loader: () => import('./manifests.js'), + js: () => import('./manifests.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/health-check/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/health-check/manifests.ts index b4ecc50de9..f40347abb5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/health-check/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/health-check/manifests.ts @@ -4,7 +4,7 @@ export const manifests = [ alias: 'Umb.Dashboard.HealthCheck', name: 'Health Check', elementName: 'umb-dashboard-health-check', - loader: () => import('./dashboard-health-check.element.js'), + js: () => import('./dashboard-health-check.element.js'), weight: 102, meta: { label: 'Health Check', diff --git a/src/Umbraco.Web.UI.Client/src/packages/health-check/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/health-check/umbraco-package.ts index 3d26664c83..e5010de863 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/health-check/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/health-check/umbraco-package.ts @@ -4,6 +4,6 @@ export const extensions = [ name: 'Health Check Bundle', alias: 'Umb.Bundle.HealthCheck', type: 'bundle', - loader: () => import('./manifests.js'), + js: () => import('./manifests.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/umbraco-package.ts index 88065840c9..e6bf88561d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/umbraco-package.ts @@ -4,6 +4,6 @@ export const extensions = [ name: 'Log Viewer Bundle', alias: 'Umb.Bundle.LogViewer', type: 'bundle', - loader: () => import('./manifests.js'), + js: () => import('./manifests.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer/manifests.ts index 1f18fed08a..fca448fb96 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer/manifests.ts @@ -11,7 +11,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: workspaceAlias, name: 'LogViewer Root Workspace', - loader: () => import('./logviewer-workspace.element.js'), + js: () => import('./logviewer-workspace.element.js'), meta: { entityType: 'logviewer', }, @@ -22,7 +22,7 @@ const workspaceViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.LogViewer.Overview', name: 'LogViewer Root Workspace Overview View', - loader: () => import('../views/overview/index.js'), + js: () => import('../views/overview/index.js'), weight: 300, meta: { label: 'Overview', @@ -40,7 +40,7 @@ const workspaceViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.LogViewer.Search', name: 'LogViewer Root Workspace Search View', - loader: () => import('../views/search/index.js'), + js: () => import('../views/search/index.js'), weight: 200, meta: { label: 'Search', @@ -63,7 +63,7 @@ const modals: Array = [ type: 'modal', alias: 'Umb.Modal.LogViewer.SaveSearch', name: 'Saved Searches Modal', - loader: () => import('../views/search/components/log-viewer-search-input-modal.element.js'), + js: () => import('../views/search/components/log-viewer-search-input-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/manifests.ts index 99b6c879ff..1b23b3567c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/manifests.ts @@ -11,7 +11,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.MediaType', name: 'Media Type Workspace', - loader: () => import('./media-type-workspace.element.js'), + js: () => import('./media-type-workspace.element.js'), meta: { entityType: 'media-type', }, @@ -22,7 +22,7 @@ const workspaceViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.MediaType.Design', name: 'Media Type Workspace Design View', - loader: () => import('./views/details/media-type-design-workspace-view.element.js'), + js: () => import('./views/details/media-type-design-workspace-view.element.js'), weight: 90, meta: { label: 'Details', @@ -40,7 +40,7 @@ const workspaceViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.MediaType.ListView', name: 'Media Type Workspace ListView View', - loader: () => import('./views/details/media-type-list-view-workspace-view.element.js'), + js: () => import('./views/details/media-type-list-view-workspace-view.element.js'), weight: 90, meta: { label: 'List View', @@ -58,7 +58,7 @@ const workspaceViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.MediaType.Permissions', name: 'Media Type Workspace Permissions View', - loader: () => import('./views/details/media-type-permissions-workspace-view.element.js'), + js: () => import('./views/details/media-type-permissions-workspace-view.element.js'), weight: 90, meta: { label: 'Permissions', diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context.ts index 7da542eca1..f5ababbdad 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context.ts @@ -1,5 +1,5 @@ import { UmbMediaTypeRepository } from '../repository/media-type.repository.js'; -import { UmbSaveableWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { UmbSaveableWorkspaceContextInterface, UmbEditableWorkspaceContextBase } from '@umbraco-cms/backoffice/workspace'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; @@ -7,7 +7,7 @@ import { MediaTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; type EntityType = MediaTypeResponseModel; export class UmbMediaTypeWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbSaveableWorkspaceContextInterface { #data = new UmbObjectState(undefined); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection-view/collection-view-media-test.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection-view/collection-view-media-test.element.ts index 56fb051b99..8c955554b5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection-view/collection-view-media-test.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection-view/collection-view-media-test.element.ts @@ -10,6 +10,8 @@ export class UmbCollectionViewMediaTestElement extends LitElement { static styles = [UmbTextStyles, css``]; } +export default UmbCollectionViewMediaTestElement; + declare global { interface HTMLElementTagNameMap { 'umb-collection-view-media-test': UmbCollectionViewMediaTestElement; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection-view/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection-view/manifests.ts index 25fbc5a8c3..875a88e8e4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection-view/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection-view/manifests.ts @@ -5,46 +5,49 @@ export const manifests: Array = [ type: 'collectionView', alias: 'Umb.CollectionView.MediaGrid', name: 'Media Grid Collection View', - loader: () => import('./media-grid-collection-view.element.js'), + js: () => import('./media-grid-collection-view.element.js'), weight: 300, meta: { label: 'Grid', icon: 'icon-grid', pathName: 'grid', }, - conditions: { - entityType: 'media', - }, + conditions: [{ + alias: 'Umb.Condition.WorkspaceEntityType', + match: 'media', + }], }, { type: 'collectionView', alias: 'Umb.CollectionView.MediaTable', name: 'Media Table Collection View', - loader: () => import('./media-table-collection-view.element.js'), + js: () => import('./media-table-collection-view.element.js'), weight: 200, meta: { label: 'Table', icon: 'icon-box', pathName: 'table', }, - conditions: { - entityType: 'media', - }, + conditions: [{ + alias: 'Umb.Condition.WorkspaceEntityType', + match: 'media', + }], }, { type: 'collectionView', alias: 'Umb.CollectionView.Test', name: 'Test', elementName: 'umb-collection-view-media-test', - loader: () => import('./collection-view-media-test.element.js'), + js: () => import('./collection-view-media-test.element.js'), weight: 100, meta: { label: 'Test', icon: 'icon-newspaper', pathName: 'test', }, - conditions: { - entityType: 'media', - }, + conditions: [{ + alias: 'Umb.Condition.WorkspaceEntityType', + match: 'media', + }], }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/manifests.ts index 3d04b2ed1c..e0c4ae4b7c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/manifests.ts @@ -11,7 +11,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.Media', name: 'Media Workspace', - loader: () => import('./media-workspace.element.js'), + js: () => import('./media-workspace.element.js'), meta: { entityType: 'media', }, @@ -22,7 +22,7 @@ const workspaceEditorViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.Media.Edit', name: 'Media Workspace Edit View', - loader: () => import('./views/edit/media-edit-workspace-view.element.js'), + js: () => import('./views/edit/media-edit-workspace-view.element.js'), weight: 200, meta: { label: 'Media', @@ -40,7 +40,7 @@ const workspaceEditorViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.Media.Info', name: 'Media Workspace Info View', - loader: () => import('./views/info/media-info-workspace-view.element.js'), + js: () => import('./views/info/media-info-workspace-view.element.js'), weight: 100, meta: { label: 'Info', diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts index f995eefe58..64aab3cac8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts @@ -1,13 +1,16 @@ import { UmbMediaRepository } from '../repository/media.repository.js'; import type { UmbMediaDetailModel } from '../index.js'; -import { UmbSaveableWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { + UmbSaveableWorkspaceContextInterface, + UmbEditableWorkspaceContextBase, +} from '@umbraco-cms/backoffice/workspace'; import { appendToFrozenArray, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; type EntityType = UmbMediaDetailModel; export class UmbMediaWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbSaveableWorkspaceContextInterface { #data = new UmbObjectState(undefined); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/media/umbraco-package.ts index 33cc7c2bc3..5e9568b348 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/umbraco-package.ts @@ -4,6 +4,6 @@ export const extensions = [ name: 'Media Management Bundle', alias: 'Umb.Bundle.MediaManagement', type: 'bundle', - loader: () => import('./manifests.js'), + js: () => import('./manifests.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/manifests.ts index 83d139f995..a6e5810f16 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/manifests.ts @@ -1,4 +1,5 @@ import { UMB_MEMBER_GROUP_ENTITY_TYPE } from '../entity.js'; +import { UmbSaveWorkspaceAction } from '@umbraco-cms/backoffice/workspace'; import type { ManifestWorkspace, ManifestWorkspaceAction, @@ -11,13 +12,55 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: UMB_MEMBER_GROUP_WORKSPACE_ALIAS, name: 'MemberGroup Workspace', - loader: () => import('./member-group-workspace.element.js'), + js: () => import('./member-group-workspace.element.js'), meta: { entityType: UMB_MEMBER_GROUP_ENTITY_TYPE, }, }; -const workspaceViews: Array = []; -const workspaceActions: Array = []; +const workspaceViews: Array = [ + /* + { + type: 'workspaceEditorView', + alias: 'Umb.WorkspaceView.MemberGroup.Info', + name: 'Member Group Workspace Info View', + js: () => import('./views/info/workspace-view-member-group-info.element.js'), + weight: 90, + meta: { + label: 'Info', + pathname: 'info', + icon: 'info', + }, + conditions: [ + { + alias: 'Umb.Condition.WorkspaceAlias', + match: workspace.alias, + }, + ], + }, + */ +]; + +const workspaceActions: Array = [ + /* + { + type: 'workspaceAction', + alias: 'Umb.WorkspaceAction.MemberGroup.Save', + name: 'Save Member Group Workspace Action', + api: UmbSaveWorkspaceAction, + meta: { + label: 'Save', + look: 'primary', + color: 'positive', + }, + conditions: [ + { + alias: 'Umb.Condition.WorkspaceAlias', + match: workspace.alias, + }, + ], + }, + */ +]; export const manifests = [workspace, ...workspaceViews, ...workspaceActions]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace.context.ts index da129a9079..36a48e1b06 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace.context.ts @@ -2,12 +2,15 @@ import { UmbMemberGroupDetailRepository } from '../repository/index.js'; import type { UmbMemberGroupDetailModel } from '../types.js'; import { UMB_MEMBER_GROUP_ENTITY_TYPE } from '../entity.js'; import { UMB_MEMBER_GROUP_WORKSPACE_ALIAS } from './manifests.js'; -import { UmbSaveableWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { + UmbSaveableWorkspaceContextInterface, + UmbEditableWorkspaceContextBase, +} from '@umbraco-cms/backoffice/workspace'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbMemberGroupWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbSaveableWorkspaceContextInterface { constructor(host: UmbControllerHostElement) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-types/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-types/workspace/manifests.ts index b10ff49eea..b2b3d120e2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-types/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-types/workspace/manifests.ts @@ -8,7 +8,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.MemberType', name: 'Member Type Workspace', - loader: () => import('./member-type-workspace.element.js'), + js: () => import('./member-type-workspace.element.js'), meta: { entityType: 'member-type', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-types/workspace/member-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-types/workspace/member-type-workspace.context.ts index 760ca83cb5..8063754df5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-types/workspace/member-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-types/workspace/member-type-workspace.context.ts @@ -1,5 +1,5 @@ import { UmbMemberTypeRepository } from '../repository/member-type.repository.js'; -import { UmbSaveableWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { UmbSaveableWorkspaceContextInterface, UmbEditableWorkspaceContextBase } from '@umbraco-cms/backoffice/workspace'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; @@ -8,7 +8,7 @@ import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; type EntityType = any; export class UmbMemberTypeWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbSaveableWorkspaceContextInterface { #data = new UmbObjectState(undefined); diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/members/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/members/members/workspace/manifests.ts index bd1d228b24..6480c7bffd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/members/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/members/workspace/manifests.ts @@ -11,7 +11,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: UMB_MEMBER_WORKSPACE_ALIAS, name: 'Member Workspace', - loader: () => import('./member-workspace.element.js'), + js: () => import('./member-workspace.element.js'), meta: { entityType: UMB_MEMBER_ENTITY_TYPE, }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/members/workspace/member-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/members/members/workspace/member-workspace.context.ts index c9fcce913e..986f6adc3a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/members/workspace/member-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/members/workspace/member-workspace.context.ts @@ -2,12 +2,15 @@ import { UmbMemberDetailRepository } from '../repository/index.js'; import type { UmbMemberDetailModel } from '../types.js'; import { UMB_MEMBER_ENTITY_TYPE } from '../entity.js'; import { UMB_MEMBER_WORKSPACE_ALIAS } from './manifests.js'; -import { UmbSaveableWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { + UmbSaveableWorkspaceContextInterface, + UmbEditableWorkspaceContextBase, +} from '@umbraco-cms/backoffice/workspace'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbMemberWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbSaveableWorkspaceContextInterface { constructor(host: UmbControllerHostElement) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/section.manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/members/section.manifests.ts index 5202ad4e78..fefa0409d1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/section.manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/section.manifests.ts @@ -20,7 +20,7 @@ const dashboards: Array = [ alias: 'Umb.Dashboard.Members', name: 'Members Dashboard', weight: 10, - loader: () => import('./dashboards/welcome/dashboard-members-welcome.element.js'), + js: () => import('./dashboards/welcome/dashboard-members-welcome.element.js'), meta: { label: 'Members', pathname: 'members', diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/members/umbraco-package.ts index d9f3f5ca2d..00cf4a8912 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/umbraco-package.ts @@ -4,6 +4,6 @@ export const extensions = [ name: 'Member Management Bundle', alias: 'Umb.Bundle.MemberManagement', type: 'bundle', - loader: () => import('./manifests.js'), + js: () => import('./manifests.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/packages/package-builder/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/packages/package-builder/manifests.ts index 960013f966..6596b52a70 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/packages/package-builder/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/packages/package-builder/manifests.ts @@ -9,7 +9,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.PackageBuilder', name: 'Package Builder Workspace', - loader: () => import('./workspace/workspace-package-builder.element.js'), + js: () => import('./workspace/workspace-package-builder.element.js'), meta: { entityType: 'package-builder', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/packages/package-repo/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/packages/package-repo/manifests.ts index 7ceb2f10d3..e29749ec86 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/packages/package-repo/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/packages/package-repo/manifests.ts @@ -9,7 +9,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.Package', name: 'Package Workspace', - loader: () => import('./workspace/workspace-package.element.js'), + js: () => import('./workspace/workspace-package.element.js'), meta: { entityType: 'package', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/packages/package-section/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/packages/package-section/manifests.ts index 1f9449f458..24a35bcf07 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/packages/package-section/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/packages/package-section/manifests.ts @@ -18,7 +18,7 @@ const sectionsViews: Array = [ type: 'sectionView', alias: 'Umb.SectionView.Packages.Repo', name: 'Packages Repo Section View', - loader: () => import('./views/market-place/packages-market-place-section-view.element.js'), + js: () => import('./views/market-place/packages-market-place-section-view.element.js'), weight: 300, meta: { label: 'Packages', @@ -36,7 +36,7 @@ const sectionsViews: Array = [ type: 'sectionView', alias: 'Umb.SectionView.Packages.Installed', name: 'Installed Packages Section View', - loader: () => import('./views/installed/installed-packages-section-view.element.js'), + js: () => import('./views/installed/installed-packages-section-view.element.js'), weight: 200, meta: { label: 'Installed', @@ -54,7 +54,7 @@ const sectionsViews: Array = [ type: 'sectionView', alias: 'Umb.SectionView.Packages.Builder', name: 'Packages Builder Section View', - loader: () => import('./views/created/created-packages-section-view.element.js'), + js: () => import('./views/created/created-packages-section-view.element.js'), weight: 100, meta: { label: 'Created', diff --git a/src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/package.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/package.repository.ts index 3b01a01e15..4c413502fe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/package.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/package.repository.ts @@ -1,13 +1,11 @@ import { UmbPackageStore, UMB_PACKAGE_STORE_TOKEN } from './package.store.js'; import { UmbPackageServerDataSource } from './sources/package.server.data.js'; import { UmbBaseController, type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UmbApi, isManifestJSType, ManifestBase } from '@umbraco-cms/backoffice/extension-api'; +import { UmbApi, ManifestBase, isManifestBaseType } from '@umbraco-cms/backoffice/extension-api'; import { OpenAPI } from '@umbraco-cms/backoffice/backend-api'; -// TODO: Figure out if we should base stores like this on something more generic for "collections" rather than trees. - /** - * A repository for Packages which mimicks a tree store. + * A repository for Packages which mimics a tree store. * @export */ export class UmbPackageRepository extends UmbBaseController implements UmbApi { @@ -49,16 +47,24 @@ export class UmbPackageRepository extends UmbBaseController implements UmbApi { p.extensions?.forEach((e) => { // Crudely validate that the extension at least follows a basic manifest structure // Idea: Use `Zod` to validate the manifest - if (this.isManifestBase(e)) { + if (isManifestBaseType(e)) { /** * Crude check to see if extension is of type "js" since it is safe to assume we do not * need to load any other types of extensions in the backoffice (we need a js file to load) */ - if (isManifestJSType(e)) { - // Add API base url if the js path is relative - if (!e.js.startsWith('http')) { - e.js = `${this.#apiBaseUrl}${e.js}`; - } + // Add base url if the js path is relative + if ('js' in e && typeof e.js === 'string' && !e.js.startsWith('http')) { + e.js = `${this.#apiBaseUrl}${e.js}`; + } + + // Add base url if the element path is relative + if ('element' in e && typeof e.element === 'string' && !e.element.startsWith('http')) { + e.element = `${this.#apiBaseUrl}${e.element}`; + } + + // Add base url if the element path api relative + if ('api' in e && typeof e.api === 'string' && !e.api.startsWith('http')) { + e.api = `${this.#apiBaseUrl}${e.api}`; } extensions.push(e); @@ -108,8 +114,4 @@ export class UmbPackageRepository extends UmbBaseController implements UmbApi { await this.#init; return this.#packageStore!.migrations; } - - private isManifestBase(x: unknown): x is ManifestBase { - return typeof x === 'object' && x !== null && 'alias' in x; - } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/packages/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/packages/umbraco-package.ts index 860930db14..0fea44fd6b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/packages/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/packages/umbraco-package.ts @@ -4,6 +4,6 @@ export const extensions = [ name: 'Package Management Bundle', alias: 'Umb.Bundle.PackageManagement', type: 'bundle', - loader: () => import('./manifests.js'), + js: () => import('./manifests.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/modal-views/fields-settings.element.ts b/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/modal-views/fields-settings.element.ts index 975b2d33aa..8b26180371 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/modal-views/fields-settings.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/search/examine-management-dashboard/views/modal-views/fields-settings.element.ts @@ -5,12 +5,13 @@ import { UmbExamineFieldsSettingsModalData, UmbModalBaseElement, } from '@umbraco-cms/backoffice/modal'; +import { ManifestModal, UmbModalExtensionElement } from '@umbraco-cms/backoffice/extension-registry'; @customElement('umb-examine-fields-settings-modal') -export class UmbExamineFieldsSettingsModalElement extends UmbModalBaseElement< +export default class UmbExamineFieldsSettingsModalElement extends UmbModalBaseElement< UmbExamineFieldsSettingsModalData, UmbExamineFieldsSettingsModalValue -> { +> implements UmbModalExtensionElement{ @state() private _fields?: UmbExamineFieldsSettingsModalData; diff --git a/src/Umbraco.Web.UI.Client/src/packages/search/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/search/manifests.ts index f75e4cf10a..6e772b8731 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/search/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/search/manifests.ts @@ -5,7 +5,7 @@ export const manifests: Array = [ type: 'headerApp', alias: 'Umb.HeaderApp.Search', name: 'Header App Search', - loader: () => import('./umb-search-header-app.element.js'), + js: () => import('./umb-search-header-app.element.js'), weight: 900, meta: { label: 'Search', @@ -17,14 +17,14 @@ export const manifests: Array = [ type: 'modal', alias: 'Umb.Modal.Search', name: 'Search Modal', - loader: () => import('./search-modal/search-modal.element.js'), + js: () => import('./search-modal/search-modal.element.js'), }, { type: 'dashboard', alias: 'Umb.Dashboard.ExamineManagement', name: 'Examine Management Dashboard', elementName: 'umb-dashboard-examine-management', - loader: () => import('./examine-management-dashboard/dashboard-examine-management.element.js'), + js: () => import('./examine-management-dashboard/dashboard-examine-management.element.js'), weight: 400, meta: { label: 'Examine Management', @@ -41,6 +41,6 @@ export const manifests: Array = [ type: 'modal', alias: 'Umb.Modal.ExamineFieldsSettings', name: 'Examine Field Settings Modal', - loader: () => import('./examine-management-dashboard/views/modal-views/fields-settings.element.js'), + js: () => import('./examine-management-dashboard/views/modal-views/fields-settings.element.js'), }, -]; +]; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/search/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/search/umbraco-package.ts index 19cfeaa65d..d669951eaa 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/search/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/search/umbraco-package.ts @@ -4,6 +4,6 @@ export const extensions = [ name: 'Search Bundle', alias: 'Umb.Bundle.Search', type: 'bundle', - loader: () => import('./manifests.js'), + js: () => import('./manifests.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts index d2313cc203..7bb4f3c0e3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/manifests.ts @@ -8,7 +8,7 @@ const dashboards: Array = [ alias: 'Umb.Dashboard.SettingsWelcome', name: 'Welcome Settings Dashboard', elementName: 'umb-dashboard-settings-welcome', - loader: () => import('./settings-welcome/dashboard-settings-welcome.element.js'), + js: () => import('./settings-welcome/dashboard-settings-welcome.element.js'), weight: 500, meta: { label: 'Welcome', @@ -26,7 +26,7 @@ const dashboards: Array = [ alias: 'Umb.Dashboard.ModelsBuilder', name: 'Models Builder Dashboard', elementName: 'umb-dashboard-models-builder', - loader: () => import('./models-builder/dashboard-models-builder.element.js'), + js: () => import('./models-builder/dashboard-models-builder.element.js'), weight: 300, meta: { label: 'Models Builder', @@ -44,7 +44,7 @@ const dashboards: Array = [ alias: 'Umb.Dashboard.PublishedStatus', name: 'Published Status Dashboard', elementName: 'umb-dashboard-published-status', - loader: () => import('./published-status/dashboard-published-status.element.js'), + js: () => import('./published-status/dashboard-published-status.element.js'), weight: 200, meta: { label: 'Published Status', @@ -62,7 +62,7 @@ const dashboards: Array = [ alias: 'Umb.Dashboard.Profiling', name: 'Profiling', elementName: 'umb-dashboard-performance-profiling', - loader: () => import('./performance-profiling/dashboard-performance-profiling.element.js'), + js: () => import('./performance-profiling/dashboard-performance-profiling.element.js'), weight: 101, meta: { label: 'Profiling', @@ -80,7 +80,7 @@ const dashboards: Array = [ alias: 'Umb.Dashboard.Telemetry', name: 'Telemetry', elementName: 'umb-dashboard-telemetry', - loader: () => import('./telemetry/dashboard-telemetry.element.js'), + js: () => import('./telemetry/dashboard-telemetry.element.js'), weight: 100, meta: { label: 'Telemetry Data', diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/models-builder/dashboard-models-builder.element.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/models-builder/dashboard-models-builder.element.ts index e20f8bb740..c775b14110 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/models-builder/dashboard-models-builder.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/dashboards/models-builder/dashboard-models-builder.element.ts @@ -170,6 +170,9 @@ export class UmbDashboardModelsBuilderElement extends UmbLitElement { ]; } + +export default UmbDashboardModelsBuilderElement; + declare global { interface HTMLElementTagNameMap { 'umb-dashboard-models-builder': UmbDashboardModelsBuilderElement; diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/extensions/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/extensions/workspace/manifests.ts index 02adb59cb3..66c8d376a7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/extensions/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/extensions/workspace/manifests.ts @@ -8,7 +8,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.ExtensionRoot', name: 'Extension Root Workspace', - loader: () => import('./extension-root-workspace.element.js'), + js: () => import('./extension-root-workspace.element.js'), meta: { entityType: 'extension-root', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/app-language-select/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/app-language-select/manifests.ts index b305273eb4..0b8ec9fa4d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/app-language-select/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/app-language-select/manifests.ts @@ -5,13 +5,13 @@ const entityActions: Array = [ type: 'globalContext', alias: 'Umb.GlobalContext.AppLanguage', name: 'App Language Context', - loader: () => import('./app-language.context.js'), + js: () => import('./app-language.context.js'), }, { type: 'sectionSidebarApp', alias: 'Umb.SectionSidebarItem.LanguageSelect', name: 'App Language Select Section Sidebar Item', - loader: () => import('./app-language-select.element.js'), + js: () => import('./app-language-select.element.js'), weight: 900, conditions: [ { diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/modals/manifests.ts index da1c68abcd..7f415a3e19 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/modals/manifests.ts @@ -5,7 +5,7 @@ const modals: Array = [ type: 'modal', alias: 'Umb.Modal.LanguagePicker', name: 'Language Picker Modal', - loader: () => import('./language-picker/language-picker-modal.element.js'), + js: () => import('./language-picker/language-picker-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language-root/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language-root/manifests.ts index ce8e2bc4ad..422bf207cc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language-root/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language-root/manifests.ts @@ -8,7 +8,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.LanguageRoot', name: 'Language Root Workspace', - loader: () => import('./language-root-workspace.element.js'), + js: () => import('./language-root-workspace.element.js'), meta: { entityType: 'language-root', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/language-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/language-workspace.context.ts index ba5ea55a24..9c6626a865 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/language-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/language-workspace.context.ts @@ -1,12 +1,12 @@ import { UmbLanguageRepository } from '../../repository/language.repository.js'; -import { UmbSaveableWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { UmbSaveableWorkspaceContextInterface, UmbEditableWorkspaceContextBase } from '@umbraco-cms/backoffice/workspace'; import { ApiError, LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbLanguageWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbSaveableWorkspaceContextInterface { #data = new UmbObjectState(undefined); diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/manifests.ts index fb4abedcfd..36e2637ca6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/manifests.ts @@ -9,7 +9,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.Language', name: 'Language Workspace', - loader: () => import('./language-workspace.element.js'), + js: () => import('./language-workspace.element.js'), meta: { entityType: 'language', }, @@ -20,7 +20,7 @@ const workspaceViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.Language.Details', name: 'Language Workspace Details View', - loader: () => import('./views/details/language-details-workspace-view.element.js'), + js: () => import('./views/details/language-details-workspace-view.element.js'), weight: 90, meta: { label: 'Details', diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/manifests.ts index b9d478a1c1..f0461fb833 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/manifests.ts @@ -9,7 +9,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.RelationType', name: 'Relation Type Workspace', - loader: () => import('./relation-type-workspace.element.js'), + js: () => import('./relation-type-workspace.element.js'), meta: { entityType: 'relation-type', }, @@ -20,7 +20,7 @@ const workspaceViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.RelationType.RelationType', name: 'Relation Type Workspace RelationType View', - loader: () => import('./views/relation-type/relation-type-workspace-view-relation-type.element.js'), + js: () => import('./views/relation-type/relation-type-workspace-view-relation-type.element.js'), weight: 20, meta: { label: 'RelationType', @@ -38,7 +38,7 @@ const workspaceViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.RelationType.Relation', name: 'Relation Type Workspace Relation View', - loader: () => import('./views/relation/workspace-view-relation-type-relation.element.js'), + js: () => import('./views/relation/workspace-view-relation-type-relation.element.js'), weight: 10, meta: { label: 'Relation', diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/relation-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/relation-type-workspace.context.ts index 11d85995f4..237d6a83a5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/relation-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/relation-type-workspace.context.ts @@ -1,12 +1,12 @@ import { UmbRelationTypeRepository } from '../repository/relation-type.repository.js'; -import { UmbSaveableWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { UmbSaveableWorkspaceContextInterface, UmbEditableWorkspaceContextBase } from '@umbraco-cms/backoffice/workspace'; import type { RelationTypeBaseModel, RelationTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbRelationTypeWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbSaveableWorkspaceContextInterface { #data = new UmbObjectState(undefined); diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/umbraco-package.ts index 3d01bae2f8..87a8be3eec 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/umbraco-package.ts @@ -4,6 +4,6 @@ export const extensions = [ name: 'Settings Bundle', alias: 'Umb.Bundle.Settings', type: 'bundle', - loader: () => import('./manifests.js'), + js: () => import('./manifests.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/config/storage-type/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/config/storage-type/manifests.ts index 979b9b3994..15692889bf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/config/storage-type/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/config/storage-type/manifests.ts @@ -5,7 +5,7 @@ export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.Tags.StorageType', name: 'Tags Storage Type Property Editor UI', - loader: () => import('./property-editor-ui-tags-storage-type.element.js'), + js: () => import('./property-editor-ui-tags-storage-type.element.js'), meta: { label: 'Tags Storage Type', propertyEditorSchemaAlias: '', diff --git a/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/config/storage-type/property-editor-ui-tags-storage-type.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/config/storage-type/property-editor-ui-tags-storage-type.element.ts index 32f491ca9c..7007becdeb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/config/storage-type/property-editor-ui-tags-storage-type.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/config/storage-type/property-editor-ui-tags-storage-type.element.ts @@ -1,4 +1,6 @@ +import { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -6,12 +8,12 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; * @element umb-property-editor-ui-tags-storage-type */ @customElement('umb-property-editor-ui-tags-storage-type') -export class UmbPropertyEditorUITagsStorageTypeElement extends UmbLitElement { +export class UmbPropertyEditorUITagsStorageTypeElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property() value = ''; @property({ type: Array, attribute: false }) - public config = []; + public config?: UmbPropertyEditorConfigCollection; render() { return html`
umb-property-editor-ui-tags-storage-type
`; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/manifests.ts index 66d19b9ffe..2d5aa1437e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tags/property-editors/tags/manifests.ts @@ -5,7 +5,7 @@ const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.Tags', name: 'Tags Property Editor UI', - loader: () => import('./property-editor-ui-tags.element.js'), + js: () => import('./property-editor-ui-tags.element.js'), meta: { label: 'Tags', propertyEditorSchemaAlias: 'Umbraco.Tags', diff --git a/src/Umbraco.Web.UI.Client/src/packages/tags/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/tags/umbraco-package.ts index 98bf4437db..5706abda93 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tags/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tags/umbraco-package.ts @@ -5,6 +5,6 @@ export const extensions = [ name: 'Tags Management Bundle', alias: 'Umb.Bundle.TagsManagement', type: 'bundle', - loader: () => import('./manifests.js'), + js: () => import('./manifests.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/code-editor/code-editor.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/code-editor/code-editor.controller.ts index 3ce2e64817..f30c2e42a4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/code-editor/code-editor.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/code-editor/code-editor.controller.ts @@ -97,9 +97,9 @@ export class UmbCodeEditorController { set value(newValue: string) { if (!this.#editor) throw new Error('Editor object not found'); - const oldValue = this.value; + const oldValue = this.value ?? ''; if (newValue !== oldValue) { - this.#editor.setValue(newValue); + this.#editor.setValue(newValue ?? ''); } } /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/manifests.ts index d363d7ee17..fefe77b850 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/manifests.ts @@ -9,19 +9,19 @@ const modals: Array = [ type: 'modal', alias: UMB_MODAL_TEMPLATING_INSERT_CHOOSE_TYPE_SIDEBAR_ALIAS, name: 'Choose insert type sidebar', - loader: () => import('./insert-choose-type-sidebar.element.js'), + js: () => import('./insert-choose-type-sidebar.element.js'), }, { type: 'modal', alias: UMB_PARTIAL_VIEW_PICKER_MODAL_ALIAS, name: 'Partial View Picker Modal', - loader: () => import('../../templating/modals/partial-view-picker-modal.element.js'), + js: () => import('../../templating/modals/partial-view-picker-modal.element.js'), }, { type: 'modal', alias: UMB_MODAL_TEMPLATING_INSERT_SECTION_SIDEBAR_ALIAS, name: 'Partial Insert Section Picker Modal', - loader: () => import('./insert-section-modal/insert-section-modal.element.js'), + js: () => import('./insert-section-modal/insert-section-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/manifests.ts index bcab50cf10..c0a9a4376d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/manifests.ts @@ -94,7 +94,7 @@ const createFromSnippetActionModal = { type: 'modal', alias: 'Umb.Modal.CreateFromSnippetPartialView', name: 'Choose insert type sidebar', - loader: () => import('./create/create-from-snippet.modal.js'), + js: () => import('./create/create-from-snippet.modal.js'), }; export const manifests = [...partialViewActions, ...partialViewFolderActions, createFromSnippetActionModal]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/manifests.ts index aa4bdfc898..2053e4feba 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/manifests.ts @@ -5,7 +5,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.PartialView', name: 'Partial View Workspace', - loader: () => import('./partial-view-workspace.element.js'), + js: () => import('./partial-view-workspace.element.js'), meta: { entityType: 'partial-view', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts index d2e032cf2e..0b925b21c6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts @@ -1,21 +1,25 @@ import { UmbPartialViewRepository } from '../repository/partial-view.repository.js'; import { UmbPartialViewDetailModel } from '../types.js'; +import { UMB_PARTIAL_VIEW_ENTITY_TYPE } from '../entity.js'; import { UmbBooleanState, UmbDeepState } from '@umbraco-cms/backoffice/observable-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; -import { UmbSaveableWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { + UmbSaveableWorkspaceContextInterface, + UmbEditableWorkspaceContextBase, +} from '@umbraco-cms/backoffice/workspace'; import { loadCodeEditor } from '@umbraco-cms/backoffice/code-editor'; import { UpdatePartialViewRequestModel } from '@umbraco-cms/backoffice/backend-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbPartialViewWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbSaveableWorkspaceContextInterface { getEntityId(): string | undefined { return this.getData()?.path; } getEntityType(): string { - return 'partial-view'; + return UMB_PARTIAL_VIEW_ENTITY_TYPE; } save(): Promise { const partialView = this.getData(); @@ -103,5 +107,5 @@ export const UMB_PARTIAL_VIEW_WORKSPACE_CONTEXT = new UmbContextToken< UmbPartialViewWorkspaceContext >( 'UmbWorkspaceContext', - (context): context is UmbPartialViewWorkspaceContext => context.getEntityType?.() === 'partial-view', + (context): context is UmbPartialViewWorkspaceContext => context.getEntityType?.() === UMB_PARTIAL_VIEW_ENTITY_TYPE, ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/manifests.ts index 4391a1062b..0cf0a7d1d7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/manifests.ts @@ -9,7 +9,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: UMB_SCRIPT_WORKSPACE_ALIAS, name: 'Script Workspace', - loader: () => import('./script-workspace.element.js'), + js: () => import('./script-workspace.element.js'), meta: { entityType: UMB_SCRIPT_ENTITY_TYPE, }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace.context.ts index e95775a540..5bf4c668a5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace.context.ts @@ -3,11 +3,14 @@ import { UmbScriptRepository } from '../repository/script.repository.js'; import { UMB_SCRIPT_WORKSPACE_ALIAS } from './manifests.js'; import { UmbBooleanState, UmbDeepState } from '@umbraco-cms/backoffice/observable-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; -import { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { UmbEditableWorkspaceContextBase } from '@umbraco-cms/backoffice/workspace'; import { loadCodeEditor } from '@umbraco-cms/backoffice/code-editor'; import { TextFileResponseModelBaseModel, UpdateScriptRequestModel } from '@umbraco-cms/backoffice/backend-api'; -export class UmbScriptWorkspaceContext extends UmbWorkspaceContext { +export class UmbScriptsWorkspaceContext extends UmbEditableWorkspaceContextBase< + UmbScriptRepository, + UmbScripDetailModel +> { #data = new UmbDeepState(undefined); data = this.#data.asObservable(); name = this.#data.asObservablePart((data) => data?.name); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/manifests.ts index 6795b5acf6..57f2961fe8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/manifests.ts @@ -10,7 +10,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.StyleSheet', name: 'Stylesheet Workspace', - loader: () => import('./stylesheet-workspace.element.js'), + js: () => import('./stylesheet-workspace.element.js'), meta: { entityType: 'stylesheet', }, @@ -21,7 +21,7 @@ const workspaceEditorViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.Stylesheet.CodeEditor', name: 'Stylesheet Workspace Code Editor View', - loader: () => import('./views/code-editor/stylesheet-workspace-view-code-editor.element.js'), + js: () => import('./views/code-editor/stylesheet-workspace-view-code-editor.element.js'), weight: 700, meta: { label: 'Code', @@ -39,7 +39,7 @@ const workspaceEditorViews: Array = [ type: 'workspaceEditorView', alias: 'Umb.WorkspaceView.Stylesheet.RichTextEditor', name: 'Stylesheet Workspace Rich Text Editor View', - loader: () => import('./views/rich-text-editor/stylesheet-workspace-view-rich-text-editor.element.js'), + js: () => import('./views/rich-text-editor/stylesheet-workspace-view-rich-text-editor.element.js'), weight: 800, meta: { label: 'Rich Text Editor', @@ -82,7 +82,7 @@ const modals: Array = [ type: 'modal', alias: UMB_MODAL_TEMPLATING_STYLESHEET_RTF_STYLE_SIDEBAR, name: 'Rich text editor style modal', - loader: () => + js: () => import('./views/rich-text-editor/stylesheet-workspace-view-rich-text-editor-style-sidebar.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts index 480c6be472..1ee3e2e599 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts @@ -1,6 +1,6 @@ import { UmbStylesheetRepository } from '../repository/stylesheet.repository.js'; import { StylesheetDetails } from '../index.js'; -import { UmbSaveableWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { UmbSaveableWorkspaceContextInterface, UmbEditableWorkspaceContextBase } from '@umbraco-cms/backoffice/workspace'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbArrayState, UmbBooleanState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import { loadCodeEditor } from '@umbraco-cms/backoffice/code-editor'; @@ -10,7 +10,7 @@ import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export type RichTextRuleModelSortable = RichTextRuleModel & { sortOrder?: number }; export class UmbStylesheetWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbSaveableWorkspaceContextInterface { #data = new UmbObjectState(undefined); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/modals/manifests.ts index 5a506a54ec..3262600c7e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/modals/manifests.ts @@ -7,7 +7,7 @@ const modals: Array = [ type: 'modal', alias: UMB_MODAL_TEMPLATING_QUERY_BUILDER_SIDEBAR_ALIAS, name: 'Template query builder', - loader: () => import('./query-builder/query-builder.element.js'), + js: () => import('./query-builder/query-builder.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/manifests.ts index 08cc983f92..def1036755 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/manifests.ts @@ -9,7 +9,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.Template', name: 'Template Workspace', - loader: () => import('./template-workspace.element.js'), + js: () => import('./template-workspace.element.js'), meta: { entityType: 'template', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context.ts index 7638c10b05..ae77ddbd4b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context.ts @@ -1,14 +1,17 @@ import { UmbTemplateRepository } from '../repository/index.js'; import { UmbTemplateTreeRepository } from '../tree/index.js'; import { loadCodeEditor } from '@umbraco-cms/backoffice/code-editor'; -import { UmbSaveableWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { + UmbSaveableWorkspaceContextInterface, + UmbEditableWorkspaceContextBase, +} from '@umbraco-cms/backoffice/workspace'; import { UmbBooleanState, UmbDeepState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import type { TemplateItemResponseModel, TemplateResponseModel } from '@umbraco-cms/backoffice/backend-api'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbTemplateWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbSaveableWorkspaceContextInterface { #data = new UmbDeepState(undefined); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/umbraco-package.ts index f5925d9135..7c36d4038e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/umbraco-package.ts @@ -4,6 +4,6 @@ export const extensions = [ name: 'Template Management Bundle', alias: 'Umb.Bundle.TemplateManagement', type: 'bundle', - loader: () => import('./manifests.js'), + js: () => import('./manifests.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/manifests.ts index 079cf078d5..28644423d9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/manifests.ts @@ -4,7 +4,7 @@ export const dashboard: ManifestDashboard = { type: 'dashboard', alias: 'Umb.Dashboard.UmbracoNews', name: 'Umbraco News Dashboard', - loader: () => import('./umbraco-news-dashboard.element.js'), + js: () => import('./umbraco-news-dashboard.element.js'), weight: 20, meta: { label: 'Welcome', diff --git a/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-package.ts index 0439c11cd4..dd74ae2886 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-package.ts @@ -4,6 +4,6 @@ export const extensions = [ name: 'Umbraco News Bundle', alias: 'Umb.Bundle.UmbracoNews', type: 'bundle', - loader: () => import('./manifests.js'), + js: () => import('./manifests.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/manifests.ts index aae734cfa3..9b279495bb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/manifests.ts @@ -8,7 +8,7 @@ export const headerApps: Array = [ type: 'store', alias: 'Umb.Store.CurrentUser', name: 'Current User Store', - loader: () => import('./current-user-history.store.js'), + js: () => import('./current-user-history.store.js'), }, { type: 'globalContext', @@ -20,7 +20,7 @@ export const headerApps: Array = [ type: 'headerApp', alias: 'Umb.HeaderApp.CurrentUser', name: 'Current User', - loader: () => import('./current-user-header-app.element.js'), + js: () => import('./current-user-header-app.element.js'), weight: 0, meta: { label: 'TODO: how should we enable this to not be set.', diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/manifests.ts index b2fc561678..56ff4f54b3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/manifests.ts @@ -5,7 +5,7 @@ const modals: Array = [ type: 'modal', alias: 'Umb.Modal.User.Current', name: 'Current User Modal', - loader: () => import('./current-user/current-user-modal.element.js'), + js: () => import('./current-user/current-user-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/user-profile-apps/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/user-profile-apps/manifests.ts index 7fd6433ee6..bc3f9d482a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/user-profile-apps/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/user-profile-apps/manifests.ts @@ -5,7 +5,7 @@ export const userProfileApps: Array = [ type: 'userProfileApp', alias: 'Umb.UserProfileApp.profile', name: 'Profile User Profile App', - loader: () => import('./user-profile-app-profile.element.js'), + js: () => import('./user-profile-app-profile.element.js'), weight: 900, meta: { label: 'Profile User Profile App', @@ -16,7 +16,7 @@ export const userProfileApps: Array = [ type: 'userProfileApp', alias: 'Umb.UserProfileApp.ExternalLoginProviders', name: 'External Login Providers User Profile App', - loader: () => import('./user-profile-app-external-login-providers.element.js'), + js: () => import('./user-profile-app-external-login-providers.element.js'), weight: 800, meta: { label: 'External Login Providers User Profile App', @@ -27,7 +27,7 @@ export const userProfileApps: Array = [ type: 'userProfileApp', alias: 'Umb.UserProfileApp.Themes', name: 'Themes User Profile App', - loader: () => import('./user-profile-app-themes.element.js'), + js: () => import('./user-profile-app-themes.element.js'), weight: 200, meta: { label: 'Themes User Profile App', @@ -38,7 +38,7 @@ export const userProfileApps: Array = [ type: 'userProfileApp', alias: 'Umb.UserProfileApp.History', name: 'History User Profile App', - loader: () => import('./user-profile-app-history.element.js'), + js: () => import('./user-profile-app-history.element.js'), weight: 100, meta: { label: 'History User Profile App', diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/modals/manifests.ts index 5154cb2bab..aa086f1d21 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/modals/manifests.ts @@ -5,7 +5,7 @@ const modals: Array = [ type: 'modal', alias: 'Umb.Modal.ChangePassword', name: 'Change Password Modal', - loader: () => import('./change-password/change-password-modal.element.js'), + js: () => import('./change-password/change-password-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/user/umbraco-package.ts index 7418be463d..21c953c36f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/umbraco-package.ts @@ -5,6 +5,6 @@ export const extensions = [ name: 'User Management Bundle', alias: 'Umb.Bundle.UserManagement', type: 'bundle', - loader: () => import('./manifests.js'), + js: () => import('./manifests.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/modals/manifests.ts index 3ca7f7c040..82fd2b5cba 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/modals/manifests.ts @@ -5,7 +5,7 @@ const modals: Array = [ type: 'modal', alias: 'Umb.Modal.UserGroupPicker', name: 'User Group Picker Modal', - loader: () => import('./user-group-picker/user-group-picker-modal.element.js'), + js: () => import('./user-group-picker/user-group-picker-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/section-view/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/section-view/manifests.ts index 91ba844f04..a32081ef3b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/section-view/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/section-view/manifests.ts @@ -6,7 +6,7 @@ const sectionsViews: Array = [ type: 'sectionView', alias: 'Umb.SectionView.UserGroups', name: 'User Groups Section View', - loader: () => import('./user-groups-section-view.element.js'), + js: () => import('./user-groups-section-view.element.js'), weight: 100, meta: { label: 'User Groups', diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/manifests.ts index f55239e92f..58ba05612a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/manifests.ts @@ -9,7 +9,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.UserGroup', name: 'User Group Workspace', - loader: () => import('./user-group-workspace.element.js'), + js: () => import('./user-group-workspace.element.js'), meta: { entityType: 'user-group', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace.context.ts index 04d94822d8..07af16b3a1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace.context.ts @@ -1,13 +1,13 @@ import { UmbUserGroupRepository } from '../repository/user-group.repository.js'; import { UmbUserRepository } from '../../user/repository/user.repository.js'; -import { UmbSaveableWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { UmbSaveableWorkspaceContextInterface, UmbEditableWorkspaceContextBase } from '@umbraco-cms/backoffice/workspace'; import type { UserGroupResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { UmbArrayState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbUserGroupWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbSaveableWorkspaceContextInterface { #data = new UmbObjectState(undefined); diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/modals/manifests.ts index ca7d2173bf..152cdbe431 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-permission/modals/manifests.ts @@ -3,6 +3,6 @@ export const manifests = [ type: 'modal', alias: 'Umb.Modal.EntityUserPermissionSettings', name: 'Entity User Permission Settings Modal', - loader: () => import('./entity-user-permission-settings/entity-user-permission-settings-modal.element.js'), + js: () => import('./entity-user-permission-settings/entity-user-permission-settings-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/views/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/views/manifests.ts index 0ddd3ac0b1..5b10cbd2c2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/views/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/views/manifests.ts @@ -5,31 +5,33 @@ const tableCollectionView: ManifestCollectionView = { type: 'collectionView', alias: 'Umb.CollectionView.UserTable', name: 'User Table Collection Collection View', - loader: () => import('./table/user-table-collection-view.element.js'), + js: () => import('./table/user-table-collection-view.element.js'), meta: { label: 'Table', icon: 'icon-box', pathName: 'table', }, - conditions: { - entityType: UMB_USER_ENTITY_TYPE, - }, + conditions: [{ + alias: 'Umb.Condition.WorkspaceEntityType', + match: UMB_USER_ENTITY_TYPE, + }], }; const gridCollectionView: ManifestCollectionView = { type: 'collectionView', alias: 'Umb.CollectionView.UserGrid', name: 'Media Table Collection View', - loader: () => import('./grid/user-grid-collection-view.element.js'), + js: () => import('./grid/user-grid-collection-view.element.js'), weight: 200, meta: { label: 'Grid', icon: 'icon-grid', pathName: 'grid', }, - conditions: { - entityType: UMB_USER_ENTITY_TYPE, - }, + conditions: [{ + alias: 'Umb.Condition.WorkspaceEntityType', + match: UMB_USER_ENTITY_TYPE, + }], }; export const manifests = [tableCollectionView, gridCollectionView]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/manifests.ts index 629257a3e5..b8bde4b0a9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/modals/manifests.ts @@ -5,31 +5,31 @@ const modals: Array = [ type: 'modal', alias: 'Umb.Modal.User.Create', name: 'Create User Modal', - loader: () => import('./create/user-create-modal.element.js'), + js: () => import('./create/user-create-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.User.CreateSuccess', name: 'Create Success User Modal', - loader: () => import('./create/user-create-success-modal.element.js'), + js: () => import('./create/user-create-success-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.User.Invite', name: 'Invite User Modal', - loader: () => import('./invite/user-invite-modal.element.js'), + js: () => import('./invite/user-invite-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.User.ResendInvite', name: 'Resend Invite to User Modal', - loader: () => import('./resend-invite/resend-invite-to-user-modal.element.js'), + js: () => import('./resend-invite/resend-invite-to-user-modal.element.js'), }, { type: 'modal', alias: 'Umb.Modal.User.Picker', name: 'User Picker Modal', - loader: () => import('./user-picker/user-picker-modal.element.js'), + js: () => import('./user-picker/user-picker-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/section-view/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/section-view/manifests.ts index 1990276631..3db7ef495c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/section-view/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/section-view/manifests.ts @@ -6,7 +6,7 @@ const sectionsViews: Array = [ type: 'sectionView', alias: 'Umb.SectionView.Users', name: 'Users Section View', - loader: () => import('./users-section-view.element.js'), + js: () => import('./users-section-view.element.js'), weight: 200, meta: { label: 'Users', diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/manifests.ts index 87a3718fd2..001b4b2769 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/manifests.ts @@ -10,7 +10,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.User', name: 'User Workspace', - loader: () => import('./user-workspace.element.js'), + element: () => import('./user-workspace.element.js'), meta: { entityType: USER_ENTITY_TYPE, }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context.ts index 030e4d1c03..a20645ad06 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context.ts @@ -1,6 +1,6 @@ import { UmbUserRepository } from '../repository/user.repository.js'; import { USER_ENTITY_TYPE, type UmbUserDetail } from '../index.js'; -import { UmbSaveableWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { UmbSaveableWorkspaceContextInterface, UmbEditableWorkspaceContextBase } from '@umbraco-cms/backoffice/workspace'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import type { UpdateUserRequestModel } from '@umbraco-cms/backoffice/backend-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; @@ -9,7 +9,7 @@ import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user'; import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs'; export class UmbUserWorkspaceContext - extends UmbWorkspaceContext + extends UmbEditableWorkspaceContextBase implements UmbSaveableWorkspaceContextInterface { #currentUserContext?: typeof UMB_CURRENT_USER_CONTEXT.TYPE; diff --git a/src/Umbraco.Web.UI.Client/storybook/stories/extending/modals/intro.mdx b/src/Umbraco.Web.UI.Client/storybook/stories/extending/modals/intro.mdx index de34a1766b..6b62f53adc 100644 --- a/src/Umbraco.Web.UI.Client/storybook/stories/extending/modals/intro.mdx +++ b/src/Umbraco.Web.UI.Client/storybook/stories/extending/modals/intro.mdx @@ -19,7 +19,7 @@ import { Meta } from '@storybook/addon-docs'; "type": "modal", "alias": "Our.Modal.SomethingPicker", "name": "My Something Picker Modal", - "loader": "./my-something-picker-modal-element.js", + "js": "./my-something-picker-modal-element.js", }, ``` @@ -27,7 +27,7 @@ import { Meta } from '@storybook/addon-docs'; ### Modal Token -For type safety we recommend that you make a Modal Token, Its posible to go without. +For type safety, we recommend that you make a Modal Token, It's possible to go without. The Modal Token binds the Modal Type to the Modal Data Type and Modal Value Type. `` diff --git a/src/Umbraco.Web.UI.Client/storybook/stories/extending/property-editors.mdx b/src/Umbraco.Web.UI.Client/storybook/stories/extending/property-editors.mdx index f84024f781..a1b5840d54 100644 --- a/src/Umbraco.Web.UI.Client/storybook/stories/extending/property-editors.mdx +++ b/src/Umbraco.Web.UI.Client/storybook/stories/extending/property-editors.mdx @@ -102,11 +102,11 @@ interface UmbPropertyEditorUIElement {} import { LitElement, html, css } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { UUITextStyles } from '@umbraco-ui/uui-base/lib/styles'; -import { UmbElement } from '@umbraco-cms/element'; +import { UmbElementMixin } from '@umbraco-cms/element'; // TODO: should we make examples with LitElement or just vanilla JS? or should we have for more libraries? @customElement('my-text-box') -export class UmbPropertyEditorUITextBoxElement extends UmbElement(LitElement) { +export class UmbPropertyEditorUITextBoxElement extends UmbElementMixin(LitElement) implements UmbPropertyEditorUIElement { static styles = [ UUITextStyles, css` @@ -119,8 +119,8 @@ export class UmbPropertyEditorUITextBoxElement extends UmbElement(LitElement) { @property() value = ''; - @property({ type: Array, attribute: false }) - public config = []; + @property({ type: Object, attribute: false }) + public config?: UmbPropertyEditorConfigCollection; private onInput(e: InputEvent) { this.value = (e.target as HTMLInputElement).value;