diff --git a/src/Umbraco.Web.UI.Client/.eslintrc.json b/src/Umbraco.Web.UI.Client/.eslintrc.json
index 5034e34797..1d78b5edbf 100644
--- a/src/Umbraco.Web.UI.Client/.eslintrc.json
+++ b/src/Umbraco.Web.UI.Client/.eslintrc.json
@@ -1,18 +1,18 @@
{
"ignorePatterns": ["vite.*.ts"],
"root": true,
+ "plugins": ["eslint-plugin-local-rules"],
"extends": ["eslint:recommended", "plugin:import/recommended", "prettier"],
- "plugins": ["import", "eslint-plugin-local-rules"],
"overrides": [
{
"files": ["**/*.ts"],
- "plugins": ["import", "@typescript-eslint", "lit", "lit-a11y"],
"extends": [
"eslint:recommended",
"plugin:import/recommended",
"plugin:import/typescript",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
+ "plugin:wc/recommended",
"plugin:lit/recommended",
"plugin:lit-a11y/recommended",
"plugin:storybook/recommended",
@@ -34,7 +34,8 @@
"import/no-unresolved": "error",
"import/order": "warn",
"local-rules/bad-type-import": "error",
- "local-rules/no-direct-api-import": "warn"
+ "local-rules/no-direct-api-import": "warn",
+ "@typescript-eslint/no-non-null-assertion": "off"
},
"settings": {
"import/parsers": {
diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json
index f2c8e3250d..30f49277b1 100644
--- a/src/Umbraco.Web.UI.Client/package-lock.json
+++ b/src/Umbraco.Web.UI.Client/package-lock.json
@@ -20,7 +20,6 @@
"element-internals-polyfill": "^1.1.18",
"lit": "^2.6.0",
"lodash": "^4.17.21",
- "openapi-typescript-fetch": "^1.1.3",
"router-slot": "^1.5.5",
"rxjs": "^7.8.0",
"uuid": "^9.0.0"
@@ -55,7 +54,8 @@
"eslint-plugin-lit": "^1.8.0",
"eslint-plugin-lit-a11y": "^2.3.0",
"eslint-plugin-local-rules": "^1.3.2",
- "eslint-plugin-storybook": "^0.6.8",
+ "eslint-plugin-storybook": "^0.6.10",
+ "eslint-plugin-wc": "^1.4.0",
"lit-html": "^2.6.0",
"msw": "^0.49.2",
"msw-storybook-addon": "^1.6.3",
@@ -13789,9 +13789,9 @@
"dev": true
},
"node_modules/eslint-plugin-storybook": {
- "version": "0.6.8",
- "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.8.tgz",
- "integrity": "sha512-57vyICs19ODx0ql+shM0hKFn4Nvwcrdw29KJbj6QKGZ+Y47aDws/lvBx65++F0vpEsr0lkZljSdUbxWjIP2+Rw==",
+ "version": "0.6.10",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.10.tgz",
+ "integrity": "sha512-3DKXRey06EhwnTKaG6fgMqGTy4C3z6Ikyv6VVixO5BvaExWQe3yGWIAufrC2Et0OaAMIaMwx9KWjqb/Wq+JxPg==",
"dev": true,
"dependencies": {
"@storybook/csf": "^0.0.1",
@@ -13815,6 +13815,19 @@
"lodash": "^4.17.15"
}
},
+ "node_modules/eslint-plugin-wc": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-1.4.0.tgz",
+ "integrity": "sha512-AmoKhJyBNcS3I+dbS/JTmRSq4REUvQ/JJCeWJezlK8gqTsdr5JD+EAvHldH/tVvU+l6qR2Tykga5hTINP9zS8A==",
+ "dev": true,
+ "dependencies": {
+ "is-valid-element-name": "^1.0.0",
+ "js-levenshtein-esm": "^1.2.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=5"
+ }
+ },
"node_modules/eslint-rule-extender": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/eslint-rule-extender/-/eslint-rule-extender-0.0.1.tgz",
@@ -17314,6 +17327,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+ "dev": true
+ },
"node_modules/is-reference": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.0.tgz",
@@ -17470,6 +17489,15 @@
"dev": true,
"optional": true
},
+ "node_modules/is-valid-element-name": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-valid-element-name/-/is-valid-element-name-1.0.0.tgz",
+ "integrity": "sha512-GZITEJY2LkSjQfaIPBha7eyZv+ge0PhBR7KITeCCWvy7VBQrCUdFkvpI+HrAPQjVtVjy1LvlEkqQTHckoszruw==",
+ "dev": true,
+ "dependencies": {
+ "is-potential-custom-element-name": "^1.0.0"
+ }
+ },
"node_modules/is-weakmap": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
@@ -17884,6 +17912,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/js-levenshtein-esm": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-1.2.0.tgz",
+ "integrity": "sha512-fzreKVq1eD7eGcQr7MtRpQH94f8gIfhdrc7yeih38xh684TNMK9v5aAu2wxfIRMk/GpAJRrzcirMAPIaSDaByQ==",
+ "dev": true
+ },
"node_modules/js-sdsl": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz",
@@ -21296,15 +21330,6 @@
"node": ">=12"
}
},
- "node_modules/openapi-typescript-fetch": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/openapi-typescript-fetch/-/openapi-typescript-fetch-1.1.3.tgz",
- "integrity": "sha512-smLZPck4OkKMNExcw8jMgrMOGgVGx2N/s6DbKL2ftNl77g5HfoGpZGFy79RBzU/EkaO0OZpwBnslfdBfh7ZcWg==",
- "engines": {
- "node": ">= 12.0.0",
- "npm": ">= 7.0.0"
- }
- },
"node_modules/optionator": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@@ -40001,9 +40026,9 @@
"dev": true
},
"eslint-plugin-storybook": {
- "version": "0.6.8",
- "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.8.tgz",
- "integrity": "sha512-57vyICs19ODx0ql+shM0hKFn4Nvwcrdw29KJbj6QKGZ+Y47aDws/lvBx65++F0vpEsr0lkZljSdUbxWjIP2+Rw==",
+ "version": "0.6.10",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.10.tgz",
+ "integrity": "sha512-3DKXRey06EhwnTKaG6fgMqGTy4C3z6Ikyv6VVixO5BvaExWQe3yGWIAufrC2Et0OaAMIaMwx9KWjqb/Wq+JxPg==",
"dev": true,
"requires": {
"@storybook/csf": "^0.0.1",
@@ -40023,6 +40048,16 @@
}
}
},
+ "eslint-plugin-wc": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-1.4.0.tgz",
+ "integrity": "sha512-AmoKhJyBNcS3I+dbS/JTmRSq4REUvQ/JJCeWJezlK8gqTsdr5JD+EAvHldH/tVvU+l6qR2Tykga5hTINP9zS8A==",
+ "dev": true,
+ "requires": {
+ "is-valid-element-name": "^1.0.0",
+ "js-levenshtein-esm": "^1.2.0"
+ }
+ },
"eslint-rule-extender": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/eslint-rule-extender/-/eslint-rule-extender-0.0.1.tgz",
@@ -42571,6 +42606,12 @@
"isobject": "^3.0.1"
}
},
+ "is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+ "dev": true
+ },
"is-reference": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.0.tgz",
@@ -42679,6 +42720,15 @@
"dev": true,
"optional": true
},
+ "is-valid-element-name": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-valid-element-name/-/is-valid-element-name-1.0.0.tgz",
+ "integrity": "sha512-GZITEJY2LkSjQfaIPBha7eyZv+ge0PhBR7KITeCCWvy7VBQrCUdFkvpI+HrAPQjVtVjy1LvlEkqQTHckoszruw==",
+ "dev": true,
+ "requires": {
+ "is-potential-custom-element-name": "^1.0.0"
+ }
+ },
"is-weakmap": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
@@ -42990,6 +43040,12 @@
"integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==",
"dev": true
},
+ "js-levenshtein-esm": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-1.2.0.tgz",
+ "integrity": "sha512-fzreKVq1eD7eGcQr7MtRpQH94f8gIfhdrc7yeih38xh684TNMK9v5aAu2wxfIRMk/GpAJRrzcirMAPIaSDaByQ==",
+ "dev": true
+ },
"js-sdsl": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz",
@@ -45572,11 +45628,6 @@
}
}
},
- "openapi-typescript-fetch": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/openapi-typescript-fetch/-/openapi-typescript-fetch-1.1.3.tgz",
- "integrity": "sha512-smLZPck4OkKMNExcw8jMgrMOGgVGx2N/s6DbKL2ftNl77g5HfoGpZGFy79RBzU/EkaO0OZpwBnslfdBfh7ZcWg=="
- },
"optionator": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json
index 5d4f2a0580..73939ddbc2 100644
--- a/src/Umbraco.Web.UI.Client/package.json
+++ b/src/Umbraco.Web.UI.Client/package.json
@@ -35,7 +35,7 @@
"test": "web-test-runner --coverage",
"test:watch": "web-test-runner --watch",
"test:e2e": "npx playwright test",
- "lint": "eslint --cache src e2e",
+ "lint": "eslint src e2e",
"lint:fix": "npm run lint -- --fix",
"format": "prettier 'src/**/*.ts'",
"format:fix": "npm run format -- --write",
@@ -56,17 +56,16 @@
},
"dependencies": {
"@umbraco-ui/uui": "^1.1.0",
+ "@umbraco-ui/uui-color-swatch": "file:umbraco-ui-uui-color-swatch-0.0.0.tgz",
+ "@umbraco-ui/uui-color-swatches": "file:umbraco-ui-uui-color-swatches-2.0.0.tgz",
"@umbraco-ui/uui-css": "^1.0.0",
"@umbraco-ui/uui-modal": "file:umbraco-ui-uui-modal-0.0.0.tgz",
"@umbraco-ui/uui-modal-container": "file:umbraco-ui-uui-modal-container-0.0.0.tgz",
"@umbraco-ui/uui-modal-dialog": "file:umbraco-ui-uui-modal-dialog-0.0.0.tgz",
"@umbraco-ui/uui-modal-sidebar": "file:umbraco-ui-uui-modal-sidebar-0.0.0.tgz",
- "@umbraco-ui/uui-color-swatches": "file:umbraco-ui-uui-color-swatches-2.0.0.tgz",
- "@umbraco-ui/uui-color-swatch": "file:umbraco-ui-uui-color-swatch-0.0.0.tgz",
"element-internals-polyfill": "^1.1.18",
"lit": "^2.6.0",
"lodash": "^4.17.21",
- "openapi-typescript-fetch": "^1.1.3",
"router-slot": "^1.5.5",
"rxjs": "^7.8.0",
"uuid": "^9.0.0"
@@ -101,7 +100,8 @@
"eslint-plugin-lit": "^1.8.0",
"eslint-plugin-lit-a11y": "^2.3.0",
"eslint-plugin-local-rules": "^1.3.2",
- "eslint-plugin-storybook": "^0.6.8",
+ "eslint-plugin-storybook": "^0.6.10",
+ "eslint-plugin-wc": "^1.4.0",
"lit-html": "^2.6.0",
"msw": "^0.49.2",
"msw-storybook-addon": "^1.6.3",
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts
index 73f4f93dfb..52d943758a 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts
@@ -74,23 +74,36 @@ export class UmbCollectionViewsMediaGridElement extends UmbLitElement {
constructor() {
super();
- document.addEventListener('dragenter', () => {
- this.toggleAttribute('dragging', true);
- });
- document.addEventListener('dragleave', () => {
- this.toggleAttribute('dragging', false);
- });
- document.addEventListener('drop', (e) => {
- e.preventDefault();
- this.toggleAttribute('dragging', false);
- });
+ document.addEventListener('dragenter', this._handleDragEnter.bind(this));
+ document.addEventListener('dragleave', this._handleDragLeave.bind(this));
+ document.addEventListener('drop', this._handleDrop.bind(this));
this.consumeContext('umbCollectionContext', (instance) => {
- console.log("umbCollectionContext", instance)
+ console.log('umbCollectionContext', instance);
this._collectionContext = instance;
this._observeCollectionContext();
});
}
+ disconnectedCallback(): void {
+ super.disconnectedCallback();
+ document.removeEventListener('dragenter', this._handleDragEnter.bind(this));
+ document.removeEventListener('dragleave', this._handleDragLeave.bind(this));
+ document.removeEventListener('drop', this._handleDrop.bind(this));
+ }
+
+ private _handleDragEnter() {
+ this.toggleAttribute('dragging', true);
+ }
+
+ private _handleDragLeave() {
+ this.toggleAttribute('dragging', false);
+ }
+
+ private _handleDrop(e: DragEvent) {
+ e.preventDefault();
+ this.toggleAttribute('dragging', false);
+ }
+
private _observeCollectionContext() {
if (!this._collectionContext) return;
@@ -144,11 +157,13 @@ export class UmbCollectionViewsMediaGridElement extends UmbLitElement {
label="Drop files here"
accept="">
- ${this._mediaItems ? repeat(
- this._mediaItems,
- (file, index) => file.key + index,
- (file) => this._renderMediaItem(file)
- ) : ''}
+ ${this._mediaItems
+ ? repeat(
+ this._mediaItems,
+ (file, index) => file.key + index,
+ (file) => this._renderMediaItem(file)
+ )
+ : ''}
`;
}
diff --git a/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.controller.ts b/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.controller.ts
index fd89ccf2ea..ecf921af17 100644
--- a/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.controller.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.controller.ts
@@ -6,6 +6,10 @@ import { UmbControllerHostInterface } from 'src/core/controller/controller-host.
export class UmbContextConsumerController extends UmbContextConsumer implements UmbControllerInterface {
+ public get unique() {
+ return this._contextAlias;
+ }
+
constructor(host:UmbControllerHostInterface, contextAlias: string, callback: UmbContextCallback) {
super(host, contextAlias, callback);
host.addController(this);
diff --git a/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.ts b/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.ts
index 84c8ba1af4..0d6261875a 100644
--- a/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.ts
@@ -23,7 +23,7 @@ export class UmbContextConsumer {
* @param {UmbContextCallback} _callback
* @memberof UmbContextConsumer
*/
- constructor(protected host: HostType, private _contextAlias: string, private _callback: UmbContextCallback) {}
+ constructor(protected host: HostType, protected _contextAlias: string, private _callback: UmbContextCallback) {}
private _onResponse = (instance: unknown) => {
diff --git a/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.controller.ts b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.controller.ts
index efd9d239a8..301f260c27 100644
--- a/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.controller.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.controller.ts
@@ -5,6 +5,10 @@ import { UmbControllerHostInterface } from 'src/core/controller/controller-host.
export class UmbContextProviderController extends UmbContextProvider implements UmbControllerInterface {
+ public get unique() {
+ return this._contextAlias;
+ }
+
constructor(host:UmbControllerHostInterface, contextAlias: string, instance: unknown) {
super(host, contextAlias, instance);
diff --git a/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.ts b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.ts
index 3bb525cd56..02d183216f 100644
--- a/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.ts
@@ -9,8 +9,8 @@ export class UmbContextProvider {
protected host: HostType;
- private _contextAlias: string;
- private _instance: unknown;
+ protected _contextAlias: string;
+ #instance: unknown;
/**
* Creates an instance of UmbContextProvider.
@@ -22,7 +22,7 @@ export class UmbContextProvider {
constructor(host: HostType, contextAlias: string, instance: unknown) {
this.host = host;
this._contextAlias = contextAlias;
- this._instance = instance;
+ this.#instance = instance;
}
/**
@@ -51,6 +51,6 @@ export class UmbContextProvider {
if (event.contextAlias !== this._contextAlias) return;
event.stopPropagation();
- event.callback(this._instance);
+ event.callback(this.#instance);
};
}
diff --git a/src/Umbraco.Web.UI.Client/src/core/controller/controller-host.mixin.ts b/src/Umbraco.Web.UI.Client/src/core/controller/controller-host.mixin.ts
index ede2d1a138..37291b934e 100644
--- a/src/Umbraco.Web.UI.Client/src/core/controller/controller-host.mixin.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/controller/controller-host.mixin.ts
@@ -45,6 +45,16 @@ export const UmbControllerHostMixin = (superCl
* @param {UmbControllerInterface} ctrl
*/
addController(ctrl: UmbControllerInterface): void {
+
+ // Check if there is one already with same unique
+ if(ctrl.unique) {
+ this.#controllers.forEach(x => {
+ if(x.unique === ctrl.unique) {
+ this.removeController(x);
+ }
+ });
+ }
+
this.#controllers.push(ctrl);
if(this.#attached) {
ctrl.hostConnected();
diff --git a/src/Umbraco.Web.UI.Client/src/core/controller/controller.class.ts b/src/Umbraco.Web.UI.Client/src/core/controller/controller.class.ts
index 355bee9e47..02235df414 100644
--- a/src/Umbraco.Web.UI.Client/src/core/controller/controller.class.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/controller/controller.class.ts
@@ -4,8 +4,15 @@ import { UmbControllerInterface } from './controller.interface';
export abstract class UmbController implements UmbControllerInterface {
protected host?: UmbControllerHostInterface;
- constructor(host: UmbControllerHostInterface) {
+
+ private _alias?: string;
+ public get unique() {
+ return this._alias;
+ }
+
+ constructor(host: UmbControllerHostInterface, alias?: string) {
this.host = host;
+ this._alias = alias;
this.host.addController(this);
}
diff --git a/src/Umbraco.Web.UI.Client/src/core/controller/controller.interface.ts b/src/Umbraco.Web.UI.Client/src/core/controller/controller.interface.ts
index a70411fca4..f8feb3ff9c 100644
--- a/src/Umbraco.Web.UI.Client/src/core/controller/controller.interface.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/controller/controller.interface.ts
@@ -1,4 +1,5 @@
export interface UmbControllerInterface {
+ get unique(): string | undefined;
hostConnected(): void;
hostDisconnected(): void;
destroy(): void;
diff --git a/src/Umbraco.Web.UI.Client/src/core/controller/controller.test.ts b/src/Umbraco.Web.UI.Client/src/core/controller/controller.test.ts
new file mode 100644
index 0000000000..4da206b75e
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/core/controller/controller.test.ts
@@ -0,0 +1,50 @@
+import { expect } from '@open-wc/testing';
+import { customElement } from 'lit/decorators.js';
+import { UmbContextProviderController } from '../context-api/provide/context-provider.controller';
+import { UmbControllerHostInterface, UmbControllerHostMixin } from './controller-host.mixin';
+
+class MyClass {
+ prop = 'value from provider';
+}
+
+@customElement('test-my-controller-host')
+class MyHostElement extends UmbControllerHostMixin(HTMLElement) {
+
+}
+
+describe('UmbContextProvider', () => {
+ type NewType = UmbControllerHostInterface;
+
+ let hostElement: NewType;
+ const contextInstance = new MyClass();
+
+ beforeEach(() => {
+ hostElement = document.createElement('test-my-controller-host') as UmbControllerHostInterface;
+ });
+
+
+ describe('Destroyed controllers is gone from host', () => {
+ it('has a host property', () => {
+
+ const ctrl = new UmbContextProviderController(hostElement, 'my-test-context', contextInstance);
+
+ expect(hostElement.hasController(ctrl)).to.be.true;
+
+ ctrl.destroy();
+
+ expect(hostElement.hasController(ctrl)).to.be.false;
+ });
+ });
+
+ describe('Unique controllers replace each other', () => {
+ it('has a host property', () => {
+
+ const firstCtrl = new UmbContextProviderController(hostElement, 'my-test-context', contextInstance);
+ const secondCtrl = new UmbContextProviderController(hostElement, 'my-test-context', contextInstance);
+
+ expect(hostElement.hasController(firstCtrl)).to.be.false;
+ expect(hostElement.hasController(secondCtrl)).to.be.true;
+ });
+ });
+
+});
diff --git a/src/Umbraco.Web.UI.Client/src/core/element/element.mixin.ts b/src/Umbraco.Web.UI.Client/src/core/element/element.mixin.ts
index 87255a95df..d4bcaf7d3b 100644
--- a/src/Umbraco.Web.UI.Client/src/core/element/element.mixin.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/element/element.mixin.ts
@@ -12,7 +12,7 @@ interface ResolvedContexts {
}
export declare class UmbElementMixinInterface extends UmbControllerHostInterface {
- observe(source: Observable, callback: (_value: T) => void): UmbObserverController;
+ observe(source: Observable, callback: (_value: T) => void, unique?: string): UmbObserverController;
provideContext(alias: string, instance: unknown): UmbContextProviderController;
consumeContext(alias: string, callback: UmbContextCallback): UmbContextConsumerController;
consumeAllContexts(contextAliases: string[], callback: (_instances: ResolvedContexts) => void): void;
@@ -28,8 +28,8 @@ export const UmbElementMixin = (superClass: T)
* @return {UmbObserverController} Reference to a Observer Controller instance
* @memberof UmbElementMixin
*/
- observe(source: Observable, callback: (_value: T) => void): UmbObserverController {
- return new UmbObserverController(this, source, callback);
+ observe(source: Observable, callback: (_value: T) => void, unique?: string): UmbObserverController {
+ return new UmbObserverController(this, source, callback, unique);
}
/**
diff --git a/src/Umbraco.Web.UI.Client/src/core/element/lit-element.element.ts b/src/Umbraco.Web.UI.Client/src/core/element/lit-element.element.ts
index 05404a5744..daa2005b81 100644
--- a/src/Umbraco.Web.UI.Client/src/core/element/lit-element.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/element/lit-element.element.ts
@@ -1,6 +1,6 @@
import { LitElement } from 'lit';
import { UmbElementMixin } from './element.mixin';
-export abstract class UmbLitElement extends UmbElementMixin(LitElement) {
+export class UmbLitElement extends UmbElementMixin(LitElement) {
}
diff --git a/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.controller.ts b/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.controller.ts
index afc6225df7..6ccf637ff9 100644
--- a/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.controller.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.controller.ts
@@ -6,9 +6,23 @@ import { UmbControllerHostInterface } from 'src/core/controller/controller-host.
export class UmbObserverController extends UmbObserver implements UmbControllerInterface {
- constructor(host:UmbControllerHostInterface, source: Observable, callback: (_value: T) => void) {
+ _alias?: string;
+ public get unique() {
+ return this._alias;
+ }
+
+ constructor(host:UmbControllerHostInterface, source: Observable, callback: (_value: T) => void, alias?: string) {
super(source, callback);
- // TODO: What should happen if source or some? identifier is already present?
+ this._alias = alias;
+
+ // Lets check if controller is already here:
+ /*
+ if (this._subscriptions.has(source)) {
+ const subscription = this._subscriptions.get(source);
+ subscription?.unsubscribe();
+ }
+ */
+
host.addController(this);
}
@@ -16,4 +30,5 @@ export class UmbObserverController extends UmbObserver implements UmbContr
return;
}
+
}
diff --git a/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts b/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts
index d21a9359a0..9fa9968e3f 100644
--- a/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts
@@ -3,16 +3,8 @@ import { Observable, Subscription } from 'rxjs';
export class UmbObserver {
#subscription!: Subscription;
-
- constructor(source: Observable, callback: (_value: T) => void) {
- // TODO: can be transferred to something using alias?
- /*
- if (this._subscriptions.has(source)) {
- const subscription = this._subscriptions.get(source);
- subscription?.unsubscribe();
- }
- */
+ constructor(source: Observable, callback: (_value: T) => void) {
this.#subscription = source.subscribe((value) => callback(value));
}
@@ -27,4 +19,4 @@ export class UmbObserver {
this.#subscription.unsubscribe();
}
-};
\ No newline at end of file
+};
diff --git a/src/Umbraco.Web.UI.Client/src/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/core/resources/resource.controller.ts
index 5668c41180..ae3776f47d 100644
--- a/src/Umbraco.Web.UI.Client/src/core/resources/resource.controller.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/resources/resource.controller.ts
@@ -7,12 +7,13 @@ import { UmbNotificationOptions, UmbNotificationService } from 'src/core/notific
import { UmbNotificationDefaultData } from 'src/core/notification/layouts/default';
export class UmbResourceController extends UmbController {
+
#promise: Promise;
#notificationService?: UmbNotificationService;
- constructor(host: UmbControllerHostInterface, promise: Promise) {
- super(host);
+ constructor(host: UmbControllerHostInterface, promise: Promise, alias?: string) {
+ super(host, alias);
this.#promise = promise;