From ee023b50725587faa84d4c1e83c51c18367428ea Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:13:14 +0200 Subject: [PATCH 01/39] add a `scope` on entrypoints and bundles and let the initializers look for that scope before trying to load the extension --- .../bundle-extension-initializer.ts | 42 ++++++++++++------- .../entry-point-extension-initializer.ts | 28 +++++++++---- .../types/manifest-bundle.interface.ts | 8 ++++ .../types/manifest-entrypoint.interface.ts | 8 ++++ 4 files changed, 61 insertions(+), 25 deletions(-) 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 04d1ac3e8d..5117c055cb 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 @@ -8,25 +8,35 @@ export class UmbBundleExtensionInitializer extends UmbControllerBase { #extensionRegistry; #bundleMap = new Map(); - constructor(host: UmbControllerHost, extensionRegistry: UmbExtensionRegistry) { + constructor( + host: UmbControllerHost, + extensionRegistry: UmbExtensionRegistry, + scope: 'global' | 'local' = 'local', + ) { super(host); this.#extensionRegistry = extensionRegistry; - this.observe(extensionRegistry.byType('bundle'), (bundles) => { - // Unregister removed bundles: - this.#bundleMap.forEach((existingBundle) => { - if (!bundles.find((b) => b.alias === existingBundle.alias)) { - this.unregisterBundle(existingBundle); - this.#bundleMap.delete(existingBundle.alias); - } - }); + this.observe( + extensionRegistry.byTypeAndFilter('bundle', (ext) => { + const extScope = ext.scope || 'local'; + return extScope === scope; + }), + (bundles) => { + // Unregister removed bundles: + this.#bundleMap.forEach((existingBundle) => { + if (!bundles.find((b) => b.alias === existingBundle.alias)) { + this.unregisterBundle(existingBundle); + this.#bundleMap.delete(existingBundle.alias); + } + }); - // Register new bundles: - bundles.forEach((bundle) => { - if (this.#bundleMap.has(bundle.alias)) return; - this.#bundleMap.set(bundle.alias, bundle); - this.instantiateBundle(bundle); - }); - }); + // Register new bundles: + bundles.forEach((bundle) => { + if (this.#bundleMap.has(bundle.alias)) return; + this.#bundleMap.set(bundle.alias, bundle); + this.instantiateBundle(bundle); + }); + }, + ); } async instantiateBundle(manifest: ManifestBundle) { 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 a1af6f23c2..4b015a50fe 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 @@ -9,18 +9,28 @@ export class UmbEntryPointExtensionInitializer extends UmbControllerBase { #extensionRegistry; #entryPointMap = new Map(); - constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry) { + constructor( + host: UmbElement, + extensionRegistry: UmbExtensionRegistry, + scope: 'global' | 'local' = 'local', + ) { super(host); this.#host = host; this.#extensionRegistry = extensionRegistry; - this.observe(extensionRegistry.byType('entryPoint'), (entryPoints) => { - entryPoints.forEach((entryPoint) => { - if (this.#entryPointMap.has(entryPoint.alias)) return; - this.#entryPointMap.set(entryPoint.alias, entryPoint); - // TODO: Should we unInit a entry point if is removed? - this.instantiateEntryPoint(entryPoint); - }); - }); + this.observe( + extensionRegistry.byTypeAndFilter('entryPoint', (ext) => { + const extScope = ext.scope || 'local'; + return extScope === scope; + }), + (entryPoints) => { + entryPoints.forEach((entryPoint) => { + if (this.#entryPointMap.has(entryPoint.alias)) return; + this.#entryPointMap.set(entryPoint.alias, entryPoint); + // TODO: Should we unInit a entry point if is removed? + this.instantiateEntryPoint(entryPoint); + }); + }, + ); } async instantiateEntryPoint(manifest: ManifestEntryPoint) { 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 index f0e62ede1b..05118a7888 100644 --- 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 @@ -7,4 +7,12 @@ import type { ManifestBase } from './manifest-base.interface.js'; export interface ManifestBundle extends ManifestPlainJs<{ [key: string]: Array }> { type: 'bundle'; + + /** + * The scope of the bundle. If global, the bundle will be loaded on the root host and never be destroyed. + * If local, the bundle will be loaded on the BackofficeElement and will be destroyed when the host is removed for example when signing out. + * @default 'local' + * @enum ['global', 'local'] + */ + scope?: 'global' | 'local'; } 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 index 0c400daf64..90a1ddfedc 100644 --- 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 @@ -7,4 +7,12 @@ import type { ManifestPlainJs } from './base.types.js'; */ export interface ManifestEntryPoint extends ManifestPlainJs { type: 'entryPoint'; + + /** + * The scope of the entry point. If global, the entry point will be loaded on the root host and never be destroyed. + * If local, the entry point will be loaded on the BackofficeElement and will be destroyed when the host is removed for example when signing out. + * @default 'local' + * @enum ['global', 'local'] + */ + scope?: 'global' | 'local'; } From 6e9a70d2e6d16f05ddec16a7ea7480754b1c222c Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:13:24 +0200 Subject: [PATCH 02/39] let the umb-app element load any 'global' entrypoints and extensions --- src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts index de8c4ee346..e0902afa17 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts @@ -13,7 +13,11 @@ import type { Guard, UmbRoute } from '@umbraco-cms/backoffice/router'; import { pathWithoutBasePath } from '@umbraco-cms/backoffice/router'; import { OpenAPI, RuntimeLevelModel } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbContextDebugController } from '@umbraco-cms/backoffice/debug'; -import { UmbServerExtensionRegistrator } from '@umbraco-cms/backoffice/extension-api'; +import { + UmbBundleExtensionInitializer, + UmbEntryPointExtensionInitializer, + UmbServerExtensionRegistrator, +} from '@umbraco-cms/backoffice/extension-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; @customElement('umb-app') @@ -79,6 +83,10 @@ export class UmbAppElement extends UmbLitElement { OpenAPI.BASE = window.location.origin; + // Let bundles and entry points initialize before the application is initialized if they are global + new UmbBundleExtensionInitializer(this, umbExtensionsRegistry, 'global'); + new UmbEntryPointExtensionInitializer(this, umbExtensionsRegistry, 'global'); + new UmbIconRegistry().attach(this); new UUIIconRegistryEssential().attach(this); From 78af5198ce167877c01945580e59b321c7849d99 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 16 Apr 2024 16:15:40 +0200 Subject: [PATCH 03/39] add a `beforeInit` export to entry points --- .../entry-point-extension-initializer.ts | 36 +++++++++---------- .../models/entry-point.interface.ts | 12 +++++++ 2 files changed, 29 insertions(+), 19 deletions(-) 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 4b015a50fe..91409b3ee6 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,6 +1,7 @@ import type { ManifestEntryPoint } from '../types/index.js'; import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; -import { hasInitExport, loadManifestPlainJs } from '../functions/index.js'; +import type { UmbEntryPointModule } from '../models/entry-point.interface.js'; +import { hasBeforeInitExport, hasInitExport, loadManifestPlainJs } from '../functions/index.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; @@ -12,32 +13,29 @@ export class UmbEntryPointExtensionInitializer extends UmbControllerBase { constructor( host: UmbElement, extensionRegistry: UmbExtensionRegistry, - scope: 'global' | 'local' = 'local', + initFn: keyof UmbEntryPointModule, ) { super(host); this.#host = host; this.#extensionRegistry = extensionRegistry; - this.observe( - extensionRegistry.byTypeAndFilter('entryPoint', (ext) => { - const extScope = ext.scope || 'local'; - return extScope === scope; - }), - (entryPoints) => { - entryPoints.forEach((entryPoint) => { - if (this.#entryPointMap.has(entryPoint.alias)) return; - this.#entryPointMap.set(entryPoint.alias, entryPoint); - // TODO: Should we unInit a entry point if is removed? - this.instantiateEntryPoint(entryPoint); - }); - }, - ); + this.observe(extensionRegistry.byType('entryPoint'), (entryPoints) => { + entryPoints.forEach((entryPoint) => { + if (this.#entryPointMap.has(entryPoint.alias)) return; + this.#entryPointMap.set(entryPoint.alias, entryPoint); + // TODO: Should we unInit a entry point if is removed? + this.instantiateEntryPoint(entryPoint, initFn); + }); + }); } - async instantiateEntryPoint(manifest: ManifestEntryPoint) { + async instantiateEntryPoint(manifest: ManifestEntryPoint, initFn: keyof UmbEntryPointModule = 'onInit') { 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)) { + + // If the extension has known exports, be sure to run those + if (initFn === 'beforeInit' && hasBeforeInitExport(js)) { + js.beforeInit(this.#host, this.#extensionRegistry); + } else if (initFn === 'onInit' && hasInitExport(js)) { js.onInit(this.#host, this.#extensionRegistry); } } 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 7359ae0865..1a857c5174 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 @@ -2,11 +2,23 @@ import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; import type { ManifestBase } from '../types/index.js'; import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; +export type UmbEntryPointBeforeInit = (host: UmbElement, extensionRegistry: UmbExtensionRegistry) => void; export type UmbEntryPointOnInit = (host: UmbElement, extensionRegistry: UmbExtensionRegistry) => void; /** * Interface containing supported life-cycle functions for ESModule entry points */ export interface UmbEntryPointModule { + /** + * Function that will be called when the main element is loaded as the first thing + * before any app logic is executed such as authentication. + * @remark The 'host' argument is the umb-app element. + */ + beforeInit: UmbEntryPointBeforeInit; + + /** + * Function that will be called when the backoffice element is initialized after authentication. + * @remark The 'host' argument is the umb-backoffice element. + */ onInit: UmbEntryPointOnInit; } From 549432dab4dd7fa0eb4eebe229db10c850be11db Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 16 Apr 2024 16:16:00 +0200 Subject: [PATCH 04/39] let the app element load any bundles and only call the 'beforeInit' function on entrypoints --- src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts index e0902afa17..e38042a8b6 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts @@ -83,9 +83,10 @@ export class UmbAppElement extends UmbLitElement { OpenAPI.BASE = window.location.origin; - // Let bundles and entry points initialize before the application is initialized if they are global - new UmbBundleExtensionInitializer(this, umbExtensionsRegistry, 'global'); - new UmbEntryPointExtensionInitializer(this, umbExtensionsRegistry, 'global'); + new UmbBundleExtensionInitializer(this, umbExtensionsRegistry); + + // Initialise any entryPoints that export the 'beforeInit' function + new UmbEntryPointExtensionInitializer(this, umbExtensionsRegistry, 'beforeInit'); new UmbIconRegistry().attach(this); new UUIIconRegistryEssential().attach(this); @@ -107,7 +108,7 @@ export class UmbAppElement extends UmbLitElement { // Register Core extensions (this is specifically done here because we need these extensions to be registered before the application is initialized) onInit(this, umbExtensionsRegistry); - // Register public extensions + // Register public extensions (login extensions) await new UmbServerExtensionRegistrator(this, umbExtensionsRegistry).registerPublicExtensions(); // Try to initialise the auth flow and get the runtime status From 2f11333f070f92294c472a57fdb19838083a8bc2 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 16 Apr 2024 16:16:17 +0200 Subject: [PATCH 05/39] let the backoffice element only call 'onInit' on entrypoints (and dont load bundles) --- .../src/apps/backoffice/backoffice.element.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts index 672117fa16..8c6a24cc84 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts @@ -52,8 +52,8 @@ export class UmbBackofficeElement extends UmbLitElement { new UmbBackofficeContext(this); - new UmbBundleExtensionInitializer(this, umbExtensionsRegistry); - new UmbEntryPointExtensionInitializer(this, umbExtensionsRegistry); + // Initialise any entryPoints that export the 'onLoad' function + new UmbEntryPointExtensionInitializer(this, umbExtensionsRegistry, 'onInit'); new UmbServerExtensionRegistrator(this, umbExtensionsRegistry).registerPrivateExtensions(); From 39f7c47631a64fe2f90a9efd62393e603f87c3f7 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 16 Apr 2024 16:16:29 +0200 Subject: [PATCH 06/39] add helper function to check for beforeInit --- .../functions/has-before-init-export.function.ts | 8 ++++++++ .../src/libs/extension-api/functions/index.ts | 1 + 2 files changed, 9 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-before-init-export.function.ts diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-before-init-export.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-before-init-export.function.ts new file mode 100644 index 0000000000..4e8ed2109f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-before-init-export.function.ts @@ -0,0 +1,8 @@ +import type { UmbEntryPointModule } from '../models/entry-point.interface.js'; + +/** + * Validate if an ESModule export has a function called 'befireInit' + */ +export function hasBeforeInitExport(obj: unknown): obj is Pick { + return obj !== null && typeof obj === 'object' && 'beforeInit' 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 21d7768a87..394c62c87a 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-with-api.function.js'; export * from './create-extension-element.function.js'; export * from './has-init-export.function.js'; +export * from './has-before-init-export.function.js'; export * from './load-manifest-api.function.js'; export * from './load-manifest-element.function.js'; export * from './load-manifest-plain-css.function.js'; From 68e51e505362c26499360ffeee6aed58ed53b3d6 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 16 Apr 2024 16:16:40 +0200 Subject: [PATCH 07/39] revert bundles --- .../bundle-extension-initializer.ts | 42 +++++++------------ .../types/manifest-bundle.interface.ts | 8 ---- 2 files changed, 16 insertions(+), 34 deletions(-) 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 5117c055cb..04d1ac3e8d 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 @@ -8,35 +8,25 @@ export class UmbBundleExtensionInitializer extends UmbControllerBase { #extensionRegistry; #bundleMap = new Map(); - constructor( - host: UmbControllerHost, - extensionRegistry: UmbExtensionRegistry, - scope: 'global' | 'local' = 'local', - ) { + constructor(host: UmbControllerHost, extensionRegistry: UmbExtensionRegistry) { super(host); this.#extensionRegistry = extensionRegistry; - this.observe( - extensionRegistry.byTypeAndFilter('bundle', (ext) => { - const extScope = ext.scope || 'local'; - return extScope === scope; - }), - (bundles) => { - // Unregister removed bundles: - this.#bundleMap.forEach((existingBundle) => { - if (!bundles.find((b) => b.alias === existingBundle.alias)) { - this.unregisterBundle(existingBundle); - this.#bundleMap.delete(existingBundle.alias); - } - }); + this.observe(extensionRegistry.byType('bundle'), (bundles) => { + // Unregister removed bundles: + this.#bundleMap.forEach((existingBundle) => { + if (!bundles.find((b) => b.alias === existingBundle.alias)) { + this.unregisterBundle(existingBundle); + this.#bundleMap.delete(existingBundle.alias); + } + }); - // Register new bundles: - bundles.forEach((bundle) => { - if (this.#bundleMap.has(bundle.alias)) return; - this.#bundleMap.set(bundle.alias, bundle); - this.instantiateBundle(bundle); - }); - }, - ); + // Register new bundles: + bundles.forEach((bundle) => { + if (this.#bundleMap.has(bundle.alias)) return; + this.#bundleMap.set(bundle.alias, bundle); + this.instantiateBundle(bundle); + }); + }); } async instantiateBundle(manifest: ManifestBundle) { 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 index 05118a7888..f0e62ede1b 100644 --- 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 @@ -7,12 +7,4 @@ import type { ManifestBase } from './manifest-base.interface.js'; export interface ManifestBundle extends ManifestPlainJs<{ [key: string]: Array }> { type: 'bundle'; - - /** - * The scope of the bundle. If global, the bundle will be loaded on the root host and never be destroyed. - * If local, the bundle will be loaded on the BackofficeElement and will be destroyed when the host is removed for example when signing out. - * @default 'local' - * @enum ['global', 'local'] - */ - scope?: 'global' | 'local'; } From 3ba859da63c7f8632db858b9de5fc9a298cd102b Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 17 Apr 2024 15:33:46 +0200 Subject: [PATCH 08/39] add a bit of padding to filters --- .../user/user/collection/user-collection-header.element.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts index 70515da920..d39f83537c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts @@ -224,6 +224,7 @@ export class UmbUserCollectionHeaderElement extends UmbLitElement { display: flex; gap: var(--uui-size-space-3); flex-direction: column; + padding: var(--uui-size-space-3); } `, ]; From fc515ecd687640ff2fe79cb34f0dba70404c495e Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 18 Apr 2024 09:18:14 +0200 Subject: [PATCH 09/39] chore: correct typo in jsdoc --- .../extension-api/functions/has-before-init-export.function.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-before-init-export.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-before-init-export.function.ts index 4e8ed2109f..38b9dd64f4 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-before-init-export.function.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-before-init-export.function.ts @@ -1,7 +1,7 @@ import type { UmbEntryPointModule } from '../models/entry-point.interface.js'; /** - * Validate if an ESModule export has a function called 'befireInit' + * Validate if an ESModule has exported a function called `beforeInit` */ export function hasBeforeInitExport(obj: unknown): obj is Pick { return obj !== null && typeof obj === 'object' && 'beforeInit' in obj; From 150d97c7f786188c5f19285fe6edf1999fc44ba6 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 18 Apr 2024 09:28:17 +0200 Subject: [PATCH 10/39] add client direction model --- src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts index a95a2244a8..6c6bb47ba0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts @@ -50,3 +50,8 @@ export interface UmbUniqueItemModel { name: string; icon?: string; } + +export enum UmbDirectionModel { + ASCENDING = 'Ascending', + DESCENDING = 'Descending', +} From 1ab4b814aab65d01a25a2faeb87ac627576c8bd8 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 18 Apr 2024 09:31:23 +0200 Subject: [PATCH 11/39] add client side OrderBy and StateFilter models --- .../packages/user/user/collection/types.ts | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts index 6e4474e67c..7036c10203 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts @@ -1,11 +1,26 @@ -import type { DirectionModel, UserOrderModel, UserStateModel } from '@umbraco-cms/backoffice/external/backend-api'; +import type { UmbDirectionModel } from '@umbraco-cms/backoffice/models'; export interface UmbUserCollectionFilterModel { skip?: number; take?: number; - orderBy?: UserOrderModel; - orderDirection?: DirectionModel; + orderBy?: UmbUserOrderByModel; + orderDirection?: UmbDirectionModel; userGroupIds?: string[]; - userStates?: UserStateModel[]; + userStates?: UmbUserStateFilterModel[]; filter?: string; } + +export enum UmbUserOrderByModel { + NAME = 'Name', + CREATE_DATE = 'CreateDate', + LAST_LOGIN_DATE = 'LastLoginDate', +} + +export enum UmbUserStateFilterModel { + ACTIVE = 'Active', + DISABLED = 'Disabled', + LOCKED_OUT = 'LockedOut', + INVITED = 'Invited', + INACTIVE = 'Inactive', + ALL = 'All', +} From c625332612f41b4805305a3d35f46db8b108a069 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 18 Apr 2024 09:49:03 +0200 Subject: [PATCH 12/39] revert manifest entrypoint --- .../extension-api/types/manifest-entrypoint.interface.ts | 8 -------- 1 file changed, 8 deletions(-) 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 index 90a1ddfedc..0c400daf64 100644 --- 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 @@ -7,12 +7,4 @@ import type { ManifestPlainJs } from './base.types.js'; */ export interface ManifestEntryPoint extends ManifestPlainJs { type: 'entryPoint'; - - /** - * The scope of the entry point. If global, the entry point will be loaded on the root host and never be destroyed. - * If local, the entry point will be loaded on the BackofficeElement and will be destroyed when the host is removed for example when signing out. - * @default 'local' - * @enum ['global', 'local'] - */ - scope?: 'global' | 'local'; } From 6c3e437ade0c232a4f41deec43d666c84e5c60fb Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 18 Apr 2024 11:32:55 +0200 Subject: [PATCH 13/39] add type for order by option --- .../src/packages/user/user/collection/types.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts index 7036c10203..3b072538ca 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts @@ -10,6 +10,14 @@ export interface UmbUserCollectionFilterModel { filter?: string; } +export interface UmbUserOrderByOption { + label: string; + config: { + orderBy: UmbUserOrderByModel; + orderDirection: UmbDirectionModel; + }; +} + export enum UmbUserOrderByModel { NAME = 'Name', CREATE_DATE = 'CreateDate', From 3029273eebf4ef111f8a62436bd31f9449b83b24 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 18 Apr 2024 12:52:09 +0200 Subject: [PATCH 14/39] correct labels --- src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts index 4325eecb74..ebfa4a1fae 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts @@ -1948,8 +1948,8 @@ export default { stateInactive: 'Inactive', sortNameAscending: 'Name (A-Z)', sortNameDescending: 'Name (Z-A)', - sortCreateDateAscending: 'Newest', - sortCreateDateDescending: 'Oldest', + sortCreateDateDescending: 'Newest', + sortCreateDateAscending: 'Oldest', sortLastLoginDateDescending: 'Last login', noUserGroupsAdded: 'No user groups have been added', '2faDisableText': From d5072062d7c6b219a9d851f51461229f69c6e15a Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 18 Apr 2024 12:52:16 +0200 Subject: [PATCH 15/39] add unique --- .../src/packages/user/user/collection/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts index 3b072538ca..1a259f10f1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts @@ -11,6 +11,7 @@ export interface UmbUserCollectionFilterModel { } export interface UmbUserOrderByOption { + unique: string; label: string; config: { orderBy: UmbUserOrderByModel; From ecb8e0b1c3510d4f96985fb19fc1a3a8e2aebfaa Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 18 Apr 2024 12:52:30 +0200 Subject: [PATCH 16/39] render orderBy filter --- .../user-collection-header.element.ts | 120 +++++++++++------- .../collection/user-collection.context.ts | 92 +++++++++++++- 2 files changed, 160 insertions(+), 52 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts index d39f83537c..ab4f7a4b66 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts @@ -1,29 +1,21 @@ import type { UmbUserCollectionContext } from './user-collection.context.js'; -import type { - UUIBooleanInputEvent, - UUICheckboxElement, - UUIRadioGroupElement, - UUIRadioGroupEvent, -} from '@umbraco-cms/backoffice/external/uui'; +import type { UmbUserOrderByOption } from './types.js'; +import { UmbUserStateFilterModel } from './types.js'; +import type { UUIBooleanInputEvent, UUICheckboxElement } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UMB_DEFAULT_COLLECTION_CONTEXT } from '@umbraco-cms/backoffice/collection'; -import type { UmbModalManagerContext } from '@umbraco-cms/backoffice/modal'; -import type { UserOrderModel } from '@umbraco-cms/backoffice/external/backend-api'; -import { UserStateModel } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbUserGroupDetailModel } from '@umbraco-cms/backoffice/user-group'; import { UmbUserGroupCollectionRepository } from '@umbraco-cms/backoffice/user-group'; +import { observeMultiple } from '@umbraco-cms/backoffice/observable-api'; @customElement('umb-user-collection-header') export class UmbUserCollectionHeaderElement extends UmbLitElement { @state() - private _stateFilterOptions: Array = Object.values(UserStateModel); + private _stateFilterOptions: Array = Object.values(UmbUserStateFilterModel); @state() - private _stateFilterSelection: Array = []; - - @state() - private _orderBy?: UserOrderModel; + private _stateFilterSelection: Array = []; @state() private _userGroups: Array = []; @@ -31,7 +23,12 @@ export class UmbUserCollectionHeaderElement extends UmbLitElement { @state() private _userGroupFilterSelection: Array = []; - #modalContext?: UmbModalManagerContext; + @state() + private _orderByOptions: Array = []; + + @state() + _activeOrderByOption?: UmbUserOrderByOption; + #collectionContext?: UmbUserCollectionContext; #inputTimer?: NodeJS.Timeout; #inputTimerAmount = 500; @@ -43,9 +40,26 @@ export class UmbUserCollectionHeaderElement extends UmbLitElement { this.consumeContext(UMB_DEFAULT_COLLECTION_CONTEXT, (instance) => { this.#collectionContext = instance as UmbUserCollectionContext; + this.#observeOrderByOptions(); }); } + #observeOrderByOptions() { + if (!this.#collectionContext) return; + this.observe( + observeMultiple([this.#collectionContext.orderByOptions, this.#collectionContext.activeOrderByOption]), + ([options, activeOption]) => { + debugger; + this._orderByOptions = options; + + if (activeOption) { + this._activeOrderByOption = this._orderByOptions.find((option) => option.unique === activeOption); + } + }, + '_umbObserveUserOrderByOptions', + ); + } + protected firstUpdated() { this.#requestUserGroups(); } @@ -68,7 +82,7 @@ export class UmbUserCollectionHeaderElement extends UmbLitElement { #onStateFilterChange(event: UUIBooleanInputEvent) { event.stopPropagation(); const target = event.currentTarget as UUICheckboxElement; - const value = target.value as UserStateModel; + const value = target.value as UmbUserStateFilterModel; const isChecked = target.checked; this._stateFilterSelection = isChecked @@ -78,34 +92,6 @@ export class UmbUserCollectionHeaderElement extends UmbLitElement { this.#collectionContext?.setStateFilter(this._stateFilterSelection); } - #onOrderByChange(event: UUIRadioGroupEvent) { - event.stopPropagation(); - const target = event.currentTarget as UUIRadioGroupElement | null; - - if (target) { - this._orderBy = target.value as UserOrderModel; - this.#collectionContext?.setOrderByFilter(this._orderBy); - } - } - - render() { - return html` - - ${this.#renderSearch()} -
${this.#renderFilters()} ${this.#renderCollectionViews()}
- `; - } - - #renderSearch() { - return html` - - `; - } - #onUserGroupFilterChange(event: UUIBooleanInputEvent) { const target = event.currentTarget as UUICheckboxElement; const item = this._userGroups.find((group) => group.unique === target.value); @@ -122,6 +108,10 @@ export class UmbUserCollectionHeaderElement extends UmbLitElement { this.#collectionContext?.setUserGroupFilter(uniques); } + #onOrderByChange(option: UmbUserOrderByOption) { + this.#collectionContext?.setActiveOrderByOption(option.unique); + } + #getUserGroupFilterLabel() { const length = this._userGroupFilterSelection.length; const max = 2; @@ -146,8 +136,26 @@ export class UmbUserCollectionHeaderElement extends UmbLitElement { .join(', ') + (length > max ? ' + ' + (length - max) : ''); } + render() { + return html` + + ${this.#renderSearch()} +
${this.#renderFilters()} ${this.#renderCollectionViews()}
+ `; + } + + #renderSearch() { + return html` + + `; + } + #renderFilters() { - return html` ${this.#renderStatusFilter()} ${this.#renderUserGroupFilter()} `; + return html` ${this.#renderStatusFilter()} ${this.#renderUserGroupFilter()} ${this.#renderOrderBy()} `; } #renderStatusFilter() { @@ -196,6 +204,28 @@ export class UmbUserCollectionHeaderElement extends UmbLitElement { `; } + #renderOrderBy() { + return html` + + : + ${this._activeOrderByOption ? this.localize.string(this._activeOrderByOption.label) : ''} + + + +
+ ${this._orderByOptions.map( + (option) => html` + this.#onOrderByChange(option)}> + `, + )} +
+
+
+ `; + } + #renderCollectionViews() { return html` `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection.context.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection.context.ts index 2c0d4566ab..3b454ca29b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection.context.ts @@ -1,33 +1,102 @@ import type { UmbUserDetailModel } from '../types.js'; import { UMB_COLLECTION_VIEW_USER_GRID } from './views/index.js'; -import type { UmbUserCollectionFilterModel } from './types.js'; +import type { UmbUserCollectionFilterModel, UmbUserOrderByOption, UmbUserStateFilterModel } from './types.js'; +import { UmbUserOrderByModel } from './types.js'; import { UmbDefaultCollectionContext } from '@umbraco-cms/backoffice/collection'; -import type { UserOrderModel, UserStateModel } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbDirectionModel } from '@umbraco-cms/backoffice/models'; +import { UmbArrayState, UmbStringState } from '@umbraco-cms/backoffice/observable-api'; export class UmbUserCollectionContext extends UmbDefaultCollectionContext< UmbUserDetailModel, UmbUserCollectionFilterModel > { + #orderByOptions = new UmbArrayState( + [ + { + unique: 'nameAscending', + label: '#user_sortNameAscending', + config: { + orderBy: UmbUserOrderByModel.NAME, + orderDirection: UmbDirectionModel.ASCENDING, + }, + }, + { + unique: 'nameDescending', + label: '#user_sortNameDescending', + config: { + orderBy: UmbUserOrderByModel.NAME, + orderDirection: UmbDirectionModel.DESCENDING, + }, + }, + { + unique: 'createDateDescending', + label: '#user_sortCreateDateDescending', + config: { + orderBy: UmbUserOrderByModel.CREATE_DATE, + orderDirection: UmbDirectionModel.DESCENDING, + }, + }, + { + unique: 'createDateAscending', + label: '#user_sortCreateDateAscending', + config: { + orderBy: UmbUserOrderByModel.CREATE_DATE, + orderDirection: UmbDirectionModel.ASCENDING, + }, + }, + { + unique: 'lastLoginDateDescending', + label: '#user_sortLastLoginDateDescending', + config: { + orderBy: UmbUserOrderByModel.LAST_LOGIN_DATE, + orderDirection: UmbDirectionModel.DESCENDING, + }, + }, + ], + (x) => x.label, + ); + orderByOptions = this.#orderByOptions.asObservable(); + + #activeOrderByOption = new UmbStringState(undefined); + activeOrderByOption = this.#activeOrderByOption.asObservable(); + constructor(host: UmbControllerHost) { super(host, UMB_COLLECTION_VIEW_USER_GRID); + // init default orderBy option + const defaultOrderByOption = this.#orderByOptions.getValue()[0]; + this.setActiveOrderByOption(defaultOrderByOption.unique); } /** - * Sets the state filter for the collection and refreshes the collection. - * @param {Array} selection + * Sets the active order by for the collection and refreshes the collection. + * @param {UmbUserOrderByModel} orderBy + * @param {UmbDirectionModel} orderDirection * @memberof UmbUserCollectionContext */ - setStateFilter(selection: Array) { + setActiveOrderByOption(unique: string) { + const option = this.#orderByOptions.getValue().find((x) => x.unique === unique); + this.#activeOrderByOption.setValue(unique); + this.setFilter({ orderBy: option?.config.orderBy, orderDirection: option?.config.orderDirection }); + } + + getActiveOrderByOption() {} + + /** + * Sets the state filter for the collection and refreshes the collection. + * @param {Array} selection + * @memberof UmbUserCollectionContext + */ + setStateFilter(selection: Array) { this.setFilter({ userStates: selection }); } /** * Sets the order by filter for the collection and refreshes the collection. - * @param {UserOrderModel} orderBy + * @param {UmbUserOrderByModel} orderBy * @memberof UmbUserCollectionContext */ - setOrderByFilter(orderBy: UserOrderModel) { + setOrderByFilter(orderBy: UmbUserOrderByModel) { this.setFilter({ orderBy }); } @@ -39,6 +108,15 @@ export class UmbUserCollectionContext extends UmbDefaultCollectionContext< setUserGroupFilter(selection: Array) { this.setFilter({ userGroupIds: selection }); } + + /** + * Sets the order direction filter for the collection and refreshes the collection. + * @param {UmbDirectionModel} orderDirection + * @memberof UmbUserCollectionContext + */ + setOrderDirectionFilter(orderDirection: UmbDirectionModel) { + this.setFilter({ orderDirection }); + } } export default UmbUserCollectionContext; From d8ad2e409e7da8c27a0601d576afb8c5bc029216 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 18 Apr 2024 13:13:46 +0200 Subject: [PATCH 17/39] set default filter trough constructor --- .../default/collection-default.context.ts | 5 +- .../collection/user-collection.context.ts | 104 +++++++++--------- 2 files changed, 57 insertions(+), 52 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts index df2ec4e533..11247ffae2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts @@ -51,6 +51,7 @@ export class UmbDefaultCollectionContext< public readonly view = new UmbCollectionViewManager(this); #defaultViewAlias: string; + #defaultFilter: Partial; #initResolver?: () => void; #initialized = false; @@ -59,10 +60,11 @@ export class UmbDefaultCollectionContext< this.#initialized ? resolve() : (this.#initResolver = resolve); }); - constructor(host: UmbControllerHost, defaultViewAlias: string) { + constructor(host: UmbControllerHost, defaultViewAlias: string, defaultFilter: Partial = {}) { super(host, UMB_DEFAULT_COLLECTION_CONTEXT); this.#defaultViewAlias = defaultViewAlias; + this.#defaultFilter = defaultFilter; this.pagination.addEventListener(UmbChangeEvent.TYPE, this.#onPageChange); } @@ -79,6 +81,7 @@ export class UmbDefaultCollectionContext< } this.#filter.setValue({ + ...this.#defaultFilter, ...this.#config, ...this.#filter.getValue(), skip: 0, diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection.context.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection.context.ts index 3b454ca29b..7246c8ee7d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection.context.ts @@ -7,65 +7,69 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbDirectionModel } from '@umbraco-cms/backoffice/models'; import { UmbArrayState, UmbStringState } from '@umbraco-cms/backoffice/observable-api'; +const orderByOptions: Array = [ + { + unique: 'nameAscending', + label: '#user_sortNameAscending', + config: { + orderBy: UmbUserOrderByModel.NAME, + orderDirection: UmbDirectionModel.ASCENDING, + }, + }, + { + unique: 'nameDescending', + label: '#user_sortNameDescending', + config: { + orderBy: UmbUserOrderByModel.NAME, + orderDirection: UmbDirectionModel.DESCENDING, + }, + }, + { + unique: 'createDateDescending', + label: '#user_sortCreateDateDescending', + config: { + orderBy: UmbUserOrderByModel.CREATE_DATE, + orderDirection: UmbDirectionModel.DESCENDING, + }, + }, + { + unique: 'createDateAscending', + label: '#user_sortCreateDateAscending', + config: { + orderBy: UmbUserOrderByModel.CREATE_DATE, + orderDirection: UmbDirectionModel.ASCENDING, + }, + }, + { + unique: 'lastLoginDateDescending', + label: '#user_sortLastLoginDateDescending', + config: { + orderBy: UmbUserOrderByModel.LAST_LOGIN_DATE, + orderDirection: UmbDirectionModel.DESCENDING, + }, + }, +]; + export class UmbUserCollectionContext extends UmbDefaultCollectionContext< UmbUserDetailModel, UmbUserCollectionFilterModel > { - #orderByOptions = new UmbArrayState( - [ - { - unique: 'nameAscending', - label: '#user_sortNameAscending', - config: { - orderBy: UmbUserOrderByModel.NAME, - orderDirection: UmbDirectionModel.ASCENDING, - }, - }, - { - unique: 'nameDescending', - label: '#user_sortNameDescending', - config: { - orderBy: UmbUserOrderByModel.NAME, - orderDirection: UmbDirectionModel.DESCENDING, - }, - }, - { - unique: 'createDateDescending', - label: '#user_sortCreateDateDescending', - config: { - orderBy: UmbUserOrderByModel.CREATE_DATE, - orderDirection: UmbDirectionModel.DESCENDING, - }, - }, - { - unique: 'createDateAscending', - label: '#user_sortCreateDateAscending', - config: { - orderBy: UmbUserOrderByModel.CREATE_DATE, - orderDirection: UmbDirectionModel.ASCENDING, - }, - }, - { - unique: 'lastLoginDateDescending', - label: '#user_sortLastLoginDateDescending', - config: { - orderBy: UmbUserOrderByModel.LAST_LOGIN_DATE, - orderDirection: UmbDirectionModel.DESCENDING, - }, - }, - ], - (x) => x.label, - ); + #orderByOptions = new UmbArrayState([], (x) => x.label); orderByOptions = this.#orderByOptions.asObservable(); #activeOrderByOption = new UmbStringState(undefined); activeOrderByOption = this.#activeOrderByOption.asObservable(); constructor(host: UmbControllerHost) { - super(host, UMB_COLLECTION_VIEW_USER_GRID); - // init default orderBy option - const defaultOrderByOption = this.#orderByOptions.getValue()[0]; - this.setActiveOrderByOption(defaultOrderByOption.unique); + const firstOption: UmbUserOrderByOption = orderByOptions[0]; + + super(host, UMB_COLLECTION_VIEW_USER_GRID, { + orderBy: firstOption.config.orderBy, + orderDirection: firstOption.config.orderDirection, + }); + + this.#orderByOptions.setValue(orderByOptions); + this.#activeOrderByOption.setValue(firstOption.unique); } /** @@ -80,8 +84,6 @@ export class UmbUserCollectionContext extends UmbDefaultCollectionContext< this.setFilter({ orderBy: option?.config.orderBy, orderDirection: option?.config.orderDirection }); } - getActiveOrderByOption() {} - /** * Sets the state filter for the collection and refreshes the collection. * @param {Array} selection From 0d9de1e295ffca25cba3e13f85f7703cddea43d9 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 18 Apr 2024 14:02:50 +0200 Subject: [PATCH 18/39] set active item --- .../user/user/collection/user-collection-header.element.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts index ab4f7a4b66..44dac6b85e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts @@ -49,7 +49,6 @@ export class UmbUserCollectionHeaderElement extends UmbLitElement { this.observe( observeMultiple([this.#collectionContext.orderByOptions, this.#collectionContext.activeOrderByOption]), ([options, activeOption]) => { - debugger; this._orderByOptions = options; if (activeOption) { @@ -217,7 +216,8 @@ export class UmbUserCollectionHeaderElement extends UmbLitElement { (option) => html` this.#onOrderByChange(option)}> + @click-label=${() => this.#onOrderByChange(option)} + ?active=${this._activeOrderByOption?.unique === option.unique}> `, )} From 7cd26cf5d5ca9c6211693d3b2af6d1d4c6caa862 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 18 Apr 2024 14:16:18 +0200 Subject: [PATCH 19/39] set active item --- .../collection-view-bundle.element.ts | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/collection-view-bundle.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/collection-view-bundle.element.ts index 0ff700a282..276c742c57 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/collection-view-bundle.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/collection-view-bundle.element.ts @@ -110,11 +110,6 @@ export class UmbCollectionViewBundleElement extends UmbLitElement { #onClick(view: UmbCollectionViewLayout) { this.#collectionContext?.setLastSelectedView(this._entityUnique, view.alias); - - // TODO: This ignorer is just neede for JSON SCHEMA TO WORK, As its not updated with latest TS jet. - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - this._popover?.hidePopover(); } render() { @@ -123,7 +118,7 @@ export class UmbCollectionViewBundleElement extends UmbLitElement { return html` - ${this.#renderItemDisplay(this._currentView)} + @@ -141,31 +136,27 @@ export class UmbCollectionViewBundleElement extends UmbLitElement { #renderItem(view: UmbCollectionViewLayout) { return html` - this.#onClick(view)}> - ${this.#renderItemDisplay(view)} - ${view.label} - + this.#onClick(view)} + ?active=${view.alias === this._currentView?.alias}> + + `; } - #renderItemDisplay(view: UmbCollectionViewLayout) { - return html``; - } - static styles = [ UmbTextStyles, css` :host { --uui-button-content-align: left; } - .label { - margin-left: var(--uui-size-space-1); - } + .filter-dropdown { - display: flex; - gap: var(--uui-size-space-1); - flex-direction: column; + padding: var(--uui-size-space-3); } + umb-icon { display: inline-block; } From 2f107d26122e694014c953b1e9134940e057ceae Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 18 Apr 2024 14:30:50 +0200 Subject: [PATCH 20/39] hacking typescript --- .../user-collection.server.data-source.ts | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/repository/user-collection.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/repository/user-collection.server.data-source.ts index 96c87e5046..eaf765fd5a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/repository/user-collection.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/repository/user-collection.server.data-source.ts @@ -2,7 +2,12 @@ import type { UmbUserDetailModel } from '../../types.js'; import { UMB_USER_ENTITY_TYPE } from '../../entity.js'; import type { UmbUserCollectionFilterModel } from '../types.js'; import type { UmbCollectionDataSource } from '@umbraco-cms/backoffice/collection'; -import type { UserResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; +import type { + DirectionModel, + UserOrderModel, + UserResponseModel, + UserStateModel, +} from '@umbraco-cms/backoffice/external/backend-api'; import { UserService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; @@ -32,7 +37,19 @@ export class UmbUserCollectionServerDataSource implements UmbCollectionDataSourc * @memberof UmbUserCollectionServerDataSource */ async getCollection(filter: UmbUserCollectionFilterModel) { - const { data, error } = await tryExecuteAndNotify(this.#host, UserService.getFilterUser(filter)); + // TODO: This is a temporary workaround to avoid a type error. + const { data, error } = await tryExecuteAndNotify( + this.#host, + UserService.getFilterUser({ + filter: filter.filter, + orderBy: filter.orderBy as unknown as UserOrderModel, + orderDirection: filter.orderDirection as unknown as DirectionModel, + skip: filter.skip, + take: filter.take, + userGroupIds: filter.userGroupIds, + userStates: filter.userStates as unknown as Array, + }), + ); if (data) { const { items, total } = data; From efce6d40e9af3333e50cb28ed8514a95bba06d4a Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:32:20 +0200 Subject: [PATCH 21/39] add a new base extension initializer to initialize new types of entry point modules --- .../bundle-extension-initializer.ts | 45 ++++++----------- .../entry-point-extension-initializer.ts | 46 +++++++----------- .../extension-initializer-base.ts | 48 +++++++++++++++++++ .../libs/extension-api/initializers/index.ts | 1 + 4 files changed, 80 insertions(+), 60 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/extension-initializer-base.ts 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 04d1ac3e8d..545176457a 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,35 +1,18 @@ import type { ManifestBase, ManifestBundle } from '../types/index.js'; import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; import { loadManifestPlainJs } from '../functions/load-manifest-plain-js.function.js'; -import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbExtensionInitializerBase } from './extension-initializer-base.js'; +import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; -export class UmbBundleExtensionInitializer extends UmbControllerBase { - #extensionRegistry; - #bundleMap = new Map(); - - constructor(host: UmbControllerHost, extensionRegistry: UmbExtensionRegistry) { - super(host); - this.#extensionRegistry = extensionRegistry; - this.observe(extensionRegistry.byType('bundle'), (bundles) => { - // Unregister removed bundles: - this.#bundleMap.forEach((existingBundle) => { - if (!bundles.find((b) => b.alias === existingBundle.alias)) { - this.unregisterBundle(existingBundle); - this.#bundleMap.delete(existingBundle.alias); - } - }); - - // Register new bundles: - bundles.forEach((bundle) => { - if (this.#bundleMap.has(bundle.alias)) return; - this.#bundleMap.set(bundle.alias, bundle); - this.instantiateBundle(bundle); - }); - }); +/** + * Extension initializer for the `bundle` extension type + */ +export class UmbBundleExtensionInitializer extends UmbExtensionInitializerBase<'bundle', ManifestBundle> { + constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry) { + super(host, extensionRegistry, 'bundle'); } - async instantiateBundle(manifest: ManifestBundle) { + async instantiateExtension(manifest: ManifestBundle): Promise { if (manifest.js) { const js = await loadManifestPlainJs(manifest.js); @@ -38,16 +21,16 @@ export class UmbBundleExtensionInitializer extends UmbControllerBase { const value = js[key]; if (Array.isArray(value)) { - this.#extensionRegistry.registerMany(value); + this.extensionRegistry.registerMany(value); } else if (typeof value === 'object') { - this.#extensionRegistry.register(value); + this.extensionRegistry.register(value); } }); } } } - async unregisterBundle(manifest: ManifestBundle) { + async unloadExtension(manifest: ManifestBundle): Promise { if (manifest.js) { const js = await loadManifestPlainJs(manifest.js); @@ -56,9 +39,9 @@ export class UmbBundleExtensionInitializer extends UmbControllerBase { const value = js[key]; if (Array.isArray(value)) { - this.#extensionRegistry.unregisterMany(value.map((v) => v.alias)); + this.extensionRegistry.unregisterMany(value.map((v) => v.alias)); } else if (typeof value === 'object') { - this.#extensionRegistry.unregister((value as ManifestBase).alias); + 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 91409b3ee6..011d0f58f5 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,43 +1,31 @@ import type { ManifestEntryPoint } from '../types/index.js'; +import { hasInitExport, loadManifestPlainJs } from '../functions/index.js'; import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; -import type { UmbEntryPointModule } from '../models/entry-point.interface.js'; -import { hasBeforeInitExport, hasInitExport, loadManifestPlainJs } from '../functions/index.js'; -import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import { UmbExtensionInitializerBase } from './extension-initializer-base.js'; import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; -export class UmbEntryPointExtensionInitializer extends UmbControllerBase { - #host; - #extensionRegistry; - #entryPointMap = new Map(); - - constructor( - host: UmbElement, - extensionRegistry: UmbExtensionRegistry, - initFn: keyof UmbEntryPointModule, - ) { - super(host); - this.#host = host; - this.#extensionRegistry = extensionRegistry; - this.observe(extensionRegistry.byType('entryPoint'), (entryPoints) => { - entryPoints.forEach((entryPoint) => { - if (this.#entryPointMap.has(entryPoint.alias)) return; - this.#entryPointMap.set(entryPoint.alias, entryPoint); - // TODO: Should we unInit a entry point if is removed? - this.instantiateEntryPoint(entryPoint, initFn); - }); - }); +/** + * Extension initializer for the `entryPoint` extension type + */ +export class UmbEntryPointExtensionInitializer extends UmbExtensionInitializerBase<'entryPoint', ManifestEntryPoint> { + constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry) { + super(host, extensionRegistry, 'entryPoint'); } - async instantiateEntryPoint(manifest: ManifestEntryPoint, initFn: keyof UmbEntryPointModule = 'onInit') { + async instantiateExtension(manifest: ManifestEntryPoint) { if (manifest.js) { const js = await loadManifestPlainJs(manifest.js); // If the extension has known exports, be sure to run those - if (initFn === 'beforeInit' && hasBeforeInitExport(js)) { - js.beforeInit(this.#host, this.#extensionRegistry); - } else if (initFn === 'onInit' && hasInitExport(js)) { - js.onInit(this.#host, this.#extensionRegistry); + if (hasInitExport(js)) { + js.onInit(this.host, this.extensionRegistry); } } } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + unloadExtension(_manifest: ManifestEntryPoint): void { + // No-op + // Entry points are not unloaded, but if they were, this is where you would do it. + } } diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/extension-initializer-base.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/extension-initializer-base.ts new file mode 100644 index 0000000000..39b67f29c4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/extension-initializer-base.ts @@ -0,0 +1,48 @@ +import type { ManifestBase } from '../types/index.js'; +import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; +import type { SpecificManifestTypeOrManifestBase } from '../types/map.types.js'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; +import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; + +/** + * Base class for extension initializers, which are responsible for loading and unloading extensions. + */ +export abstract class UmbExtensionInitializerBase< + Key extends string, + T extends ManifestBase = SpecificManifestTypeOrManifestBase, +> extends UmbControllerBase { + protected host; + protected extensionRegistry; + #extensionMap = new Map(); + + constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry, manifestType: Key) { + super(host); + this.host = host; + this.extensionRegistry = extensionRegistry; + this.observe(extensionRegistry.byType(manifestType), (extensions) => { + this.#extensionMap.forEach((existingExt) => { + if (!extensions.find((b) => b.alias === existingExt.alias)) { + this.unloadExtension(existingExt); + this.#extensionMap.delete(existingExt.alias); + } + }); + + extensions.forEach((extension) => { + if (this.#extensionMap.has(extension.alias)) return; + this.#extensionMap.set(extension.alias, extension); + this.instantiateExtension(extension); + }); + }); + } + + /** + * Perform any logic required to instantiate the extension. + */ + abstract instantiateExtension(manifest: T): Promise | void; + + /** + * Perform any logic required to unload the extension. + */ + abstract unloadExtension(manifest: T): Promise | void; +} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts index 1441726314..b89ddacc4c 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts @@ -1,2 +1,3 @@ export * from './bundle-extension-initializer.js'; export * from './entry-point-extension-initializer.js'; +export * from './extension-initializer-base.js'; From 603623d680afe6add5b1f19fd5e976ece40c95ea Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:32:39 +0200 Subject: [PATCH 22/39] load entryPoints in the backoffice element --- .../src/apps/backoffice/backoffice.element.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts index 8c6a24cc84..72babdddbe 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts @@ -52,8 +52,7 @@ export class UmbBackofficeElement extends UmbLitElement { new UmbBackofficeContext(this); - // Initialise any entryPoints that export the 'onLoad' function - new UmbEntryPointExtensionInitializer(this, umbExtensionsRegistry, 'onInit'); + new UmbEntryPointExtensionInitializer(this, umbExtensionsRegistry); new UmbServerExtensionRegistrator(this, umbExtensionsRegistry).registerPrivateExtensions(); From 448a03b12a57dbb089862b558b85557e80eef4d8 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:32:58 +0200 Subject: [PATCH 23/39] remove the beforeInit function --- .../libs/extension-api/models/entry-point.interface.ts | 9 --------- 1 file changed, 9 deletions(-) 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 1a857c5174..ad38d5f3a4 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 @@ -2,23 +2,14 @@ import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; import type { ManifestBase } from '../types/index.js'; import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; -export type UmbEntryPointBeforeInit = (host: UmbElement, extensionRegistry: UmbExtensionRegistry) => void; export type UmbEntryPointOnInit = (host: UmbElement, extensionRegistry: UmbExtensionRegistry) => void; /** * Interface containing supported life-cycle functions for ESModule entry points */ export interface UmbEntryPointModule { - /** - * Function that will be called when the main element is loaded as the first thing - * before any app logic is executed such as authentication. - * @remark The 'host' argument is the umb-app element. - */ - beforeInit: UmbEntryPointBeforeInit; - /** * Function that will be called when the backoffice element is initialized after authentication. - * @remark The 'host' argument is the umb-backoffice element. */ onInit: UmbEntryPointOnInit; } From b6e64d4acd6fddc450f5115bdf07d701dc852058 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:33:37 +0200 Subject: [PATCH 24/39] add a new type `appEntryPoint` and a corresponding initializer and load them up in the app element --- .../src/apps/app/app.element.ts | 5 ++- .../app-entry-point-extension-initializer.ts | 34 +++++++++++++++++++ .../libs/extension-api/initializers/index.ts | 1 + .../src/libs/extension-api/types/index.ts | 1 + .../manifest-app-entrypoint.interface.ts | 10 ++++++ 5 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/app-entry-point-extension-initializer.ts create mode 100644 src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-app-entrypoint.interface.ts diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts index e38042a8b6..3ced70e5a4 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts @@ -14,8 +14,8 @@ import { pathWithoutBasePath } from '@umbraco-cms/backoffice/router'; import { OpenAPI, RuntimeLevelModel } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbContextDebugController } from '@umbraco-cms/backoffice/debug'; import { + UmbAppEntryPointExtensionInitializer, UmbBundleExtensionInitializer, - UmbEntryPointExtensionInitializer, UmbServerExtensionRegistrator, } from '@umbraco-cms/backoffice/extension-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; @@ -85,8 +85,7 @@ export class UmbAppElement extends UmbLitElement { new UmbBundleExtensionInitializer(this, umbExtensionsRegistry); - // Initialise any entryPoints that export the 'beforeInit' function - new UmbEntryPointExtensionInitializer(this, umbExtensionsRegistry, 'beforeInit'); + new UmbAppEntryPointExtensionInitializer(this, umbExtensionsRegistry); new UmbIconRegistry().attach(this); new UUIIconRegistryEssential().attach(this); diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/app-entry-point-extension-initializer.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/app-entry-point-extension-initializer.ts new file mode 100644 index 0000000000..9167e1c8bd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/app-entry-point-extension-initializer.ts @@ -0,0 +1,34 @@ +import type { ManifestAppEntryPoint } from '../types/index.js'; +import { hasInitExport, loadManifestPlainJs } from '../functions/index.js'; +import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; +import { UmbExtensionInitializerBase } from './extension-initializer-base.js'; +import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; + +/** + * Extension initializer for the `appEntryPoint` extension type + */ +export class UmbAppEntryPointExtensionInitializer extends UmbExtensionInitializerBase< + 'appEntryPoint', + ManifestAppEntryPoint +> { + constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry) { + super(host, extensionRegistry, 'appEntryPoint'); + } + + async instantiateExtension(manifest: ManifestAppEntryPoint) { + if (manifest.js) { + const js = await loadManifestPlainJs(manifest.js); + + // If the extension has known exports, be sure to run those + if (hasInitExport(js)) { + js.onInit(this.host, this.extensionRegistry); + } + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + unloadExtension(_manifest: ManifestAppEntryPoint): void { + // No-op + // Entry points are not unloaded, but if they were, this is where you would do it. + } +} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts index b89ddacc4c..38b630c03a 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts @@ -1,3 +1,4 @@ +export * from './app-entry-point-extension-initializer.js'; export * from './bundle-extension-initializer.js'; export * from './entry-point-extension-initializer.js'; export * from './extension-initializer-base.js'; 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 index 4bb9e60d1c..6577a9fccc 100644 --- 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 @@ -1,5 +1,6 @@ export * from './base.types.js'; export * from './condition.types.js'; +export * from './manifest-app-entrypoint.interface.js'; export * from './manifest-base.interface.js'; export * from './manifest-bundle.interface.js'; export * from './manifest-condition.interface.js'; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-app-entrypoint.interface.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-app-entrypoint.interface.ts new file mode 100644 index 0000000000..72bf874044 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-app-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 ManifestAppEntryPoint extends ManifestPlainJs { + type: 'appEntryPoint'; +} From e984e23b49036113b09a232d08507e8e5fd338e9 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 18 Apr 2024 14:35:38 +0200 Subject: [PATCH 25/39] prevent to much ui updating --- .../user/user/collection/user-collection-header.element.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts index 44dac6b85e..375bbd302d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts @@ -49,9 +49,12 @@ export class UmbUserCollectionHeaderElement extends UmbLitElement { this.observe( observeMultiple([this.#collectionContext.orderByOptions, this.#collectionContext.activeOrderByOption]), ([options, activeOption]) => { - this._orderByOptions = options; + // the options are hardcoded in the context, so we can just compare the length + if (this._orderByOptions.length !== options.length) { + this._orderByOptions = options; + } - if (activeOption) { + if (activeOption && activeOption !== this._activeOrderByOption?.unique) { this._activeOrderByOption = this._orderByOptions.find((option) => option.unique === activeOption); } }, From e2f99743a0f6287f75bf850082862a034511f74e Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:40:52 +0200 Subject: [PATCH 26/39] add a function `onUnload` to be called when an entrypoint is unregistered --- .../has-before-init-export.function.ts | 8 -------- .../functions/has-on-unload-export.function.ts | 8 ++++++++ .../src/libs/extension-api/functions/index.ts | 2 +- .../app-entry-point-extension-initializer.ts | 12 ++++++++---- .../entry-point-extension-initializer.ts | 18 +++++++++++------- .../models/entry-point.interface.ts | 12 +++++++++++- 6 files changed, 39 insertions(+), 21 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-before-init-export.function.ts create mode 100644 src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-on-unload-export.function.ts diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-before-init-export.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-before-init-export.function.ts deleted file mode 100644 index 38b9dd64f4..0000000000 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-before-init-export.function.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { UmbEntryPointModule } from '../models/entry-point.interface.js'; - -/** - * Validate if an ESModule has exported a function called `beforeInit` - */ -export function hasBeforeInitExport(obj: unknown): obj is Pick { - return obj !== null && typeof obj === 'object' && 'beforeInit' in obj; -} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-on-unload-export.function.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-on-unload-export.function.ts new file mode 100644 index 0000000000..716494239c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/functions/has-on-unload-export.function.ts @@ -0,0 +1,8 @@ +import type { UmbEntryPointModule } from '../models/entry-point.interface.js'; + +/** + * Validate if an ESModule has exported a function called `onUnload` + */ +export function hasOnUnloadExport(obj: unknown): obj is Pick { + return obj !== null && typeof obj === 'object' && 'onUnload' 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 394c62c87a..34def52a4a 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,7 +2,7 @@ export * from './create-extension-api.function.js'; export * from './create-extension-element-with-api.function.js'; export * from './create-extension-element.function.js'; export * from './has-init-export.function.js'; -export * from './has-before-init-export.function.js'; +export * from './has-on-unload-export.function.js'; export * from './load-manifest-api.function.js'; export * from './load-manifest-element.function.js'; export * from './load-manifest-plain-css.function.js'; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/app-entry-point-extension-initializer.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/app-entry-point-extension-initializer.ts index 9167e1c8bd..6a3cdee09c 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/app-entry-point-extension-initializer.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/app-entry-point-extension-initializer.ts @@ -1,5 +1,6 @@ import type { ManifestAppEntryPoint } from '../types/index.js'; -import { hasInitExport, loadManifestPlainJs } from '../functions/index.js'; +import { hasInitExport, hasOnUnloadExport, loadManifestPlainJs } from '../functions/index.js'; +import type { UmbEntryPointModule } from '../models/index.js'; import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; import { UmbExtensionInitializerBase } from './extension-initializer-base.js'; import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; @@ -11,6 +12,8 @@ export class UmbAppEntryPointExtensionInitializer extends UmbExtensionInitialize 'appEntryPoint', ManifestAppEntryPoint > { + #jsInstance?: UmbEntryPointModule; + constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry) { super(host, extensionRegistry, 'appEntryPoint'); } @@ -27,8 +30,9 @@ export class UmbAppEntryPointExtensionInitializer extends UmbExtensionInitialize } // eslint-disable-next-line @typescript-eslint/no-unused-vars - unloadExtension(_manifest: ManifestAppEntryPoint): void { - // No-op - // Entry points are not unloaded, but if they were, this is where you would do it. + async unloadExtension(_manifest: ManifestAppEntryPoint): Promise { + if (this.#jsInstance && hasOnUnloadExport(this.#jsInstance)) { + this.#jsInstance.onUnload(this.host, this.extensionRegistry); + } } } 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 011d0f58f5..d3f4898f35 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,5 +1,6 @@ import type { ManifestEntryPoint } from '../types/index.js'; -import { hasInitExport, loadManifestPlainJs } from '../functions/index.js'; +import type { UmbEntryPointModule } from '../models/index.js'; +import { hasInitExport, hasOnUnloadExport, loadManifestPlainJs } from '../functions/index.js'; import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; import { UmbExtensionInitializerBase } from './extension-initializer-base.js'; import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; @@ -8,24 +9,27 @@ import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; * Extension initializer for the `entryPoint` extension type */ export class UmbEntryPointExtensionInitializer extends UmbExtensionInitializerBase<'entryPoint', ManifestEntryPoint> { + #jsInstance?: UmbEntryPointModule; + constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry) { super(host, extensionRegistry, 'entryPoint'); } async instantiateExtension(manifest: ManifestEntryPoint) { if (manifest.js) { - const js = await loadManifestPlainJs(manifest.js); + this.#jsInstance = await loadManifestPlainJs(manifest.js); // If the extension has known exports, be sure to run those - if (hasInitExport(js)) { - js.onInit(this.host, this.extensionRegistry); + if (hasInitExport(this.#jsInstance)) { + this.#jsInstance.onInit(this.host, this.extensionRegistry); } } } // eslint-disable-next-line @typescript-eslint/no-unused-vars - unloadExtension(_manifest: ManifestEntryPoint): void { - // No-op - // Entry points are not unloaded, but if they were, this is where you would do it. + async unloadExtension(_manifest: ManifestEntryPoint): Promise { + if (this.#jsInstance && hasOnUnloadExport(this.#jsInstance)) { + this.#jsInstance.onUnload(this.host, this.extensionRegistry); + } } } 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 ad38d5f3a4..14ea1a2b0d 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 @@ -4,12 +4,22 @@ import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; export type UmbEntryPointOnInit = (host: UmbElement, extensionRegistry: UmbExtensionRegistry) => void; +export type UmbEntryPointOnUnload = (host: UmbElement, extensionRegistry: UmbExtensionRegistry) => void; + /** * Interface containing supported life-cycle functions for ESModule entry points */ export interface UmbEntryPointModule { /** - * Function that will be called when the backoffice element is initialized after authentication. + * Function that will be called when the host element is initialized and/or the extension is loaded for the first time. + * @optional */ onInit: UmbEntryPointOnInit; + + /** + * Function that will be called when the extension is unregistered. + * @remark This does not mean the host element is destroyed, only that the extension is no longer available. You should listen to the host element's `destroy` event if you need to clean up after the host element. + * @optional + */ + onUnload: UmbEntryPointOnUnload; } From b14cbb825557c2f58c3da73b65e72ffd9ae3a74a Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:47:19 +0200 Subject: [PATCH 27/39] format --- src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts index 3ced70e5a4..7d0d88f8fa 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts @@ -84,7 +84,6 @@ export class UmbAppElement extends UmbLitElement { OpenAPI.BASE = window.location.origin; new UmbBundleExtensionInitializer(this, umbExtensionsRegistry); - new UmbAppEntryPointExtensionInitializer(this, umbExtensionsRegistry); new UmbIconRegistry().attach(this); From 8ccabf4e908ac3f247e540168e3159aa2a1a4ddd Mon Sep 17 00:00:00 2001 From: leekelleher Date: Thu, 18 Apr 2024 13:49:39 +0100 Subject: [PATCH 28/39] MemberGroupPicker: fixes sorting --- .../input-member-group.element.ts | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts index 565e13a28e..595996c49c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts @@ -6,26 +6,24 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { MemberItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { splitStringToArray } from '@umbraco-cms/backoffice/utils'; import { UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; -import { type UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter'; - -const SORTER_CONFIG: UmbSorterConfig = { - getUniqueOfElement: (element) => { - return element.getAttribute('detail'); - }, - getUniqueOfModel: (modelEntry) => { - return modelEntry; - }, - identifier: 'Umb.SorterIdentifier.InputMemberGroup', - itemSelector: 'uui-ref-node', - containerSelector: 'uui-ref-list', -}; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; @customElement('umb-input-member-group') export class UmbInputMemberGroupElement extends UUIFormControlMixin(UmbLitElement, '') { - #sorter = new UmbSorterController(this, { - ...SORTER_CONFIG, + #sorter = new UmbSorterController(this, { + getUniqueOfElement: (element) => { + return element.id; + }, + getUniqueOfModel: (modelEntry) => { + return modelEntry; + }, + identifier: 'Umb.SorterIdentifier.InputMemberGroup', + itemSelector: 'uui-ref-node', + containerSelector: 'uui-ref-list', onChange: ({ model }) => { this.selection = model; + this.dispatchEvent(new UmbChangeEvent()); }, }); @@ -197,7 +195,7 @@ export class UmbInputMemberGroupElement extends UUIFormControlMixin(UmbLitElemen // TODO: get the correct variant name const name = item.name; return html` - + ${this.#renderOpenButton(item)} Date: Thu, 18 Apr 2024 14:50:06 +0200 Subject: [PATCH 29/39] add jsdoc --- .../types/manifest-app-entrypoint.interface.ts | 7 +++++-- .../extension-api/types/manifest-entrypoint.interface.ts | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-app-entrypoint.interface.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-app-entrypoint.interface.ts index 72bf874044..1599955564 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-app-entrypoint.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-app-entrypoint.interface.ts @@ -2,8 +2,11 @@ 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 + * Manifest for an `appEntryPoint`, which is loaded up front when the app starts. + * + * 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. + * This is useful for extensions that need to be loaded up front, like an `authProvider`. */ export interface ManifestAppEntryPoint extends ManifestPlainJs { type: 'appEntryPoint'; 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 index 0c400daf64..0277859955 100644 --- 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 @@ -2,8 +2,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 + * Manifest for an `entryPoint`, which is loaded after the Backoffice has been loaded and authentication has been done. + * + * 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'; From 9ae0ce288fa033637f919514ab4e98a816974bc7 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Thu, 18 Apr 2024 13:50:20 +0100 Subject: [PATCH 30/39] MemberGroupPicker: code tidyup --- .../input-member-group.element.ts | 66 +++++++------------ 1 file changed, 22 insertions(+), 44 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts index 595996c49c..121447158b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts @@ -1,13 +1,12 @@ import type { UmbMemberGroupItemModel } from '../../repository/index.js'; import { UmbMemberPickerContext } from './input-member-group.context.js'; import { css, html, customElement, property, state, ifDefined, repeat } from '@umbraco-cms/backoffice/external/lit'; -import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { MemberItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { splitStringToArray } from '@umbraco-cms/backoffice/utils'; -import { UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; +import { UmbModalRouteRegistrationController, UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/modal'; +import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; @customElement('umb-input-member-group') export class UmbInputMemberGroupElement extends UUIFormControlMixin(UmbLitElement, '') { @@ -89,7 +88,6 @@ export class UmbInputMemberGroupElement extends UUIFormControlMixin(UmbLitElemen @property() public set value(idsString: string) { - // Its with full purpose we don't call super.value, as thats being handled by the observation of the context selection. this.selection = splitStringToArray(idsString); } public get value(): string { @@ -110,7 +108,6 @@ export class UmbInputMemberGroupElement extends UUIFormControlMixin(UmbLitElemen constructor() { super(); - // TODO: This would have to be more specific if used in a property editor context... [NL] new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) .addAdditionalPath('member-group') .onSetup(() => { @@ -120,10 +117,8 @@ export class UmbInputMemberGroupElement extends UUIFormControlMixin(UmbLitElemen this._editMemberGroupPath = routeBuilder({}); }); - this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(','))); - this.observe(this.#pickerContext.selectedItems, (selectedItems) => { - this._items = selectedItems; - }); + this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(',')), '_observeSelection'); + this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems), '_observeItems'); } connectedCallback(): void { @@ -142,16 +137,6 @@ export class UmbInputMemberGroupElement extends UUIFormControlMixin(UmbLitElemen ); } - protected _openPicker() { - this.#pickerContext.openPicker({ - hideTreeRoot: true, - }); - } - - protected _requestRemoveItem(item: UmbMemberGroupItemModel) { - this.#pickerContext.requestRemoveItem(item.unique!); - } - protected getFormElement() { return undefined; } @@ -162,29 +147,31 @@ export class UmbInputMemberGroupElement extends UUIFormControlMixin(UmbLitElemen }); } - #requestRemoveItem(item: MemberItemResponseModel) { - this.#pickerContext.requestRemoveItem(item.id!); + #removeItem(item: UmbMemberGroupItemModel) { + this.#pickerContext.requestRemoveItem(item.unique); } render() { - return html` ${this.#renderItems()} ${this.#renderAddButton()} `; + return html`${this.#renderItems()} ${this.#renderAddButton()}`; } #renderItems() { if (!this._items) return; - return html` - ${repeat( - this._items, - (item) => item.unique, - (item) => this.#renderItem(item), - )} - `; + return html` + + ${repeat( + this._items, + (item) => item.unique, + (item) => this.#renderItem(item), + )} + + `; } #renderAddButton() { if (this.max === 1 && this.selection.length >= this.max) return; return html``; @@ -192,17 +179,11 @@ export class UmbInputMemberGroupElement extends UUIFormControlMixin(UmbLitElemen #renderItem(item: UmbMemberGroupItemModel) { if (!item.unique) return; - // TODO: get the correct variant name - const name = item.name; return html` ${this.#renderOpenButton(item)} - this._requestRemoveItem(item)} - label="${this.localize.term('general_remove')} ${name}"> - ${this.localize.term('general_remove')} - + this.#removeItem(item)} label=${this.localize.term('general_remove')}> `; @@ -210,21 +191,18 @@ export class UmbInputMemberGroupElement extends UUIFormControlMixin(UmbLitElemen #renderOpenButton(item: UmbMemberGroupItemModel) { if (!this.showOpenButton) return; - // TODO: get the correct variant name - const name = item.name; return html` - + label="${this.localize.term('general_open')} ${item.name}"> + ${this.localize.term('general_open')} `; } static styles = [ css` - #add-button { + #btn-add { width: 100%; } From 84b96a42eed626b790b2d2b419da86eaebf67662 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Thu, 18 Apr 2024 13:50:37 +0100 Subject: [PATCH 31/39] MemberGroupPicker: property-editor code tidyup --- ...y-editor-ui-member-group-picker.element.ts | 61 ++++++------------- 1 file changed, 20 insertions(+), 41 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/member-group-picker/property-editor-ui-member-group-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/member-group-picker/property-editor-ui-member-group-picker.element.ts index d3eeb31651..e223e3fa18 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/member-group-picker/property-editor-ui-member-group-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/member-group-picker/property-editor-ui-member-group-picker.element.ts @@ -1,67 +1,46 @@ -import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; -import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; +import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; +import type { NumberRangeValueType } from '@umbraco-cms/backoffice/models'; import type { UmbInputMemberGroupElement } from '@umbraco-cms/backoffice/member-group'; +import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; +import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; /** * @element umb-property-editor-ui-member-group-picker */ @customElement('umb-property-editor-ui-member-group-picker') export class UmbPropertyEditorUIMemberGroupPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement { - // private _value: Array = []; - - // @property({ type: Array }) - // public set value(value: Array) { - // this._value = Array.isArray(value) ? value : value ? [value] : []; - // } - // public get value(): Array { - // return this._value; - // } - - @property({ type: String }) - public value: string = ''; + @property() + public value?: string; public set config(config: UmbPropertyEditorConfigCollection | undefined) { - const validationLimit = config?.find((x) => x.alias === 'validationLimit'); + if (!config) return; - this._limitMin = (validationLimit?.value as any)?.min; - this._limitMax = (validationLimit?.value as any)?.max; + const minMax = config?.getValueByAlias('validationLimit'); + this.min = minMax?.min ?? 0; + this.max = minMax?.max ?? Infinity; } @state() - _items: Array = []; + min = 0; @state() - private _limitMin?: number; - @state() - private _limitMax?: number; + max = Infinity; - protected updated(_changedProperties: PropertyValueMap | Map): void { - super.updated(_changedProperties); - if (_changedProperties.has('value')) { - this._items = this.value ? this.value.split(',') : []; - } - } - - private _onChange(event: CustomEvent) { - //TODO: This is a hack, something changed so now we need to convert the array to a comma separated string to make it work with the server. - const toCommaSeparatedString = (event.target as UmbInputMemberGroupElement).selection.join(','); - // this.value = (event.target as UmbInputMemberGroupElement).selection; - this.value = toCommaSeparatedString; - this.dispatchEvent(new CustomEvent('property-value-change')); + #onChange(event: CustomEvent & { target: UmbInputMemberGroupElement }) { + this.value = event.target.value; + this.dispatchEvent(new UmbPropertyValueChangeEvent()); } render() { return html` Add + .min=${this.min} + .max=${this.max} + .value=${this.value ?? ''} + ?showOpenButton=${true} + @change=${this.#onChange}> `; } } From 6eab243a785d71687820a8ed2681fe1ea36b3c55 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:51:27 +0200 Subject: [PATCH 32/39] add ManifestAppEntryPoint to the manifest types --- .../src/packages/core/extension-registry/models/index.ts | 2 ++ 1 file changed, 2 insertions(+) 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 b9d2934554..b9b9a22ce0 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 @@ -62,6 +62,7 @@ import type { ManifestGranularUserPermission } from './user-granular-permission. import type { ManifestCollectionAction } from './collection-action.model.js'; import type { ManifestMfaLoginProvider } from './mfa-login-provider.model.js'; import type { + ManifestAppEntryPoint, ManifestBase, ManifestBundle, ManifestCondition, @@ -139,6 +140,7 @@ export type ManifestWorkspaces = ManifestWorkspace | ManifestWorkspaceRoutableKi export type ManifestWorkspaceViews = ManifestWorkspaceView | ManifestWorkspaceViewContentTypeDesignEditorKind; export type ManifestTypes = + | ManifestAppEntryPoint | ManifestAuthProvider | ManifestBundle | ManifestBlockEditorCustomView From b8260d8a1728195550ea6189736c8da9a00c7ad4 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:51:43 +0200 Subject: [PATCH 33/39] cleanup imports --- .../src/apps/backoffice/backoffice.element.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts index 72babdddbe..46ec1ef946 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts @@ -2,7 +2,6 @@ import { UmbBackofficeContext } from './backoffice.context.js'; import { css, html, customElement } from '@umbraco-cms/backoffice/external/lit'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { - UmbBundleExtensionInitializer, UmbEntryPointExtensionInitializer, UmbServerExtensionRegistrator, } from '@umbraco-cms/backoffice/extension-api'; From 45d16413b936ccefed3c6a626fabda659d312427 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 18 Apr 2024 15:00:13 +0200 Subject: [PATCH 34/39] save module instances to a map --- .../app-entry-point-extension-initializer.ts | 25 +++++++++++++------ .../entry-point-extension-initializer.ts | 25 +++++++++++++------ 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/app-entry-point-extension-initializer.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/app-entry-point-extension-initializer.ts index 6a3cdee09c..570dafe356 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/app-entry-point-extension-initializer.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/app-entry-point-extension-initializer.ts @@ -12,7 +12,7 @@ export class UmbAppEntryPointExtensionInitializer extends UmbExtensionInitialize 'appEntryPoint', ManifestAppEntryPoint > { - #jsInstance?: UmbEntryPointModule; + #instanceMap = new Map(); constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry) { super(host, extensionRegistry, 'appEntryPoint'); @@ -20,19 +20,28 @@ export class UmbAppEntryPointExtensionInitializer extends UmbExtensionInitialize async instantiateExtension(manifest: ManifestAppEntryPoint) { if (manifest.js) { - const js = await loadManifestPlainJs(manifest.js); + const moduleInstance = await loadManifestPlainJs(manifest.js); + + if (!moduleInstance) return; + + this.#instanceMap.set(manifest.alias, moduleInstance); // If the extension has known exports, be sure to run those - if (hasInitExport(js)) { - js.onInit(this.host, this.extensionRegistry); + if (hasInitExport(moduleInstance)) { + moduleInstance.onInit(this.host, this.extensionRegistry); } } } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async unloadExtension(_manifest: ManifestAppEntryPoint): Promise { - if (this.#jsInstance && hasOnUnloadExport(this.#jsInstance)) { - this.#jsInstance.onUnload(this.host, this.extensionRegistry); + async unloadExtension(manifest: ManifestAppEntryPoint): Promise { + const moduleInstance = this.#instanceMap.get(manifest.alias); + + if (!moduleInstance) return; + + if (hasOnUnloadExport(moduleInstance)) { + moduleInstance.onUnload(this.host, this.extensionRegistry); } + + this.#instanceMap.delete(manifest.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 d3f4898f35..b5cd9d678b 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 @@ -9,7 +9,7 @@ import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; * Extension initializer for the `entryPoint` extension type */ export class UmbEntryPointExtensionInitializer extends UmbExtensionInitializerBase<'entryPoint', ManifestEntryPoint> { - #jsInstance?: UmbEntryPointModule; + #instanceMap = new Map(); constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry) { super(host, extensionRegistry, 'entryPoint'); @@ -17,19 +17,28 @@ export class UmbEntryPointExtensionInitializer extends UmbExtensionInitializerBa async instantiateExtension(manifest: ManifestEntryPoint) { if (manifest.js) { - this.#jsInstance = await loadManifestPlainJs(manifest.js); + const moduleInstance = await loadManifestPlainJs(manifest.js); + + if (!moduleInstance) return; + + this.#instanceMap.set(manifest.alias, moduleInstance); // If the extension has known exports, be sure to run those - if (hasInitExport(this.#jsInstance)) { - this.#jsInstance.onInit(this.host, this.extensionRegistry); + if (hasInitExport(moduleInstance)) { + moduleInstance.onInit(this.host, this.extensionRegistry); } } } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async unloadExtension(_manifest: ManifestEntryPoint): Promise { - if (this.#jsInstance && hasOnUnloadExport(this.#jsInstance)) { - this.#jsInstance.onUnload(this.host, this.extensionRegistry); + async unloadExtension(manifest: ManifestEntryPoint): Promise { + const moduleInstance = this.#instanceMap.get(manifest.alias); + + if (!moduleInstance) return; + + if (hasOnUnloadExport(moduleInstance)) { + moduleInstance.onUnload(this.host, this.extensionRegistry); } + + this.#instanceMap.delete(manifest.alias); } } From d8b659291ede0f16fd4ae04bd563f763c77e7394 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 18 Apr 2024 15:28:14 +0200 Subject: [PATCH 35/39] use objects instead of enums --- .../src/packages/core/models/index.ts | 5 --- .../packages/core/utils/direction/index.ts | 6 +++ .../src/packages/core/utils/index.ts | 9 ++-- .../user-collection.server.data-source.ts | 7 ++-- .../packages/user/user/collection/types.ts | 28 ++++--------- .../user-collection-header.element.ts | 9 ++-- .../collection/user-collection.context.ts | 41 ++++++++++--------- .../user/user/collection/utils/index.ts | 18 ++++++++ 8 files changed, 65 insertions(+), 58 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/utils/direction/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/user/collection/utils/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts index 6c6bb47ba0..a95a2244a8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts @@ -50,8 +50,3 @@ export interface UmbUniqueItemModel { name: string; icon?: string; } - -export enum UmbDirectionModel { - ASCENDING = 'Ascending', - DESCENDING = 'Descending', -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/direction/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/direction/index.ts new file mode 100644 index 0000000000..d427c102b1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/direction/index.ts @@ -0,0 +1,6 @@ +export type UmbDirectionType = 'Ascending' | 'Descending'; + +export const UmbDirection = Object.freeze({ + ASCENDING: 'Ascending', + DESCENDING: 'Descending', +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts index 3782c9eb05..ad688e29d5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts @@ -1,3 +1,6 @@ +export * from './debounce/debounce.function.js'; +export * from './direction/index.js'; +export * from './download/blob-download.function.js'; export * from './get-processed-image-url.function.js'; export * from './math/math.js'; export * from './pagination-manager/pagination.manager.js'; @@ -7,11 +10,9 @@ export * from './path/path-encode.function.js'; export * from './path/path-folder-name.function.js'; export * from './path/umbraco-path.function.js'; export * from './selection-manager/selection.manager.js'; +export * from './string/from-camel-case.function.js'; export * from './string/generate-umbraco-alias.function.js'; export * from './string/increment-string.function.js'; export * from './string/split-string-to-array.js'; -export * from './type/diff.type.js'; export * from './string/to-camel-case/to-camel-case.function.js'; -export * from './string/from-camel-case.function.js'; -export * from './debounce/debounce.function.js'; -export * from './download/blob-download.function.js'; +export * from './type/diff.type.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/repository/user-collection.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/repository/user-collection.server.data-source.ts index eaf765fd5a..37b5078a7a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/repository/user-collection.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/repository/user-collection.server.data-source.ts @@ -37,17 +37,16 @@ export class UmbUserCollectionServerDataSource implements UmbCollectionDataSourc * @memberof UmbUserCollectionServerDataSource */ async getCollection(filter: UmbUserCollectionFilterModel) { - // TODO: This is a temporary workaround to avoid a type error. const { data, error } = await tryExecuteAndNotify( this.#host, UserService.getFilterUser({ filter: filter.filter, - orderBy: filter.orderBy as unknown as UserOrderModel, - orderDirection: filter.orderDirection as unknown as DirectionModel, + orderBy: filter.orderBy as unknown as UserOrderModel, // TODO: This is a temporary workaround to avoid a type error. + orderDirection: filter.orderDirection as unknown as DirectionModel, // TODO: This is a temporary workaround to avoid a type error. skip: filter.skip, take: filter.take, userGroupIds: filter.userGroupIds, - userStates: filter.userStates as unknown as Array, + userStates: filter.userStates as unknown as Array, // TODO: This is a temporary workaround to avoid a type error. }), ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts index 1a259f10f1..a1d5e5c99a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/types.ts @@ -1,12 +1,13 @@ -import type { UmbDirectionModel } from '@umbraco-cms/backoffice/models'; +import type { UmbUserOrderByType, UmbUserStateFilterType } from './utils/index.js'; +import type { UmbDirectionType } from '@umbraco-cms/backoffice/utils'; export interface UmbUserCollectionFilterModel { skip?: number; take?: number; - orderBy?: UmbUserOrderByModel; - orderDirection?: UmbDirectionModel; + orderBy?: UmbUserOrderByType; + orderDirection?: UmbDirectionType; userGroupIds?: string[]; - userStates?: UmbUserStateFilterModel[]; + userStates?: UmbUserStateFilterType[]; filter?: string; } @@ -14,22 +15,7 @@ export interface UmbUserOrderByOption { unique: string; label: string; config: { - orderBy: UmbUserOrderByModel; - orderDirection: UmbDirectionModel; + orderBy: UmbUserOrderByType; + orderDirection: UmbDirectionType; }; } - -export enum UmbUserOrderByModel { - NAME = 'Name', - CREATE_DATE = 'CreateDate', - LAST_LOGIN_DATE = 'LastLoginDate', -} - -export enum UmbUserStateFilterModel { - ACTIVE = 'Active', - DISABLED = 'Disabled', - LOCKED_OUT = 'LockedOut', - INVITED = 'Invited', - INACTIVE = 'Inactive', - ALL = 'All', -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts index 375bbd302d..4147e35f1d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection-header.element.ts @@ -1,6 +1,7 @@ import type { UmbUserCollectionContext } from './user-collection.context.js'; import type { UmbUserOrderByOption } from './types.js'; -import { UmbUserStateFilterModel } from './types.js'; +import type { UmbUserStateFilterType } from './utils/index.js'; +import { UmbUserStateFilter } from './utils/index.js'; import type { UUIBooleanInputEvent, UUICheckboxElement } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @@ -12,10 +13,10 @@ import { observeMultiple } from '@umbraco-cms/backoffice/observable-api'; @customElement('umb-user-collection-header') export class UmbUserCollectionHeaderElement extends UmbLitElement { @state() - private _stateFilterOptions: Array = Object.values(UmbUserStateFilterModel); + private _stateFilterOptions: Array = Object.values(UmbUserStateFilter); @state() - private _stateFilterSelection: Array = []; + private _stateFilterSelection: Array = []; @state() private _userGroups: Array = []; @@ -84,7 +85,7 @@ export class UmbUserCollectionHeaderElement extends UmbLitElement { #onStateFilterChange(event: UUIBooleanInputEvent) { event.stopPropagation(); const target = event.currentTarget as UUICheckboxElement; - const value = target.value as UmbUserStateFilterModel; + const value = target.value as UmbUserStateFilterType; const isChecked = target.checked; this._stateFilterSelection = isChecked diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection.context.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection.context.ts index 7246c8ee7d..3c81c52ace 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/user-collection.context.ts @@ -1,51 +1,53 @@ import type { UmbUserDetailModel } from '../types.js'; import { UMB_COLLECTION_VIEW_USER_GRID } from './views/index.js'; -import type { UmbUserCollectionFilterModel, UmbUserOrderByOption, UmbUserStateFilterModel } from './types.js'; -import { UmbUserOrderByModel } from './types.js'; +import type { UmbUserCollectionFilterModel, UmbUserOrderByOption } from './types.js'; +import type { UmbUserOrderByType, UmbUserStateFilterType } from './utils/index.js'; +import { UmbUserOrderBy } from './utils/index.js'; import { UmbDefaultCollectionContext } from '@umbraco-cms/backoffice/collection'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UmbDirectionModel } from '@umbraco-cms/backoffice/models'; import { UmbArrayState, UmbStringState } from '@umbraco-cms/backoffice/observable-api'; +import type { UmbDirectionType } from '@umbraco-cms/backoffice/utils'; +import { UmbDirection } from '@umbraco-cms/backoffice/utils'; const orderByOptions: Array = [ { unique: 'nameAscending', label: '#user_sortNameAscending', config: { - orderBy: UmbUserOrderByModel.NAME, - orderDirection: UmbDirectionModel.ASCENDING, + orderBy: UmbUserOrderBy.NAME, + orderDirection: UmbDirection.ASCENDING, }, }, { unique: 'nameDescending', label: '#user_sortNameDescending', config: { - orderBy: UmbUserOrderByModel.NAME, - orderDirection: UmbDirectionModel.DESCENDING, + orderBy: UmbUserOrderBy.NAME, + orderDirection: UmbDirection.DESCENDING, }, }, { unique: 'createDateDescending', label: '#user_sortCreateDateDescending', config: { - orderBy: UmbUserOrderByModel.CREATE_DATE, - orderDirection: UmbDirectionModel.DESCENDING, + orderBy: UmbUserOrderBy.CREATE_DATE, + orderDirection: UmbDirection.DESCENDING, }, }, { unique: 'createDateAscending', label: '#user_sortCreateDateAscending', config: { - orderBy: UmbUserOrderByModel.CREATE_DATE, - orderDirection: UmbDirectionModel.ASCENDING, + orderBy: UmbUserOrderBy.CREATE_DATE, + orderDirection: UmbDirection.ASCENDING, }, }, { unique: 'lastLoginDateDescending', label: '#user_sortLastLoginDateDescending', config: { - orderBy: UmbUserOrderByModel.LAST_LOGIN_DATE, - orderDirection: UmbDirectionModel.DESCENDING, + orderBy: UmbUserOrderBy.LAST_LOGIN_DATE, + orderDirection: UmbDirection.DESCENDING, }, }, ]; @@ -73,9 +75,8 @@ export class UmbUserCollectionContext extends UmbDefaultCollectionContext< } /** - * Sets the active order by for the collection and refreshes the collection. - * @param {UmbUserOrderByModel} orderBy - * @param {UmbDirectionModel} orderDirection + * Sets the active order by option for the collection and refreshes the collection. + * @param {string} unique * @memberof UmbUserCollectionContext */ setActiveOrderByOption(unique: string) { @@ -89,7 +90,7 @@ export class UmbUserCollectionContext extends UmbDefaultCollectionContext< * @param {Array} selection * @memberof UmbUserCollectionContext */ - setStateFilter(selection: Array) { + setStateFilter(selection: Array) { this.setFilter({ userStates: selection }); } @@ -98,7 +99,7 @@ export class UmbUserCollectionContext extends UmbDefaultCollectionContext< * @param {UmbUserOrderByModel} orderBy * @memberof UmbUserCollectionContext */ - setOrderByFilter(orderBy: UmbUserOrderByModel) { + setOrderByFilter(orderBy: UmbUserOrderByType) { this.setFilter({ orderBy }); } @@ -113,10 +114,10 @@ export class UmbUserCollectionContext extends UmbDefaultCollectionContext< /** * Sets the order direction filter for the collection and refreshes the collection. - * @param {UmbDirectionModel} orderDirection + * @param {any} orderDirection * @memberof UmbUserCollectionContext */ - setOrderDirectionFilter(orderDirection: UmbDirectionModel) { + setOrderDirectionFilter(orderDirection: UmbDirectionType) { this.setFilter({ orderDirection }); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/utils/index.ts new file mode 100644 index 0000000000..21a5ac4fb3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/collection/utils/index.ts @@ -0,0 +1,18 @@ +export type UmbUserOrderByType = 'Name' | 'CreateDate' | 'LastLoginDate'; + +export const UmbUserOrderBy = Object.freeze({ + NAME: 'Name', + CREATE_DATE: 'CreateDate', + LAST_LOGIN_DATE: 'LastLoginDate', +}); + +export type UmbUserStateFilterType = 'Active' | 'Disabled' | 'LockedOut' | 'Invited' | 'Inactive' | 'All'; + +export const UmbUserStateFilter = Object.freeze({ + ACTIVE: 'Active', + DISABLED: 'Disabled', + LOCKED_OUT: 'LockedOut', + INVITED: 'Invited', + INACTIVE: 'Inactive', + ALL: 'All', +}); From ae2bb99b461a128b05fd8b926801f9ef02781e62 Mon Sep 17 00:00:00 2001 From: Liam Laverty Date: Thu, 18 Apr 2024 14:33:34 +0100 Subject: [PATCH 36/39] Corrects "minimum" to "maximum" in the integer and decimal property editors --- .../packages/core/property-editor/schemas/Umbraco.Decimal.ts | 2 +- .../packages/core/property-editor/schemas/Umbraco.Integer.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.Decimal.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.Decimal.ts index 88977ccbde..20d8f4537c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.Decimal.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.Decimal.ts @@ -17,7 +17,7 @@ export const manifest: ManifestPropertyEditorSchema = { { alias: 'max', label: 'Maximum', - description: 'Enter the minimum amount of number to be entered', + description: 'Enter the maximum amount of number to be entered', propertyEditorUiAlias: 'Umb.PropertyEditorUi.Decimal', }, { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.Integer.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.Integer.ts index e4a699a8b9..e1cedaf759 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.Integer.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/schemas/Umbraco.Integer.ts @@ -17,7 +17,7 @@ export const manifest: ManifestPropertyEditorSchema = { { alias: 'max', label: 'Maximum', - description: 'Enter the minimum amount of number to be entered', + description: 'Enter the maximum amount of number to be entered', propertyEditorUiAlias: 'Umb.PropertyEditorUi.Number', }, { From bc9aad88bb69522ceb29c1963492b9177b1c99dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 19 Apr 2024 13:23:45 +0200 Subject: [PATCH 37/39] backofficeEntryPoint --- .../src/apps/app/app.element.ts | 7 ++- .../src/apps/backoffice/backoffice.element.ts | 8 +-- .../src/libs/extension-api/index.ts | 2 +- .../libs/extension-api/initializers/index.ts | 2 - .../src/libs/extension-api/types/index.ts | 2 - .../src/mocks/handlers/manifests.handlers.ts | 2 +- .../packages/core/extension-registry/index.ts | 5 +- .../app-entry-point-extension-initializer.ts | 16 +++--- ...ffice-entry-point-extension-initializer.ts | 51 +++++++++++++++++++ .../entry-point-extension-initializer.ts | 16 +++--- .../extension-registry/initializers/index.ts | 3 ++ .../models/app-entry-point.model.ts} | 4 +- .../models/backoffice-entry-point.model.ts | 12 +++++ .../models/entry-point.model.ts} | 6 ++- .../core/extension-registry/models/index.ts | 21 ++++---- 15 files changed, 116 insertions(+), 41 deletions(-) rename src/Umbraco.Web.UI.Client/src/{libs/extension-api => packages/core/extension-registry}/initializers/app-entry-point-extension-initializer.ts (73%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/initializers/backoffice-entry-point-extension-initializer.ts rename src/Umbraco.Web.UI.Client/src/{libs/extension-api => packages/core/extension-registry}/initializers/entry-point-extension-initializer.ts (73%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/initializers/index.ts rename src/Umbraco.Web.UI.Client/src/{libs/extension-api/types/manifest-app-entrypoint.interface.ts => packages/core/extension-registry/models/app-entry-point.model.ts} (72%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/backoffice-entry-point.model.ts rename src/Umbraco.Web.UI.Client/src/{libs/extension-api/types/manifest-entrypoint.interface.ts => packages/core/extension-registry/models/entry-point.model.ts} (63%) diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts index 7d0d88f8fa..3991d8ad3e 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts @@ -13,12 +13,11 @@ import type { Guard, UmbRoute } from '@umbraco-cms/backoffice/router'; import { pathWithoutBasePath } from '@umbraco-cms/backoffice/router'; import { OpenAPI, RuntimeLevelModel } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbContextDebugController } from '@umbraco-cms/backoffice/debug'; +import { UmbBundleExtensionInitializer, UmbServerExtensionRegistrator } from '@umbraco-cms/backoffice/extension-api'; import { UmbAppEntryPointExtensionInitializer, - UmbBundleExtensionInitializer, - UmbServerExtensionRegistrator, -} from '@umbraco-cms/backoffice/extension-api'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; + umbExtensionsRegistry, +} from '@umbraco-cms/backoffice/extension-registry'; @customElement('umb-app') export class UmbAppElement extends UmbLitElement { diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts index 46ec1ef946..2bf0c934e5 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts @@ -1,10 +1,11 @@ import { UmbBackofficeContext } from './backoffice.context.js'; import { css, html, customElement } from '@umbraco-cms/backoffice/external/lit'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { + UmbBackofficeEntryPointExtensionInitializer, UmbEntryPointExtensionInitializer, - UmbServerExtensionRegistrator, -} from '@umbraco-cms/backoffice/extension-api'; + umbExtensionsRegistry, +} from '@umbraco-cms/backoffice/extension-registry'; +import { UmbServerExtensionRegistrator } from '@umbraco-cms/backoffice/extension-api'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import './components/index.js'; @@ -51,6 +52,7 @@ export class UmbBackofficeElement extends UmbLitElement { new UmbBackofficeContext(this); + new UmbBackofficeEntryPointExtensionInitializer(this, umbExtensionsRegistry); new UmbEntryPointExtensionInitializer(this, umbExtensionsRegistry); new UmbServerExtensionRegistrator(this, umbExtensionsRegistry).registerPrivateExtensions(); 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 22c8863cb0..068ea83553 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 @@ -2,7 +2,7 @@ export * from './condition/index.js'; export * from './controller/index.js'; export * from './functions/index.js'; export * from './initializers/index.js'; -export type * from './models/index.js'; export * from './registry/extension.registry.js'; export * from './type-guards/index.js'; +export type * from './models/index.js'; export type * from './types/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts index 38b630c03a..cff4382b81 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/index.ts @@ -1,4 +1,2 @@ -export * from './app-entry-point-extension-initializer.js'; export * from './bundle-extension-initializer.js'; -export * from './entry-point-extension-initializer.js'; export * from './extension-initializer-base.js'; 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 index 6577a9fccc..2419049ef3 100644 --- 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 @@ -1,9 +1,7 @@ export * from './base.types.js'; export * from './condition.types.js'; -export * from './manifest-app-entrypoint.interface.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'; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/manifests.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/manifests.handlers.ts index fbfc33ed1a..20da4fec15 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/manifests.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/manifests.handlers.ts @@ -56,7 +56,7 @@ export const manifestDevelopmentHandlers = [ name: 'Package with an entry point', extensions: [ { - type: 'entryPoint', + type: 'backofficeEntryPoint', name: 'My Custom Entry Point', alias: 'My.Entrypoint.Custom', js: '/App_Plugins/custom-entrypoint.js', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/index.ts index 7442cd6ae9..017d4122e2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/index.ts @@ -1,7 +1,8 @@ export * from './conditions/index.js'; -export type * from './interfaces/index.js'; -export type * from './models/index.js'; +export * from './initializers/index.js'; export * from './registry.js'; export * from './utils/index.js'; +export type * from './interfaces/index.js'; +export type * from './models/index.js'; export { UmbExtensionElementAndApiSlotElementBase } from './extension-element-and-api-slot-element-base.js'; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/app-entry-point-extension-initializer.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/initializers/app-entry-point-extension-initializer.ts similarity index 73% rename from src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/app-entry-point-extension-initializer.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/initializers/app-entry-point-extension-initializer.ts index 570dafe356..877b118b79 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/app-entry-point-extension-initializer.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/initializers/app-entry-point-extension-initializer.ts @@ -1,9 +1,13 @@ -import type { ManifestAppEntryPoint } from '../types/index.js'; -import { hasInitExport, hasOnUnloadExport, loadManifestPlainJs } from '../functions/index.js'; -import type { UmbEntryPointModule } from '../models/index.js'; -import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; -import { UmbExtensionInitializerBase } from './extension-initializer-base.js'; +import type { ManifestAppEntryPoint } from '../models/app-entry-point.model.js'; import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; +import { + type UmbEntryPointModule, + UmbExtensionInitializerBase, + type UmbExtensionRegistry, + loadManifestPlainJs, + hasInitExport, + hasOnUnloadExport, +} from '@umbraco-cms/backoffice/extension-api'; /** * Extension initializer for the `appEntryPoint` extension type @@ -15,7 +19,7 @@ export class UmbAppEntryPointExtensionInitializer extends UmbExtensionInitialize #instanceMap = new Map(); constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry) { - super(host, extensionRegistry, 'appEntryPoint'); + super(host, extensionRegistry, 'appEntryPoint' satisfies ManifestAppEntryPoint['type']); } async instantiateExtension(manifest: ManifestAppEntryPoint) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/initializers/backoffice-entry-point-extension-initializer.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/initializers/backoffice-entry-point-extension-initializer.ts new file mode 100644 index 0000000000..29cec8c445 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/initializers/backoffice-entry-point-extension-initializer.ts @@ -0,0 +1,51 @@ +import type { ManifestBackofficeEntryPoint } from '../models/backoffice-entry-point.model.js'; +import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; +import { + type UmbEntryPointModule, + UmbExtensionInitializerBase, + type UmbExtensionRegistry, + loadManifestPlainJs, + hasInitExport, + hasOnUnloadExport, +} from '@umbraco-cms/backoffice/extension-api'; + +/** + * Extension initializer for the `backofficeEntryPoint` extension type + */ +export class UmbBackofficeEntryPointExtensionInitializer extends UmbExtensionInitializerBase< + 'backofficeEntryPoint', + ManifestBackofficeEntryPoint +> { + #instanceMap = new Map(); + + constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry) { + super(host, extensionRegistry, 'backofficeEntryPoint' satisfies ManifestBackofficeEntryPoint['type']); + } + + async instantiateExtension(manifest: ManifestBackofficeEntryPoint) { + if (manifest.js) { + const moduleInstance = await loadManifestPlainJs(manifest.js); + + if (!moduleInstance) return; + + this.#instanceMap.set(manifest.alias, moduleInstance); + + // If the extension has known exports, be sure to run those + if (hasInitExport(moduleInstance)) { + moduleInstance.onInit(this.host, this.extensionRegistry); + } + } + } + + async unloadExtension(manifest: ManifestBackofficeEntryPoint): Promise { + const moduleInstance = this.#instanceMap.get(manifest.alias); + + if (!moduleInstance) return; + + if (hasOnUnloadExport(moduleInstance)) { + moduleInstance.onUnload(this.host, this.extensionRegistry); + } + + this.#instanceMap.delete(manifest.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/packages/core/extension-registry/initializers/entry-point-extension-initializer.ts similarity index 73% rename from src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/entry-point-extension-initializer.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/initializers/entry-point-extension-initializer.ts index b5cd9d678b..1635e7c84f 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/initializers/entry-point-extension-initializer.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/initializers/entry-point-extension-initializer.ts @@ -1,9 +1,13 @@ -import type { ManifestEntryPoint } from '../types/index.js'; -import type { UmbEntryPointModule } from '../models/index.js'; -import { hasInitExport, hasOnUnloadExport, loadManifestPlainJs } from '../functions/index.js'; -import type { UmbExtensionRegistry } from '../registry/extension.registry.js'; -import { UmbExtensionInitializerBase } from './extension-initializer-base.js'; +import type { ManifestEntryPoint } from '../models/entry-point.model.js'; import type { UmbElement } from '@umbraco-cms/backoffice/element-api'; +import { + type UmbEntryPointModule, + UmbExtensionInitializerBase, + type UmbExtensionRegistry, + loadManifestPlainJs, + hasInitExport, + hasOnUnloadExport, +} from '@umbraco-cms/backoffice/extension-api'; /** * Extension initializer for the `entryPoint` extension type @@ -12,7 +16,7 @@ export class UmbEntryPointExtensionInitializer extends UmbExtensionInitializerBa #instanceMap = new Map(); constructor(host: UmbElement, extensionRegistry: UmbExtensionRegistry) { - super(host, extensionRegistry, 'entryPoint'); + super(host, extensionRegistry, 'entryPoint' satisfies ManifestEntryPoint['type']); } async instantiateExtension(manifest: ManifestEntryPoint) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/initializers/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/initializers/index.ts new file mode 100644 index 0000000000..be40e27b2e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/initializers/index.ts @@ -0,0 +1,3 @@ +export * from './app-entry-point-extension-initializer.js'; +export * from './backoffice-entry-point-extension-initializer.js'; +export * from './entry-point-extension-initializer.js'; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-app-entrypoint.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/app-entry-point.model.ts similarity index 72% rename from src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-app-entrypoint.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/app-entry-point.model.ts index 1599955564..009c9ca2d9 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-app-entrypoint.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/app-entry-point.model.ts @@ -1,5 +1,5 @@ -import type { UmbEntryPointModule } from '../models/index.js'; -import type { ManifestPlainJs } from './base.types.js'; +import type { UmbEntryPointModule } from '../../../../libs/extension-api/models/index.js'; +import type { ManifestPlainJs } from '../../../../libs/extension-api/types/base.types.js'; /** * Manifest for an `appEntryPoint`, which is loaded up front when the app starts. diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/backoffice-entry-point.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/backoffice-entry-point.model.ts new file mode 100644 index 0000000000..5477c90102 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/backoffice-entry-point.model.ts @@ -0,0 +1,12 @@ +import type { UmbEntryPointModule } from '../../../../libs/extension-api/models/index.js'; +import type { ManifestPlainJs } from '../../../../libs/extension-api/types/base.types.js'; + +/** + * Manifest for an `backofficeEntryPoint`, which is loaded after the Backoffice has been loaded and authentication has been done. + * + * 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 ManifestBackofficeEntryPoint extends ManifestPlainJs { + type: 'backofficeEntryPoint'; +} diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-entrypoint.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/entry-point.model.ts similarity index 63% rename from src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-entrypoint.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/entry-point.model.ts index 0277859955..9408719eed 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/types/manifest-entrypoint.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/entry-point.model.ts @@ -1,11 +1,13 @@ -import type { UmbEntryPointModule } from '../models/index.js'; -import type { ManifestPlainJs } from './base.types.js'; +import type { UmbEntryPointModule } from '../../../../libs/extension-api/models/index.js'; +import type { ManifestPlainJs } from '../../../../libs/extension-api/types/base.types.js'; /** * Manifest for an `entryPoint`, which is loaded after the Backoffice has been loaded and authentication has been done. * * 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. + * + * @deprecated Use `ManifestBackofficeEntryPoint` instead. */ export interface ManifestEntryPoint extends ManifestPlainJs { type: 'entryPoint'; 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 b9b9a22ce0..3a50d513c9 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 @@ -61,25 +61,25 @@ import type { ManifestEntityUserPermission } from './entity-user-permission.mode import type { ManifestGranularUserPermission } from './user-granular-permission.model.js'; import type { ManifestCollectionAction } from './collection-action.model.js'; import type { ManifestMfaLoginProvider } from './mfa-login-provider.model.js'; -import type { - ManifestAppEntryPoint, - ManifestBase, - ManifestBundle, - ManifestCondition, - ManifestEntryPoint, -} from '@umbraco-cms/backoffice/extension-api'; +import type { ManifestAppEntryPoint } from './app-entry-point.model.js'; +import type { ManifestBackofficeEntryPoint } from './backoffice-entry-point.model.js'; +import type { ManifestEntryPoint } from './entry-point.model.js'; +import type { ManifestBase, ManifestBundle, ManifestCondition } from '@umbraco-cms/backoffice/extension-api'; export type * from './auth-provider.model.js'; +export type * from './backoffice-entry-point.model.js'; export type * from './block-editor-custom-view.model.js'; -export type * from './collection.models.js'; export type * from './collection-action.model.js'; export type * from './collection-view.model.js'; +export type * from './collection.models.js'; export type * from './current-user-action.model.js'; export type * from './dashboard-collection.model.js'; export type * from './dashboard.model.js'; export type * from './dynamic-root.model.js'; export type * from './entity-action.model.js'; export type * from './entity-bulk-action.model.js'; +export type * from './entity-user-permission.model.js'; +export type * from './entry-point.model.js'; export type * from './external-login-provider.model.js'; export type * from './global-context.model.js'; export type * from './header-app.model.js'; @@ -102,14 +102,14 @@ export type * from './tinymce-plugin.model.js'; export type * from './tree-item.model.js'; export type * from './tree.model.js'; export type * from './user-granular-permission.model.js'; -export type * from './entity-user-permission.model.js'; export type * from './user-profile-app.model.js'; -export type * from './workspace-action.model.js'; export type * from './workspace-action-menu-item.model.js'; +export type * from './workspace-action.model.js'; export type * from './workspace-context.model.js'; export type * from './workspace-footer-app.model.js'; export type * from './workspace-view.model.js'; export type * from './workspace.model.js'; +export type * from './app-entry-point.model.js'; export type ManifestEntityActions = | ManifestEntityAction @@ -142,6 +142,7 @@ export type ManifestWorkspaceViews = ManifestWorkspaceView | ManifestWorkspaceVi export type ManifestTypes = | ManifestAppEntryPoint | ManifestAuthProvider + | ManifestBackofficeEntryPoint | ManifestBundle | ManifestBlockEditorCustomView | ManifestCollection From e530cc9d97b294081e4e53d6a3fa08be11ca1d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 19 Apr 2024 13:32:46 +0200 Subject: [PATCH 38/39] correct imports --- .../core/extension-registry/models/app-entry-point.model.ts | 3 +-- .../extension-registry/models/backoffice-entry-point.model.ts | 3 +-- .../core/extension-registry/models/entry-point.model.ts | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/app-entry-point.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/app-entry-point.model.ts index 009c9ca2d9..7e378e8f95 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/app-entry-point.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/app-entry-point.model.ts @@ -1,5 +1,4 @@ -import type { UmbEntryPointModule } from '../../../../libs/extension-api/models/index.js'; -import type { ManifestPlainJs } from '../../../../libs/extension-api/types/base.types.js'; +import type { ManifestPlainJs, UmbEntryPointModule } from '@umbraco-cms/backoffice/extension-api'; /** * Manifest for an `appEntryPoint`, which is loaded up front when the app starts. diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/backoffice-entry-point.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/backoffice-entry-point.model.ts index 5477c90102..468d9edb17 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/backoffice-entry-point.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/backoffice-entry-point.model.ts @@ -1,5 +1,4 @@ -import type { UmbEntryPointModule } from '../../../../libs/extension-api/models/index.js'; -import type { ManifestPlainJs } from '../../../../libs/extension-api/types/base.types.js'; +import type { ManifestPlainJs, UmbEntryPointModule } from '@umbraco-cms/backoffice/extension-api'; /** * Manifest for an `backofficeEntryPoint`, which is loaded after the Backoffice has been loaded and authentication has been done. diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/entry-point.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/entry-point.model.ts index 9408719eed..3082d0531d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/entry-point.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/entry-point.model.ts @@ -1,5 +1,4 @@ -import type { UmbEntryPointModule } from '../../../../libs/extension-api/models/index.js'; -import type { ManifestPlainJs } from '../../../../libs/extension-api/types/base.types.js'; +import type { ManifestPlainJs, UmbEntryPointModule } from '@umbraco-cms/backoffice/extension-api'; /** * Manifest for an `entryPoint`, which is loaded after the Backoffice has been loaded and authentication has been done. From 319b9f70592f91f7bef50cc4f99236e1a45d1353 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 19 Apr 2024 09:56:31 +0200 Subject: [PATCH 39/39] lazy load repo + stores --- .../repository/detail/data-type-detail.repository.ts | 3 +++ .../data-type/repository/detail/data-type-detail.store.ts | 2 ++ .../src/packages/data-type/repository/detail/manifests.ts | 6 ++---- .../data-type/repository/item/data-type-item.repository.ts | 2 ++ .../data-type/repository/item/data-type-item.store.ts | 2 ++ .../src/packages/data-type/repository/item/manifests.ts | 6 ++---- .../packages/data-type/tree/data-type-tree.repository.ts | 2 ++ .../src/packages/data-type/tree/data-type-tree.store.ts | 2 ++ .../src/packages/data-type/tree/manifests.ts | 6 ++---- 9 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/data-type-detail.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/data-type-detail.repository.ts index f252ba9568..2115695dbd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/data-type-detail.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/data-type-detail.repository.ts @@ -1,3 +1,4 @@ +import { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import type { UmbDataTypeDetailModel } from '../../types.js'; import { UmbDataTypeServerDataSource } from './data-type-detail.server.data-source.js'; import type { UmbDataTypeDetailStore } from './data-type-detail.store.js'; @@ -24,3 +25,5 @@ export class UmbDataTypeDetailRepository extends UmbDetailRepositoryBase('UmbDataTypeDetailStore'); + +export { UmbDataTypeDetailStore as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/manifests.ts index 4d3561bcbf..d8f2c18268 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/manifests.ts @@ -1,5 +1,3 @@ -import { UmbDataTypeDetailRepository } from './data-type-detail.repository.js'; -import { UmbDataTypeDetailStore } from './data-type-detail.store.js'; import type { ManifestRepository, ManifestStore } from '@umbraco-cms/backoffice/extension-registry'; export const UMB_DATA_TYPE_DETAIL_REPOSITORY_ALIAS = 'Umb.Repository.DataType.Detail'; @@ -8,7 +6,7 @@ const repository: ManifestRepository = { type: 'repository', alias: UMB_DATA_TYPE_DETAIL_REPOSITORY_ALIAS, name: 'Data Type Detail Repository', - api: UmbDataTypeDetailRepository, + api: () => import('./data-type-detail.repository.js'), }; export const UMB_DATA_TYPE_DETAIL_STORE_ALIAS = 'Umb.Store.DataType.Detail'; @@ -17,7 +15,7 @@ const store: ManifestStore = { type: 'store', alias: UMB_DATA_TYPE_DETAIL_STORE_ALIAS, name: 'Data Type Detail Store', - api: UmbDataTypeDetailStore, + api: () => import('./data-type-detail.store.js'), }; export const manifests = [repository, store]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/data-type-item.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/data-type-item.repository.ts index 2e5ab9fb07..b245b1db52 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/data-type-item.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/data-type-item.repository.ts @@ -9,3 +9,5 @@ export class UmbDataTypeItemRepository extends UmbItemRepositoryBase } export const UMB_DATA_TYPE_ITEM_STORE_CONTEXT = new UmbContextToken('UmbDataTypeItemStore'); + +export { UmbDataTypeItemStore as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/manifests.ts index ee96b57d8f..bb5083b681 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/manifests.ts @@ -1,5 +1,3 @@ -import { UmbDataTypeItemStore } from './data-type-item.store.js'; -import { UmbDataTypeItemRepository } from './data-type-item.repository.js'; import type { ManifestRepository, ManifestItemStore } from '@umbraco-cms/backoffice/extension-registry'; export const UMB_DATA_TYPE_ITEM_REPOSITORY_ALIAS = 'Umb.Repository.DataType.Item'; @@ -9,14 +7,14 @@ const itemRepository: ManifestRepository = { type: 'repository', alias: UMB_DATA_TYPE_ITEM_REPOSITORY_ALIAS, name: 'Data Type Item Repository', - api: UmbDataTypeItemRepository, + api: () => import('./data-type-item.repository.js'), }; const itemStore: ManifestItemStore = { type: 'itemStore', alias: UMB_DATA_TYPE_STORE_ALIAS, name: 'Data Type Item Store', - api: UmbDataTypeItemStore, + api: () => import('./data-type-item.store.js'), }; export const manifests = [itemRepository, itemStore]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.repository.ts index c961dc6913..07925893e7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.repository.ts @@ -29,3 +29,5 @@ export class UmbDataTypeTreeRepository return { data }; } } + +export { UmbDataTypeTreeRepository as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.store.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.store.ts index 95c56fee90..9931da6128 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.store.ts @@ -20,3 +20,5 @@ export class UmbDataTypeTreeStore extends UmbUniqueTreeStore { } export const UMB_DATA_TYPE_TREE_STORE_CONTEXT = new UmbContextToken('UmbDataTypeTreeStore'); + +export { UmbDataTypeTreeStore as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/manifests.ts index d8f1ba33ec..b3af4798cf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/manifests.ts @@ -1,7 +1,5 @@ import { manifests as folderManifests } from './folder/manifests.js'; import { manifests as reloadManifests } from './reload-tree-item-children/manifests.js'; -import { UmbDataTypeTreeRepository } from './data-type-tree.repository.js'; -import { UmbDataTypeTreeStore } from './data-type-tree.store.js'; import { UMB_DATA_TYPE_TREE_ALIAS, UMB_DATA_TYPE_TREE_REPOSITORY_ALIAS, @@ -18,14 +16,14 @@ const treeRepository: ManifestRepository = { type: 'repository', alias: UMB_DATA_TYPE_TREE_REPOSITORY_ALIAS, name: 'Data Type Tree Repository', - api: UmbDataTypeTreeRepository, + api: () => import('./data-type-tree.repository.js'), }; const treeStore: ManifestTreeStore = { type: 'treeStore', alias: UMB_DATA_TYPE_TREE_STORE_ALIAS, name: 'Data Type Tree Store', - api: UmbDataTypeTreeStore, + api: () => import('./data-type-tree.store.js'), }; const tree: ManifestTree = {