diff --git a/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-workspace-context.ts b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-workspace-context.ts index cfcfb13a33..5a94378782 100644 --- a/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-workspace-context.ts +++ b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-workspace-context.ts @@ -24,4 +24,7 @@ export class WorkspaceContextCounter extends UmbControllerBase { export const api = WorkspaceContextCounter; // Declare a Context Token that other elements can use to request the WorkspaceContextCounter: -export const EXAMPLE_COUNTER_CONTEXT = new UmbContextToken('example.workspaceContext.counter'); +export const EXAMPLE_COUNTER_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + 'example.workspaceContext.counter', +); diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 96821796d7..752b1757b3 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -45,7 +45,7 @@ "@storybook/web-components-vite": "^7.6.17", "@types/chai": "^4.3.5", "@types/mocha": "^10.0.1", - "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/eslint-plugin": "^7.1.0", "@typescript-eslint/parser": "^7.1.0", "@web/dev-server-esbuild": "^1.0.2", "@web/dev-server-import-maps": "^0.2.0", @@ -79,7 +79,7 @@ "tiny-glob": "^0.2.9", "tsc-alias": "^1.8.8", "typedoc": "^0.25.10", - "typescript": "^5.3.3", + "typescript": "^5.4.5", "typescript-json-schema": "^0.63.0", "vite": "^5.2.9", "vite-plugin-static-copy": "^1.0.2", @@ -6482,33 +6482,31 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.10.0.tgz", + "integrity": "sha512-PzCr+a/KAef5ZawX7nbyNwBDtM1HdLIT53aSA2DDlxmxMngZ43O8SIePOeX8H5S+FHXeI6t97mTt/dDdzY4Fyw==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/type-utils": "7.10.0", + "@typescript-eslint/utils": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -6516,39 +6514,6 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/parser": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.1.0.tgz", @@ -6686,16 +6651,16 @@ "dev": true }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz", + "integrity": "sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -6703,25 +6668,25 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.10.0.tgz", + "integrity": "sha512-D7tS4WDkJWrVkuzgm90qYw9RdgBcrWmbbRkrLA4d7Pg3w0ttVGDsvYGV19SH8gPR5L7OtcN5J1hTtyenO9xE9g==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/typescript-estree": "7.10.0", + "@typescript-eslint/utils": "7.10.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -6730,12 +6695,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.10.0.tgz", + "integrity": "sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==", "dev": true, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -6743,22 +6708,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.10.0.tgz", + "integrity": "sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -6770,26 +6735,26 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -6797,81 +6762,39 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz", + "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/typescript-estree": "7.10.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.10.0.tgz", + "integrity": "sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.10.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -19415,12 +19338,12 @@ } }, "node_modules/ts-api-utils": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.0.tgz", - "integrity": "sha512-d+3WxW4r8WQy2cZWpNRPPGExX8ffOLGcIhheUANKbL5Sqjbhkneki76fRAWeXkaslV2etTb4tSJBSxOsH5+CJw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "engines": { - "node": ">=18" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" @@ -19740,9 +19663,9 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index d45281107a..1409295851 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -207,7 +207,7 @@ "@storybook/web-components-vite": "^7.6.17", "@types/chai": "^4.3.5", "@types/mocha": "^10.0.1", - "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/eslint-plugin": "^7.1.0", "@typescript-eslint/parser": "^7.1.0", "@web/dev-server-esbuild": "^1.0.2", "@web/dev-server-import-maps": "^0.2.0", @@ -241,7 +241,7 @@ "tiny-glob": "^0.2.9", "tsc-alias": "^1.8.8", "typedoc": "^0.25.10", - "typescript": "^5.3.3", + "typescript": "^5.4.5", "typescript-json-schema": "^0.63.0", "vite": "^5.2.9", "vite-plugin-static-copy": "^1.0.2", diff --git a/src/Umbraco.Web.UI.Client/src/libs/element-api/element.test.ts b/src/Umbraco.Web.UI.Client/src/libs/element-api/element.test.ts index 20618e4dec..c9a29170d9 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/element-api/element.test.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/element-api/element.test.ts @@ -290,7 +290,7 @@ describe('UmbElementMixin', () => { const check2: CheckType = value as string; const check3: CheckType = value as null; const check4: CheckType = value as undefined; - expect(check).to.be.equal('hello'); + expect(check).to.be.equal(null); expect(check2 === check3 && check2 === check4).to.be.true; // Just to use the const for something. }); // Because the source is potentially undefined, the controller could be undefined and the value of the callback method could be undefined [NL] diff --git a/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/array-state.ts b/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/array-state.ts index a96ff31ce5..9741bbcebb 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/array-state.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/array-state.ts @@ -35,6 +35,7 @@ export class UmbArrayState extends UmbDeepState { */ sortBy(sortMethod?: (a: T, b: T) => number) { this.#sortMethod = sortMethod; + super.setValue(this.getValue().sort(this.#sortMethod)); return this; } diff --git a/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/basic-state.ts b/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/basic-state.ts index 57aaaa84b6..4c6394849f 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/basic-state.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/basic-state.ts @@ -33,7 +33,7 @@ export class UmbBasicState { * console.log("Value is: ", myState.value); */ public get value(): BehaviorSubject['value'] { - return this._subject.value; + return this.getValue(); } /** diff --git a/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/deep-state.ts b/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/deep-state.ts index a9a8b7f33a..f443fe02e1 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/deep-state.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/deep-state.ts @@ -13,15 +13,19 @@ import { UmbBasicState } from './basic-state.js'; * Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content. */ export class UmbDeepState extends UmbBasicState { + #mute?: boolean; + #value: T; + constructor(initialData: T) { super(deepFreeze(initialData)); + this.#value = this._subject.getValue(); } /** - * @export * @method createObservablePart * @param {(mappable: T) => R} mappingFunction - Method to return the part for this Observable to return. * @param {(previousResult: R, currentResult: R) => boolean} [memoizationFunction] - Method to Compare if the data has changed. Should return true when data is different. + * @returns {Observable} * @description - Creates an Observable from this State. */ asObservablePart( @@ -39,9 +43,63 @@ export class UmbDeepState extends UmbBasicState { setValue(data: T): void { if (!this._subject) return; const frozenData = deepFreeze(data); - // Only update data if its different than current data. - if (!jsonStringComparison(frozenData, this._subject.getValue())) { + this.#value = frozenData; + // Only update data if its not muted and is different than current data. [NL] + if (!this.#mute && !jsonStringComparison(frozenData, this._subject.getValue())) { this._subject.next(frozenData); } } + + getValue(): T { + return this.#value; + } + + /** + * @method mute + * @description - Set mute for this state. + */ + mute() { + if (this.#mute) return; + this.#mute = true; + } + + /** + * @method unmute + * @description - Unset the mute of this state. + */ + unmute() { + if (!this.#mute) return; + this.#mute = false; + // Only update data if it is different than current data. [NL] + if (!jsonStringComparison(this.#value, this._subject.getValue())) { + this._subject?.next(this.#value); + } + } + + /** + * @method isMuted + * @description - Check if the state is muted. + * @returns {boolean} - Returns true if the state is muted. + */ + isMuted() { + return this.#mute; + } + + /** + * @method getMutePromise + * @description - Get a promise which resolves when the mute is unset. + * @returns {Promise} + */ + getMutePromise() { + return new Promise((resolve) => { + if (!this.#mute) { + resolve(); + return; + } + const subscription = this._subject.subscribe(() => { + subscription.unsubscribe(); + resolve(); + }); + }); + } } diff --git a/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/object-state.test.ts b/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/object-state.test.ts index 76dc7705dd..010716e036 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/object-state.test.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/object-state.test.ts @@ -41,4 +41,50 @@ describe('UmbObjectState', () => { subject.update({ key: 'change_this_first_should_not_trigger_update' }); subject.update({ another: 'myNewValue' }); }); + + it('replays the latests value when unmuted.', (done) => { + let amountOfCallbacks = 0; + + const observer = subject.asObservable(); + observer.subscribe((value) => { + amountOfCallbacks++; + if (amountOfCallbacks === 1) { + // First callback gives us the initialized value. + expect(value.key).to.be.equal('some'); + } + if (amountOfCallbacks === 2) { + // Second callback gives us the first change. + expect(value.key).to.be.equal('firstChange'); + } + if (amountOfCallbacks === 3) { + // Third callback gives us the last change before unmuted. + expect(value.key).to.be.equal('thirdChange'); + done(); + } + }); + + subject.update({ key: 'firstChange' }); + subject.mute(); + subject.update({ key: 'secondChange' }); + subject.update({ key: 'thirdChange' }); + subject.unmute(); + }); + + /* + it('replays latests unmuted value when muted.', (done) => { + const observer = subject.asObservable(); + observer.subscribe((value) => { + expect(value).to.be.equal(initialData); + }); + + subject.mute(); + subject.update({ key: 'firstChange' }); + + observer.subscribe((value) => { + expect(value).to.be.equal(initialData); + done(); + }); + + }); + */ }); diff --git a/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/object-state.ts b/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/object-state.ts index 815bee0865..b325d50b8a 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/object-state.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/object-state.ts @@ -21,7 +21,7 @@ export class UmbObjectState extends UmbDeepState { * myState.update({value: 'myNewValue'}); */ update(partialData: Partial) { - this.setValue({ ...this._subject.getValue(), ...partialData }); + this.setValue({ ...this.getValue(), ...partialData }); return this; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts index fece6fb327..fe7917928c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts @@ -17,7 +17,7 @@ import { UmbSorterController, type UmbSorterConfig, type resolvePlacementArgs } */ function resolvePlacementAsGrid(args: resolvePlacementArgs) { // If this has areas, we do not want to move, unless we are at the edge - if (args.relatedModel.areas.length > 0 && isWithinRect(args.pointerX, args.pointerY, args.relatedRect, -10)) { + if (args.relatedModel.areas?.length > 0 && isWithinRect(args.pointerX, args.pointerY, args.relatedRect, -10)) { return null; } @@ -162,10 +162,10 @@ export class UmbBlockGridEntriesElement extends UmbLitElement { constructor() { super(); this.observe(this.#context.layoutEntries, (layoutEntries) => { - const oldValue = this._layoutEntries; + //const oldValue = this._layoutEntries; this.#sorter.setModel(layoutEntries); this._layoutEntries = layoutEntries; - this.requestUpdate('layoutEntries', oldValue); + //this.requestUpdate('layoutEntries', oldValue); }); this.observe(this.#context.amountOfAllowedBlockTypes, (length) => { @@ -197,7 +197,7 @@ export class UmbBlockGridEntriesElement extends UmbLitElement { }); } - // TODO: Missing ability to jump directly to creating a Block, when there is only one Block Type. + // TODO: Missing ability to jump directly to creating a Block, when there is only one Block Type. [NL] render() { return html` ${this._styleElement} diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts index e66cb0a985..7b24b49f4d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts @@ -171,6 +171,8 @@ export class UmbBlockGridEntriesContext 'observeThisLayouts', ); + this.removeUmbControllerByAlias('observeAreaType'); + const hostEl = this.getHostElement() as HTMLElement | undefined; if (hostEl) { hostEl.removeAttribute('data-area-alias'); @@ -196,7 +198,9 @@ export class UmbBlockGridEntriesContext this.observe( this.#parentEntry.layoutsOfArea(this.#areaKey), (layouts) => { - this._layoutEntries.setValue(layouts); + if (layouts) { + this._layoutEntries.setValue(layouts); + } }, 'observeParentLayouts', ); @@ -244,6 +248,20 @@ export class UmbBlockGridEntriesContext return this._catalogueRouteBuilderState.getValue()?.({ view: 'clipboard', index: index }); } + /* + async setLayouts(layouts: Array) { + await this._retrieveManager; + if (this.#areaKey === null) { + this._manager?.setLayouts(layouts); + } else { + if (!this.#parentUnique || !this.#areaKey) { + throw new Error('ParentUnique or AreaKey not set'); + } + this._manager?.setLayoutsOfArea(this.#parentUnique, this.#areaKey, layouts); + } + } + */ + async create( contentElementTypeKey: string, partialLayoutEntry?: Omit, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts index 819e70f08a..7eeb562cd1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts @@ -31,14 +31,18 @@ export class UmbBlockGridEntryContext { // readonly columnSpan = this._layout.asObservablePart((x) => x?.columnSpan); - readonly rowSpan = this._layout.asObservablePart((x) => x?.rowSpan ?? 1); + readonly rowSpan = this._layout.asObservablePart((x) => x?.rowSpan); + readonly layoutAreas = this._layout.asObservablePart((x) => x?.areas); readonly columnSpanOptions = this._blockType.asObservablePart((x) => x?.columnSpanOptions ?? []); readonly areaTypeGridColumns = this._blockType.asObservablePart((x) => x?.areaGridColumns); readonly areas = this._blockType.asObservablePart((x) => x?.areas ?? []); - readonly minMaxRowSpan = this._blockType.asObservablePart((x) => [x?.rowMinSpan ?? 1, x?.rowMaxSpan ?? 1]); - public getMinMaxRowSpan(): [number, number] { + readonly minMaxRowSpan = this._blockType.asObservablePart((x) => + x ? [x.rowMinSpan ?? 1, x.rowMaxSpan ?? 1] : undefined, + ); + public getMinMaxRowSpan(): [number, number] | undefined { const x = this._blockType.getValue(); - return [x?.rowMinSpan ?? 1, x?.rowMaxSpan ?? 1]; + if (!x) return undefined; + return [x.rowMinSpan ?? 1, x.rowMaxSpan ?? 1]; } readonly inlineEditingMode = this._blockType.asObservablePart((x) => x?.inlineEditing === true); @@ -65,18 +69,8 @@ export class UmbBlockGridEntryContext super(host, UMB_BLOCK_GRID_MANAGER_CONTEXT, UMB_BLOCK_GRID_ENTRIES_CONTEXT); } - protected _gotLayout(layout: UmbBlockGridLayoutModel | undefined) { - if (layout) { - // TODO: Implement size correction to fit with configurations. both for columnSpan and rowSpan. - layout = { ...layout }; - layout.columnSpan ??= 999; - layout.areas ??= []; - } - return layout; - } - layoutsOfArea(areaKey: string) { - return this._layout.asObservablePart((x) => x?.areas.find((x) => x.key === areaKey)?.items ?? []); + return this._layout.asObservablePart((x) => x?.areas?.find((x) => x.key === areaKey)?.items); } areaType(areaKey: string) { @@ -87,7 +81,7 @@ export class UmbBlockGridEntryContext const frozenValue = this._layout.value; if (!frozenValue) return; const areas = appendToFrozenArray( - frozenValue?.areas, + frozenValue?.areas ?? [], { key: areaKey, items: layouts, @@ -97,40 +91,70 @@ export class UmbBlockGridEntryContext this._layout.update({ areas }); } + /** + * Set the column span of this entry. + * @param columnSpan {number} The new column span. + */ setColumnSpan(columnSpan: number) { if (!this._entries) return; const layoutColumns = this._entries.getLayoutColumns(); if (!layoutColumns) return; - columnSpan = Math.max(1, Math.min(columnSpan, layoutColumns)); + columnSpan = this.#calcColumnSpan(columnSpan, this.getRelevantColumnSpanOptions(), layoutColumns); + if (columnSpan === this.getColumnSpan()) return; + //this._layout.update({ columnSpan }); + //const contentUdi = this.getContentUdi(); + //if (!contentUdi) return; + //this._manager?.updateLayout({ contentUdi, columnSpan }); this._layout.update({ columnSpan }); } + /** + * Get the column span of this entry. + * @returns {number} The column span. + */ getColumnSpan() { return this._layout.getValue()?.columnSpan; } + /** + * Set the row span of this entry. + * @param rowSpan {number} The new row span. + */ setRowSpan(rowSpan: number) { - const blockType = this._blockType.getValue(); - if (!blockType) return; - rowSpan = Math.max(blockType.rowMinSpan, Math.min(rowSpan, blockType.rowMaxSpan)); + const minMax = this.getMinMaxRowSpan(); + if (!minMax) return; + rowSpan = Math.max(minMax[0], Math.min(rowSpan, minMax[1])); + if (rowSpan === this.getRowSpan()) return; + //this._layout.update({ rowSpan }); + //const contentUdi = this.getContentUdi(); + //if (!contentUdi) return; + //this._manager?.updateLayout({ contentUdi, rowSpan }); this._layout.update({ rowSpan }); } - + /** + * Get the row span of this entry. + * @returns {number} The row span. + */ getRowSpan() { return this._layout.getValue()?.rowSpan; } - _gotManager() {} + _gotManager() { + this.#gotEntriesAndManager(); + } _gotEntries() { this.scaleManager.setEntriesContext(this._entries); if (!this._entries) return; + this.#gotEntriesAndManager(); + + // Retrieve scale options: this.observe( observeMultiple([this.minMaxRowSpan, this.columnSpanOptions, this._entries.layoutColumns]), ([minMaxRowSpan, columnSpanOptions, layoutColumns]) => { - if (!layoutColumns) return; + if (!layoutColumns || !minMaxRowSpan) return; const relevantColumnSpanOptions = columnSpanOptions ? columnSpanOptions .filter((x) => x.columnSpan <= layoutColumns) @@ -147,6 +171,7 @@ export class UmbBlockGridEntryContext 'observeScaleOptions', ); + // Retrieve The Grid Columns for the Areas: this.observe( observeMultiple([this.areaTypeGridColumns, this._entries.layoutColumns]), ([areaTypeGridColumns, layoutColumns]) => { @@ -154,32 +179,94 @@ export class UmbBlockGridEntryContext }, 'observeAreaGridColumns', ); + } + #gotEntriesAndManager() { + if (!this._entries || !this._manager) return; + + // Secure areas fits options: + // NOLZ this.observe( - observeMultiple([this.columnSpan, this.relevantColumnSpanOptions, this._entries.layoutColumns]), - ([columnSpan, relevantColumnSpanOptions, layoutColumns]) => { - if (!columnSpan || !layoutColumns) return; - if (relevantColumnSpanOptions.length > 0) { - // Correct columnSpan so it fits. - const newColumnSpan = - closestColumnSpanOption(columnSpan, relevantColumnSpanOptions, layoutColumns) ?? layoutColumns; - if (newColumnSpan !== columnSpan) { - //this.setColumnSpan(newColumnSpan); - this._layout.update({ columnSpan: newColumnSpan }); - } - } else { - // Reset to the layoutColumns. - if (layoutColumns !== columnSpan) { - //this.setColumnSpan(layoutColumns); - this._layout.update({ columnSpan: layoutColumns }); - } + observeMultiple([this.areas, this.layoutAreas]), + ([areas, layoutAreas]) => { + if (!areas || !layoutAreas) return; + const areasAreIdentical = + areas.length === layoutAreas.length && areas.every((area) => layoutAreas.some((y) => y.key === area.key)); + if (areasAreIdentical === false) { + /* + const contentUdi = this.getContentUdi(); + if (!contentUdi) return; + this._manager?.updateLayout({ + contentUdi, + areas: layoutAreas.map((x) => (areas.find((y) => y.key === x.key) ? x : { key: x.key, items: [] })), + }); + */ + this._layout.update({ + areas: layoutAreas.map((x) => (areas.find((y) => y.key === x.key) ? x : { key: x.key, items: [] })), + }); + } + }, + 'observeAreaValidation', + ); + + // Secure columnSpan fits options: + // NOLZ + this.observe( + observeMultiple([this.layout, this.columnSpan, this.relevantColumnSpanOptions, this._entries.layoutColumns]), + ([layout, columnSpan, relevantColumnSpanOptions, layoutColumns]) => { + if (!layout || !layoutColumns) return; + const newColumnSpan = this.#calcColumnSpan( + columnSpan ?? layoutColumns, + relevantColumnSpanOptions, + layoutColumns, + ); + if (newColumnSpan !== columnSpan) { + //const contentUdi = this.getContentUdi(); + //if (!contentUdi) return; + //this._manager?.updateLayout({ contentUdi, columnSpan: newColumnSpan }); + this._layout.update({ columnSpan: newColumnSpan }); } }, 'observeColumnSpanValidation', ); + + // Secure rowSpan fits options: + // NOLZ + this.observe( + observeMultiple([this.minMaxRowSpan, this.rowSpan]), + ([minMax, rowSpan]) => { + if (minMax) { + const newRowSpan = Math.max(minMax[0], Math.min(rowSpan ?? 1, minMax[1])); + if (newRowSpan !== rowSpan) { + //const contentUdi = this.getContentUdi(); + //if (!contentUdi) return; + //this._manager!.updateLayout({ contentUdi, rowSpan: newRowSpan }); + this._layout.update({ rowSpan: newRowSpan }); + } + } + }, + 'observeRowSpanValidation', + ); } _gotContentType(contentType: UmbContentTypeModel | undefined) { this.#firstPropertyType.setValue(contentType?.properties[0]); } + + #calcColumnSpan(columnSpan: number, relevantColumnSpanOptions: number[], layoutColumns: number) { + if (relevantColumnSpanOptions.length > 0) { + // Correct to a columnSpan option. + const newColumnSpan = + closestColumnSpanOption(columnSpan, relevantColumnSpanOptions, layoutColumns) ?? layoutColumns; + if (newColumnSpan !== columnSpan) { + return newColumnSpan; + } + } else { + // Reset to the layoutColumns. + if (layoutColumns !== columnSpan) { + return layoutColumns; + } + } + return columnSpan; + } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts index becca70930..a2d7737ec8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts @@ -1,7 +1,7 @@ import type { UmbBlockGridLayoutModel, UmbBlockGridTypeModel } from '../types.js'; import type { UmbBlockGridWorkspaceData } from '../index.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import { UmbArrayState, appendToFrozenArray } from '@umbraco-cms/backoffice/observable-api'; +import { UmbArrayState, appendToFrozenArray, partialUpdateFrozenArray } from '@umbraco-cms/backoffice/observable-api'; import { type UmbBlockDataType, UmbBlockManagerContext } from '@umbraco-cms/backoffice/block'; import type { UmbBlockTypeGroup } from '@umbraco-cms/backoffice/block-type'; @@ -43,9 +43,10 @@ export class UmbBlockGridManagerContext< /** * Inserts a layout entry into an area of a layout entry. * @param layoutEntry The layout entry to insert. - * @param content The content data to insert. - * @param settings The settings data to insert. - * @param modalData The modal data. + * @param entries The layout entries to search within. + * @param parentUnique The parentUnique to search for. + * @param areaKey The areaKey to insert the layout entry into. + * @param index The index to insert the layout entry at. * @returns a updated layout entries array if the insert was successful. * * @remarks @@ -63,53 +64,58 @@ export class UmbBlockGridManagerContext< ): Array | undefined { // I'm sorry, this code is not easy to read or maintain [NL] let i: number = entries.length; - while (--i) { - const layoutEntry = entries[i]; - if (layoutEntry.contentUdi === parentId) { + while (i--) { + const currentEntry = entries[i]; + // Lets check if we found the right parent layout entry: + if (currentEntry.contentUdi === parentId) { // Append the layout entry to be inserted and unfreeze the rest of the data: + const areas = currentEntry.areas.map((x) => + x.key === areaKey + ? { ...x, items: appendToFrozenArray(x.items, insert, (x) => x.contentUdi === insert.contentUdi) } + : x, + ); return appendToFrozenArray( entries, { - ...layoutEntry, - areas: layoutEntry.areas.map((x) => - x.key === areaKey ? { ...x, items: appendToFrozenArray(x.items, insert) } : x, - ), + ...currentEntry, + areas, }, - (x) => x.contentUdi === layoutEntry.contentUdi, + (x) => x.contentUdi === currentEntry.contentUdi, ); } - let y: number = layoutEntry.areas?.length; - while (--y) { + // Otherwise check if any items of the areas are the parent layout entry we are looking for. We do so based on parentId, recursively: + let y: number = currentEntry.areas?.length; + while (y--) { // Recursively ask the items of this area to insert the layout entry, if something returns there was a match in this branch. [NL] const correctedAreaItems = this.#appendLayoutEntryToArea( insert, - layoutEntry.areas[y].items, + currentEntry.areas[y].items, parentId, areaKey, index, ); if (correctedAreaItems) { // This area got a corrected set of items, lets append those to the area and unfreeze the surrounding data: - const area = layoutEntry.areas[y]; + const area = currentEntry.areas[y]; return appendToFrozenArray( entries, { - ...layoutEntry, + ...currentEntry, areas: appendToFrozenArray( - layoutEntry.areas, + currentEntry.areas, { ...area, items: correctedAreaItems }, (z) => z.key === area.key, ), }, - (x) => x.contentUdi === layoutEntry.contentUdi, + (x) => x.contentUdi === currentEntry.contentUdi, ); } } } - // Find layout entry based on parentId, recursively, as it needs to check layout of areas as well: return undefined; } + // TODO: Remove dependency on modalData object here. [NL] Maybe change it into requiring the originData object instead. insert( layoutEntry: BlockLayoutType, content: UmbBlockDataType, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-scale-manager/block-grid-scale-manager.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-scale-manager/block-grid-scale-manager.controller.ts index a542df1664..2fd4d9f9d7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-scale-manager/block-grid-scale-manager.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-scale-manager/block-grid-scale-manager.controller.ts @@ -9,7 +9,7 @@ export interface UmbBlockGridScalableContext extends UmbControllerHost { setRowSpan: (rowSpan: number) => void; getColumnSpan: () => number | undefined; getRowSpan: () => number | undefined; - getMinMaxRowSpan: () => [number, number]; + getMinMaxRowSpan: () => [number, number] | undefined; getRelevantColumnSpanOptions: () => Array | undefined; } @@ -135,7 +135,10 @@ export class UmbBlockGridScaleManager extends UmbControllerBase { const bestColumnSpanOption = closestColumnSpanOption(newColumnSpan, spanOptions, layoutColumns - blockStartCol); newColumnSpan = bestColumnSpanOption ?? layoutColumns; - const [rowMinSpan, rowMaxSpan] = this._host.getMinMaxRowSpan(); + // Find allowed row spans: + const minMaxRowSpan = this._host.getMinMaxRowSpan(); + if (!minMaxRowSpan) return; + const [rowMinSpan, rowMaxSpan] = minMaxRowSpan; let newRowSpan = Math.round(Math.max(blockEndRow - blockStartRow, rowMinSpan)); if (rowMaxSpan != null) { newRowSpan = Math.min(newRowSpan, rowMaxSpan); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-column-span/property-editor-ui-block-grid-column-span.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-column-span/property-editor-ui-block-grid-column-span.element.ts index e635d38d9a..4df56123d2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-column-span/property-editor-ui-block-grid-column-span.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-column-span/property-editor-ui-block-grid-column-span.element.ts @@ -41,18 +41,18 @@ export class UmbPropertyEditorUIBlockGridColumnSpanElement extends UmbLitElement this._columnsArray, (index) => index, (index) => { - const number = index + 1; + const colNumber = index + 1; let classes = 'default'; if (this.value && this.value.length > 0) { - const applied = this.value.find((column) => column.columnSpan >= index); - const picked = this.value.find((column) => column.columnSpan === index); + const applied = this.value.find((column) => column.columnSpan >= colNumber); + const picked = this.value.find((column) => column.columnSpan === colNumber); classes = picked ? 'picked applied' : applied ? 'applied' : 'default'; } return html`
- ${number} - + ${colNumber} +
`; }, ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid.element.ts index ba9f5b71b0..21e61c2da5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid.element.ts @@ -4,14 +4,13 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { html, customElement, property, state, css, type PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; -import { - UmbPropertyValueChangeEvent, - type UmbPropertyEditorConfigCollection, -} from '@umbraco-cms/backoffice/property-editor'; +import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import type { UmbBlockTypeGroup } from '@umbraco-cms/backoffice/block-type'; import type { UmbBlockGridTypeModel, UmbBlockGridValueModel } from '@umbraco-cms/backoffice/block-grid'; import type { NumberRangeValueType } from '@umbraco-cms/backoffice/models'; import '../../components/block-grid-entries/index.js'; +import { observeMultiple } from '@umbraco-cms/backoffice/observable-api'; +import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property'; /** * @element umb-property-editor-ui-block-grid @@ -63,8 +62,8 @@ export class UmbPropertyEditorUIBlockGridElement extends UmbLitElement implement this._value = buildUpValue as UmbBlockGridValueModel; this.#context.setLayouts(this._value.layout[UMB_BLOCK_GRID_PROPERTY_EDITOR_ALIAS] ?? []); - this.#context.setContents(buildUpValue.contentData); - this.#context.setSettings(buildUpValue.settingsData); + this.#context.setContents(this._value.contentData); + this.#context.setSettings(this._value.settingsData); } public get value(): UmbBlockGridValueModel { return this._value; @@ -73,30 +72,24 @@ export class UmbPropertyEditorUIBlockGridElement extends UmbLitElement implement constructor() { super(); - // TODO: Prevent initial notification from these observes: - this.observe(this.#context.layouts, (layouts) => { - this._value = { ...this._value, layout: { [UMB_BLOCK_GRID_PROPERTY_EDITOR_ALIAS]: layouts } }; - this.#fireChangeEvent(); - }); - this.observe(this.#context.contents, (contents) => { - this._value = { ...this._value, contentData: contents }; - this.#fireChangeEvent(); - }); - this.observe(this.#context.settings, (settings) => { - this._value = { ...this._value, settingsData: settings }; - this.#fireChangeEvent(); + // TODO: Prevent initial notification from these observes + this.consumeContext(UMB_PROPERTY_CONTEXT, (propertyContext) => { + this.observe( + observeMultiple([this.#context.layouts, this.#context.contents, this.#context.settings]), + ([layouts, contents, settings]) => { + this._value = { + ...this._value, + layout: { [UMB_BLOCK_GRID_PROPERTY_EDITOR_ALIAS]: layouts }, + contentData: contents, + settingsData: settings, + }; + propertyContext.setValue(this._value); + }, + 'motherObserver', + ); }); } - #debounceChangeEvent?: boolean; - #fireChangeEvent = async () => { - if (this.#debounceChangeEvent) return; - this.#debounceChangeEvent = true; - await Promise.resolve(); - this.dispatchEvent(new UmbPropertyValueChangeEvent()); - this.#debounceChangeEvent = false; - }; - protected firstUpdated(_changedProperties: PropertyValueMap | Map): void { super.firstUpdated(_changedProperties); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entries.context.ts index b34f6285cf..f3c7ea6b97 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entries.context.ts @@ -87,6 +87,11 @@ export class UmbBlockListEntriesContext extends UmbBlockEntriesContext< return this._catalogueRouteBuilderState.getValue()?.({ view: 'clipboard', index: index }); } + async setLayouts(layouts: Array) { + await this._retrieveManager; + this._manager?.setLayouts(layouts); + } + async create( contentElementTypeKey: string, partialLayoutEntry?: Omit, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts index 37270913ec..abf6497b5f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts @@ -121,17 +121,6 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement constructor() { super(); - /* - this.consumeContext(UMB_PROPERTY_CONTEXT, (propertyContext) => { - this.observe( - propertyContext?.alias, - (alias) => { - this.#catalogueModal.setUniquePathValue('propertyAlias', alias); - }, - 'observePropertyAlias', - ); - }); - */ this.observe(this.#entriesContext.layoutEntries, (layouts) => { this._layouts = layouts; // Update sorter. @@ -167,34 +156,10 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement this.observe(this.#entriesContext.catalogueRouteBuilder, (routeBuilder) => { this._catalogueRouteBuilder = routeBuilder; }); - - /* - this.#catalogueModal = new UmbModalRouteRegistrationController(this, UMB_BLOCK_CATALOGUE_MODAL) - .addUniquePaths(['propertyAlias']) - .addAdditionalPath(':view/:index') - .onSetup((routingInfo) => { - const index = routingInfo.index ? parseInt(routingInfo.index) : -1; - return { - data: { - blocks: this._blocks ?? [], - openClipboard: routingInfo.view === 'clipboard', - blockOriginData: { index: index }, - }, - }; - }) - .observeRouteBuilder((routeBuilder) => { - this._catalogueRouteBuilder = routeBuilder; - }); - */ } - #debounceChangeEvent?: boolean; - #fireChangeEvent = async () => { - if (this.#debounceChangeEvent) return; - this.#debounceChangeEvent = true; - await Promise.resolve(); + #fireChangeEvent = () => { this.dispatchEvent(new UmbPropertyValueChangeEvent()); - this.#debounceChangeEvent = false; }; render() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts index 0430a35b12..0e375270b4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts @@ -84,6 +84,9 @@ export abstract class UmbBlockEntriesContext< layoutOf(contentUdi: string) { return this._layoutEntries.asObservablePart((source) => source.find((x) => x.contentUdi === contentUdi)); } + getLayoutOf(contentUdi: string) { + return this._layoutEntries.getValue().find((x) => x.contentUdi === contentUdi); + } setLayouts(layouts: Array) { return this._layoutEntries.setValue(layouts); } @@ -123,7 +126,8 @@ export abstract class UmbBlockEntriesContext< this._manager?.removeOneContent(contentUdi); - this._layoutEntries.removeOne(contentUdi); + //this._layoutEntries.removeOne(contentUdi); + alert('TODO: Remove layout entry..'); } //copy } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts index 1517afa740..ad79fc4d13 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts @@ -204,7 +204,7 @@ export abstract class UmbBlockEntryContext< this.observe( this._entries.layoutOf(this.#contentUdi), (layout) => { - this._layout.setValue(this._gotLayout(layout)); + this._layout.setValue(layout); }, 'observeParentLayout', ); @@ -219,10 +219,6 @@ export abstract class UmbBlockEntryContext< ); } - protected _gotLayout(layout: BlockLayoutType | undefined) { - return layout; - } - #gotManager() { this.#observeBlockType(); this.#observeData(); @@ -361,9 +357,10 @@ export abstract class UmbBlockEntryContext< } async requestDelete() { + const blockName = this.getLabel(); await umbConfirmModal(this, { - headline: `Delete ${this.getLabel()}`, - content: 'Are you sure you want to delete this [INSERT BLOCK TYPE NAME]?', + headline: `Delete ${blockName}`, + content: `Are you sure you want to delete this ${blockName}?`, confirmLabel: 'Delete', color: 'danger', }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts index c8c9ab528b..4f86a9475e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts @@ -164,9 +164,9 @@ export abstract class UmbBlockManagerContext< return this.#contents.value.find((x) => x.udi === contentUdi); } - /*setOneLayout(layoutData: BlockLayoutType) { - return this._layouts.appendOne(layoutData); - }*/ + setOneLayout(layoutData: BlockLayoutType) { + this._layouts.appendOne(layoutData); + } setOneContent(contentData: UmbBlockDataType) { this.#contents.appendOne(contentData); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts index afdfb4321b..d0c3ad6c42 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts @@ -65,9 +65,30 @@ export class UmbBlockElementManager extends UmbControllerBase { } async setPropertyValue(alias: string, value: unknown) { + this.initiatePropertyValueChange(); await this.#getDataPromise; this.#data.update({ [alias]: value }); + this.finishPropertyValueChange(); + } + + #updateLock = 0; + initiatePropertyValueChange() { + this.#updateLock++; + this.#data.mute(); + // TODO: When ready enable this code will enable handling a finish automatically by this implementation 'using myState.initiatePropertyValueChange()' (Relies on TS support of Using) [NL] + /*return { + [Symbol.dispose]: this.finishPropertyValueChange, + };*/ + } + finishPropertyValueChange = () => { + this.#updateLock--; + this.#triggerPropertyValueChanges(); + }; + #triggerPropertyValueChanges() { + if (this.#updateLock === 0) { + this.#data.unmute(); + } } public createPropertyDatasetContext(host: UmbControllerHost) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts index 43e85a2c5f..624e93d557 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts @@ -242,7 +242,7 @@ export class UmbBlockWorkspaceContext { if (layoutData) { - this.#blockEntries?.setOneLayout(layoutData); + this.#blockManager?.setOneLayout(layoutData); } }); this.observe(this.content.data, (contentData) => { @@ -324,7 +324,7 @@ export class UmbBlockWorkspaceContext extends UmbContextBase( + async propertyValueByAliasAndVariantId( propertyAlias: string, propertyVariantId: UmbVariantId, ): Promise | undefined> { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/workspace/document-blueprint-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/workspace/document-blueprint-workspace.context.ts index 8cde0d626b..c71640cb62 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/workspace/document-blueprint-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/workspace/document-blueprint-workspace.context.ts @@ -75,9 +75,7 @@ export class UmbDocumentBlueprintWorkspaceContext readonly structure = new UmbContentTypeStructureManager(this, new UmbDocumentTypeDetailRepository(this)); readonly variesByCulture = this.structure.ownerContentTypePart((x) => x?.variesByCulture); - //#variesByCulture?: boolean; readonly variesBySegment = this.structure.ownerContentTypePart((x) => x?.variesBySegment); - //#variesBySegment?: boolean; readonly varies = this.structure.ownerContentTypePart((x) => x ? x.variesByCulture || x.variesBySegment : undefined, ); @@ -249,14 +247,6 @@ export class UmbDocumentBlueprintWorkspaceContext } setName(name: string, variantId?: UmbVariantId) { - // const oldVariants = this.#currentData.getValue()?.variants || []; - // const variants = partialUpdateFrozenArray( - // oldVariants, - // { name }, - // variantId ? (x) => variantId.compare(x) : () => true, - // ); - // this.#currentData.update({ variants }); - this.#updateVariantData(variantId ?? UmbVariantId.CreateInvariant(), { name }); } @@ -314,6 +304,25 @@ export class UmbDocumentBlueprintWorkspaceContext } } + #updateLock = 0; + initiatePropertyValueChange() { + this.#updateLock++; + this.#currentData.mute(); + // TODO: When ready enable this code will enable handling a finish automatically by this implementation 'using myState.initiatePropertyValueChange()' (Relies on TS support of Using) [NL] + /*return { + [Symbol.dispose]: this.finishPropertyValueChange, + };*/ + } + finishPropertyValueChange = () => { + this.#updateLock--; + this.#triggerPropertyValueChanges(); + }; + #triggerPropertyValueChanges() { + if (this.#updateLock === 0) { + this.#currentData.unmute(); + } + } + /* #calculateChangedVariants() { const persisted = this.#persistedData.getValue(); const current = this.#currentData.getValue(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-dataset-context/document-property-dataset-context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-dataset-context/document-property-dataset-context.ts index e6582e070b..216aa4ea9a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-dataset-context/document-property-dataset-context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-dataset-context/document-property-dataset-context.ts @@ -9,7 +9,7 @@ import type { UmbVariantModel } from '@umbraco-cms/backoffice/variant'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type'; -// TODO: This code can be split into a UmbContentTypePropertyDatasetContext, leaving just the publishing state and methods to this class. +// TODO: This code can be split into a UmbContentTypePropertyDatasetContext, leaving just the publishing state and methods to this class. [NL] export class UmbDocumentPropertyDataContext extends UmbContextBase implements UmbPropertyDatasetContext, UmbNameablePropertyDatasetContext @@ -27,11 +27,6 @@ export class UmbDocumentPropertyDataContext culture = this.#currentVariant.asObservablePart((x) => x?.culture); segment = this.#currentVariant.asObservablePart((x) => x?.segment); - // TODO: Refactor: Make a properties observable. (with such I think i mean a property value object array.. array with object with properties, alias, value, culture and segment) - // TO make such happen I think we need to maintain all properties and their value of this object. - // This will actually make it simpler if multiple are watching the same property. - // But it will also mean that we wil watch all properties and their structure, for variantID, all the time for all of the properties. - getEntityType(): string { return this.#workspace.getEntityType(); } @@ -72,8 +67,10 @@ export class UmbDocumentPropertyDataContext } /** - * TODO: Write proper JSDocs here. - * Ideally do not use these methods, its better to communicate directly with the workspace, but if you do not know the property variant id, then this will figure it out for you. So good for externals to set or get values of a property. + * @method propertyVariantId + * @param {string} propertyAlias + * @returns {Promise | undefined>} + * @description Get an Observable for the variant id of this property. */ async propertyVariantId(propertyAlias: string) { return (await this.#workspace.structure.propertyStructureByAlias(propertyAlias)).pipe( @@ -82,8 +79,10 @@ export class UmbDocumentPropertyDataContext } /** - * TODO: Write proper JSDocs here. - * Ideally do not use this method, its better to communicate directly with the workspace, but if you do not know the property variant id, then this will figure it out for you. So good for externals to set or get values of a property. + * @method propertyValueByAlias + * @param {string} propertyAlias + * @returns {Promise | undefined>} + * @description Get an Observable for the value of this property. */ async propertyValueByAlias( propertyAlias: string, @@ -97,7 +96,7 @@ export class UmbDocumentPropertyDataContext } // TODO: Refactor: Not used currently, but should investigate if we can implement this, to spare some energy. - async propertyValueByAliasAndCulture( + async propertyValueByAliasAndVariantId( propertyAlias: string, propertyVariantId: UmbVariantId, ): Promise | undefined> { @@ -105,17 +104,38 @@ export class UmbDocumentPropertyDataContext } /** - * TODO: Write proper JSDocs here. - * Ideally do not use these methods, its better to communicate directly with the workspace, but if you do not know the property variant id, then this will figure it out for you. So good for externals to set or get values of a property. + * @method setPropertyValueByVariant + * @param {string} propertyAlias + * @param {PromiseLike} value - value can be a promise resolving into the actual value or the raw value it self. + * @param {UmbVariantId} propertyVariantId - The variant id for the value to be set for. + * @returns {Promise} + * @description Get the value of this property. */ - async setPropertyValue(propertyAlias: string, value: unknown) { + setPropertyValueByVariant( + propertyAlias: string, + value: PromiseLike, + propertyVariantId: UmbVariantId, + ): Promise { + return this.#workspace.setPropertyValue(propertyAlias, value, propertyVariantId); + } + + /** + * @method setPropertyValue + * @param {string} propertyAlias + * @param {PromiseLike} value - value can be a promise resolving into the actual value or the raw value it self. + * @returns {Promise} + * @description Set the value of this property. + */ + async setPropertyValue(propertyAlias: string, value: PromiseLike) { + this.#workspace.initiatePropertyValueChange(); // This is not reacting to if the property variant settings changes while running. const property = await this.#workspace.structure.getPropertyStructureByAlias(propertyAlias); if (property) { const variantId = this.#createPropertyVariantId(property); // This is not reacting to if the property variant settings changes while running. - this.#workspace.setPropertyValue(propertyAlias, value, variantId); + this.#workspace.setPropertyValue(propertyAlias, await value, variantId); } + this.#workspace.finishPropertyValueChange(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 0b3ffd34c7..4bd778fc6c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -117,9 +117,7 @@ export class UmbDocumentWorkspaceContext readonly structure = new UmbContentTypeStructureManager(this, new UmbDocumentTypeDetailRepository(this)); readonly variesByCulture = this.structure.ownerContentTypePart((x) => x?.variesByCulture); - //#variesByCulture?: boolean; readonly variesBySegment = this.structure.ownerContentTypePart((x) => x?.variesBySegment); - //#variesBySegment?: boolean; readonly varies = this.structure.ownerContentTypePart((x) => x ? x.variesByCulture || x.variesBySegment : undefined, ); @@ -342,15 +340,6 @@ export class UmbDocumentWorkspaceContext } setName(name: string, variantId?: UmbVariantId) { - /* - const oldVariants = this.#currentData.getValue()?.variants || []; - const variants = partialUpdateFrozenArray( - oldVariants, - { name }, - variantId ? (x) => variantId.compare(x) : () => true, - ); - this.#currentData.update({ variants }); - */ // TODO: We should move this type of logic to the act of saving [NL] this.#updateVariantData(variantId ?? UmbVariantId.CreateInvariant(), { name }); } @@ -421,6 +410,25 @@ export class UmbDocumentWorkspaceContext } } + #updateLock = 0; + initiatePropertyValueChange() { + this.#updateLock++; + this.#currentData.mute(); + // TODO: When ready enable this code will enable handling a finish automatically by this implementation 'using myState.initiatePropertyValueChange()' (Relies on TS support of Using) [NL] + /*return { + [Symbol.dispose]: this.finishPropertyValueChange, + };*/ + } + finishPropertyValueChange = () => { + this.#updateLock--; + this.#triggerPropertyValueChanges(); + }; + #triggerPropertyValueChanges() { + if (this.#updateLock === 0) { + this.#currentData.unmute(); + } + } + #calculateChangedVariants() { const persisted = this.#persistedData.getValue(); const current = this.#currentData.getValue(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-dataset-context/media-property-dataset-context.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-dataset-context/media-property-dataset-context.ts index b6c3741116..c9bee944e0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-dataset-context/media-property-dataset-context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-dataset-context/media-property-dataset-context.ts @@ -97,7 +97,7 @@ export class UmbMediaPropertyDataContext } // TODO: Refactor: Not used currently, but should investigate if we can implement this, to spare some energy. - async propertyValueByAliasAndCulture(propertyAlias: string, propertyVariantId: UmbVariantId) { + async propertyValueByAliasAndVariantId(propertyAlias: string, propertyVariantId: UmbVariantId) { return this.#workspace.propertyValueByAlias(propertyAlias, propertyVariantId); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts index 11a8fa2103..c0575bbbde 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts @@ -71,9 +71,7 @@ export class UmbMediaWorkspaceContext readonly structure = new UmbContentTypeStructureManager(this, new UmbMediaTypeDetailRepository(this)); readonly variesByCulture = this.structure.ownerContentTypePart((x) => x?.variesByCulture); - //#variesByCulture?: boolean; readonly variesBySegment = this.structure.ownerContentTypePart((x) => x?.variesBySegment); - //#variesBySegment?: boolean; readonly varies = this.structure.ownerContentTypePart((x) => x ? x.variesByCulture || x.variesBySegment : undefined, ); @@ -250,14 +248,6 @@ export class UmbMediaWorkspaceContext } setName(name: string, variantId?: UmbVariantId) { - // const oldVariants = this.#currentData.getValue()?.variants || []; - // const variants = partialUpdateFrozenArray( - // oldVariants, - // { name }, - // variantId ? (x) => variantId.compare(x) : () => true, - // ); - // this.#currentData.update({ variants }); - this.#updateVariantData(variantId ?? UmbVariantId.CreateInvariant(), { name }); } @@ -315,6 +305,25 @@ export class UmbMediaWorkspaceContext } } + #updateLock = 0; + initiatePropertyValueChange() { + this.#updateLock++; + this.#currentData.mute(); + // TODO: When ready enable this code will enable handling a finish automatically by this implementation 'using myState.initiatePropertyValueChange()' (Relies on TS support of Using) [NL] + /*return { + [Symbol.dispose]: this.finishPropertyValueChange, + };*/ + } + finishPropertyValueChange = () => { + this.#updateLock--; + this.#triggerPropertyValueChanges(); + }; + #triggerPropertyValueChanges() { + if (this.#updateLock === 0) { + this.#currentData.unmute(); + } + } + /* #calculateChangedVariants() { const persisted = this.#persistedData.getValue(); const current = this.#currentData.getValue(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/property-dataset-context/member-property-dataset-context.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/property-dataset-context/member-property-dataset-context.ts index 4c496ac364..5847ce69ef 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/property-dataset-context/member-property-dataset-context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/property-dataset-context/member-property-dataset-context.ts @@ -97,7 +97,7 @@ export class UmbMemberPropertyDataContext } // TODO: Refactor: Not used currently, but should investigate if we can implement this, to spare some energy. - async propertyValueByAliasAndCulture(propertyAlias: string, propertyVariantId: UmbVariantId) { + async propertyValueByAliasAndVariantId(propertyAlias: string, propertyVariantId: UmbVariantId) { return this.#workspace.propertyValueByAlias(propertyAlias, propertyVariantId); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context.ts index 97cbeb3085..24ef107b13 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context.ts @@ -218,16 +218,6 @@ export class UmbMemberWorkspaceContext } setName(name: string, variantId?: UmbVariantId) { - /* - const oldVariants = this.#currentData.getValue()?.variants || []; - const variants = partialUpdateFrozenArray( - oldVariants, - { name }, - variantId ? (x) => variantId.compare(x) : () => true, - ); - this.#currentData.update({ variants }); - */ - // TODO: We should move this type of logic to the act of saving [NL] this.#updateVariantData(variantId ?? UmbVariantId.CreateInvariant(), { name }); } @@ -285,6 +275,25 @@ export class UmbMemberWorkspaceContext } } + #updateLock = 0; + initiatePropertyValueChange() { + this.#updateLock++; + this.#currentData.mute(); + // TODO: When ready enable this code will enable handling a finish automatically by this implementation 'using myState.initiatePropertyValueChange()' (Relies on TS support of Using) [NL] + /*return { + [Symbol.dispose]: this.finishPropertyValueChange, + };*/ + } + finishPropertyValueChange = () => { + this.#updateLock--; + this.#triggerPropertyValueChanges(); + }; + #triggerPropertyValueChanges() { + if (this.#updateLock === 0) { + this.#currentData.unmute(); + } + } + #updateVariantData(variantId: UmbVariantId, update?: Partial) { const currentData = this.getData(); if (!currentData) throw new Error('Data is missing');