diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 6606d01495..54a7c28c35 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -17,7 +17,7 @@ "@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", - "element-internals-polyfill": "^1.1.17", + "element-internals-polyfill": "^1.1.18", "lit": "^2.5.0", "lodash": "^4.17.21", "openapi-typescript-fetch": "^1.1.3", @@ -29,7 +29,7 @@ "@babel/core": "^7.20.12", "@mdx-js/react": "^2.2.1", "@open-wc/testing": "^3.1.7", - "@playwright/test": "^1.29.1", + "@playwright/test": "^1.29.2", "@storybook/addon-a11y": "^6.5.15", "@storybook/addon-actions": "^6.5.14", "@storybook/addon-essentials": "^6.5.15", @@ -41,8 +41,8 @@ "@types/lodash-es": "^4.17.6", "@types/mocha": "^10.0.0", "@types/uuid": "^9.0.0", - "@typescript-eslint/eslint-plugin": "^5.48.0", - "@typescript-eslint/parser": "^5.48.0", + "@typescript-eslint/eslint-plugin": "^5.48.1", + "@typescript-eslint/parser": "^5.48.1", "@web/dev-server-esbuild": "^0.3.3", "@web/dev-server-import-maps": "^0.0.7", "@web/test-runner": "^0.15.0", @@ -2897,13 +2897,13 @@ "dev": true }, "node_modules/@playwright/test": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.1.tgz", - "integrity": "sha512-iQxk2DX5U9wOGV3+/Jh9OHPsw5H3mleUL2S4BgQuwtlAfK3PnKvn38m4Rg9zIViGHVW24opSm99HQm/UFLEy6w==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.2.tgz", + "integrity": "sha512-+3/GPwOgcoF0xLz/opTnahel1/y42PdcgZ4hs+BZGIUjtmEFSXGg+nFoaH3NSmuc7a6GSFwXDJ5L7VXpqzigNg==", "dev": true, "dependencies": { "@types/node": "*", - "playwright-core": "1.29.1" + "playwright-core": "1.29.2" }, "bin": { "playwright": "cli.js" @@ -2913,9 +2913,9 @@ } }, "node_modules/@playwright/test/node_modules/playwright-core": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.1.tgz", - "integrity": "sha512-20Ai3d+lMkWpI9YZYlxk8gxatfgax5STW8GaMozAHwigLiyiKQrdkt7gaoT9UQR8FIVDg6qVXs9IoZUQrDjIIg==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.2.tgz", + "integrity": "sha512-94QXm4PMgFoHAhlCuoWyaBYKb92yOcGVHdQLoxQ7Wjlc7Flg4aC/jbFW7xMR52OfXMVkWicue4WXE7QEegbIRA==", "dev": true, "bin": { "playwright": "cli.js" @@ -6375,14 +6375,14 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.0.tgz", - "integrity": "sha512-SVLafp0NXpoJY7ut6VFVUU9I+YeFsDzeQwtK0WZ+xbRN3mtxJ08je+6Oi2N89qDn087COdO0u3blKZNv9VetRQ==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.1.tgz", + "integrity": "sha512-9nY5K1Rp2ppmpb9s9S2aBiF3xo5uExCehMDmYmmFqqyxgenbHJ3qbarcLt4ITgaD6r/2ypdlcFRdcuVPnks+fQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.48.0", - "@typescript-eslint/type-utils": "5.48.0", - "@typescript-eslint/utils": "5.48.0", + "@typescript-eslint/scope-manager": "5.48.1", + "@typescript-eslint/type-utils": "5.48.1", + "@typescript-eslint/utils": "5.48.1", "debug": "^4.3.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", @@ -6423,14 +6423,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.0.tgz", - "integrity": "sha512-1mxNA8qfgxX8kBvRDIHEzrRGrKHQfQlbW6iHyfHYS0Q4X1af+S6mkLNtgCOsGVl8+/LUPrqdHMssAemkrQ01qg==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.1.tgz", + "integrity": "sha512-4yg+FJR/V1M9Xoq56SF9Iygqm+r5LMXvheo6DQ7/yUWynQ4YfCRnsKuRgqH4EQ5Ya76rVwlEpw4Xu+TgWQUcdA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.48.0", - "@typescript-eslint/types": "5.48.0", - "@typescript-eslint/typescript-estree": "5.48.0", + "@typescript-eslint/scope-manager": "5.48.1", + "@typescript-eslint/types": "5.48.1", + "@typescript-eslint/typescript-estree": "5.48.1", "debug": "^4.3.4" }, "engines": { @@ -6450,13 +6450,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.0.tgz", - "integrity": "sha512-0AA4LviDtVtZqlyUQnZMVHydDATpD9SAX/RC5qh6cBd3xmyWvmXYF+WT1oOmxkeMnWDlUVTwdODeucUnjz3gow==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz", + "integrity": "sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.48.0", - "@typescript-eslint/visitor-keys": "5.48.0" + "@typescript-eslint/types": "5.48.1", + "@typescript-eslint/visitor-keys": "5.48.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -6467,13 +6467,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.0.tgz", - "integrity": "sha512-vbtPO5sJyFjtHkGlGK4Sthmta0Bbls4Onv0bEqOGm7hP9h8UpRsHJwsrCiWtCUndTRNQO/qe6Ijz9rnT/DB+7g==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.1.tgz", + "integrity": "sha512-Hyr8HU8Alcuva1ppmqSYtM/Gp0q4JOp1F+/JH5D1IZm/bUBrV0edoewQZiEc1r6I8L4JL21broddxK8HAcZiqQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.48.0", - "@typescript-eslint/utils": "5.48.0", + "@typescript-eslint/typescript-estree": "5.48.1", + "@typescript-eslint/utils": "5.48.1", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -6494,9 +6494,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.0.tgz", - "integrity": "sha512-UTe67B0Ypius0fnEE518NB2N8gGutIlTojeTg4nt0GQvikReVkurqxd2LvYa9q9M5MQ6rtpNyWTBxdscw40Xhw==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.1.tgz", + "integrity": "sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -6507,13 +6507,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.0.tgz", - "integrity": "sha512-7pjd94vvIjI1zTz6aq/5wwE/YrfIyEPLtGJmRfyNR9NYIW+rOvzzUv3Cmq2hRKpvt6e9vpvPUQ7puzX7VSmsEw==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz", + "integrity": "sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.48.0", - "@typescript-eslint/visitor-keys": "5.48.0", + "@typescript-eslint/types": "5.48.1", + "@typescript-eslint/visitor-keys": "5.48.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -6549,16 +6549,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.0.tgz", - "integrity": "sha512-x2jrMcPaMfsHRRIkL+x96++xdzvrdBCnYRd5QiW5Wgo1OB4kDYPbC1XjWP/TNqlfK93K/lUL92erq5zPLgFScQ==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.1.tgz", + "integrity": "sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.48.0", - "@typescript-eslint/types": "5.48.0", - "@typescript-eslint/typescript-estree": "5.48.0", + "@typescript-eslint/scope-manager": "5.48.1", + "@typescript-eslint/types": "5.48.1", + "@typescript-eslint/typescript-estree": "5.48.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" @@ -6590,12 +6590,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.0.tgz", - "integrity": "sha512-5motVPz5EgxQ0bHjut3chzBkJ3Z3sheYVcSwS5BpHZpLqSptSmELNtGixmgj65+rIfhvtQTz5i9OP2vtzdDH7Q==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz", + "integrity": "sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.48.0", + "@typescript-eslint/types": "5.48.1", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -11810,9 +11810,9 @@ "dev": true }, "node_modules/element-internals-polyfill": { - "version": "1.1.17", - "resolved": "https://registry.npmjs.org/element-internals-polyfill/-/element-internals-polyfill-1.1.17.tgz", - "integrity": "sha512-sMDJyJiwvcHB6wLnyG+y/9FRxi/9OyI8bmjyw18K6b5iVlBjmA5CJVTFz4K2I7R53yqevK8WkTrfBmSHJXH9Rw==" + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/element-internals-polyfill/-/element-internals-polyfill-1.1.18.tgz", + "integrity": "sha512-ULyzpzblTZfMPEt83NphWeREajgaKQBNSTXvNBcjTeriIy7GsuAHFUZ0CpHnlDIVdvPlWcewfu7n7vVfiifZlQ==" }, "node_modules/element-resize-detector": { "version": "1.2.4", @@ -30476,19 +30476,19 @@ } }, "@playwright/test": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.1.tgz", - "integrity": "sha512-iQxk2DX5U9wOGV3+/Jh9OHPsw5H3mleUL2S4BgQuwtlAfK3PnKvn38m4Rg9zIViGHVW24opSm99HQm/UFLEy6w==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.2.tgz", + "integrity": "sha512-+3/GPwOgcoF0xLz/opTnahel1/y42PdcgZ4hs+BZGIUjtmEFSXGg+nFoaH3NSmuc7a6GSFwXDJ5L7VXpqzigNg==", "dev": true, "requires": { "@types/node": "*", - "playwright-core": "1.29.1" + "playwright-core": "1.29.2" }, "dependencies": { "playwright-core": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.1.tgz", - "integrity": "sha512-20Ai3d+lMkWpI9YZYlxk8gxatfgax5STW8GaMozAHwigLiyiKQrdkt7gaoT9UQR8FIVDg6qVXs9IoZUQrDjIIg==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.2.tgz", + "integrity": "sha512-94QXm4PMgFoHAhlCuoWyaBYKb92yOcGVHdQLoxQ7Wjlc7Flg4aC/jbFW7xMR52OfXMVkWicue4WXE7QEegbIRA==", "dev": true } } @@ -33057,14 +33057,14 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.0.tgz", - "integrity": "sha512-SVLafp0NXpoJY7ut6VFVUU9I+YeFsDzeQwtK0WZ+xbRN3mtxJ08je+6Oi2N89qDn087COdO0u3blKZNv9VetRQ==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.1.tgz", + "integrity": "sha512-9nY5K1Rp2ppmpb9s9S2aBiF3xo5uExCehMDmYmmFqqyxgenbHJ3qbarcLt4ITgaD6r/2ypdlcFRdcuVPnks+fQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.48.0", - "@typescript-eslint/type-utils": "5.48.0", - "@typescript-eslint/utils": "5.48.0", + "@typescript-eslint/scope-manager": "5.48.1", + "@typescript-eslint/type-utils": "5.48.1", + "@typescript-eslint/utils": "5.48.1", "debug": "^4.3.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", @@ -33085,53 +33085,53 @@ } }, "@typescript-eslint/parser": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.0.tgz", - "integrity": "sha512-1mxNA8qfgxX8kBvRDIHEzrRGrKHQfQlbW6iHyfHYS0Q4X1af+S6mkLNtgCOsGVl8+/LUPrqdHMssAemkrQ01qg==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.1.tgz", + "integrity": "sha512-4yg+FJR/V1M9Xoq56SF9Iygqm+r5LMXvheo6DQ7/yUWynQ4YfCRnsKuRgqH4EQ5Ya76rVwlEpw4Xu+TgWQUcdA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.48.0", - "@typescript-eslint/types": "5.48.0", - "@typescript-eslint/typescript-estree": "5.48.0", + "@typescript-eslint/scope-manager": "5.48.1", + "@typescript-eslint/types": "5.48.1", + "@typescript-eslint/typescript-estree": "5.48.1", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.0.tgz", - "integrity": "sha512-0AA4LviDtVtZqlyUQnZMVHydDATpD9SAX/RC5qh6cBd3xmyWvmXYF+WT1oOmxkeMnWDlUVTwdODeucUnjz3gow==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz", + "integrity": "sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.48.0", - "@typescript-eslint/visitor-keys": "5.48.0" + "@typescript-eslint/types": "5.48.1", + "@typescript-eslint/visitor-keys": "5.48.1" } }, "@typescript-eslint/type-utils": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.0.tgz", - "integrity": "sha512-vbtPO5sJyFjtHkGlGK4Sthmta0Bbls4Onv0bEqOGm7hP9h8UpRsHJwsrCiWtCUndTRNQO/qe6Ijz9rnT/DB+7g==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.1.tgz", + "integrity": "sha512-Hyr8HU8Alcuva1ppmqSYtM/Gp0q4JOp1F+/JH5D1IZm/bUBrV0edoewQZiEc1r6I8L4JL21broddxK8HAcZiqQ==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.48.0", - "@typescript-eslint/utils": "5.48.0", + "@typescript-eslint/typescript-estree": "5.48.1", + "@typescript-eslint/utils": "5.48.1", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.0.tgz", - "integrity": "sha512-UTe67B0Ypius0fnEE518NB2N8gGutIlTojeTg4nt0GQvikReVkurqxd2LvYa9q9M5MQ6rtpNyWTBxdscw40Xhw==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.1.tgz", + "integrity": "sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.0.tgz", - "integrity": "sha512-7pjd94vvIjI1zTz6aq/5wwE/YrfIyEPLtGJmRfyNR9NYIW+rOvzzUv3Cmq2hRKpvt6e9vpvPUQ7puzX7VSmsEw==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz", + "integrity": "sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.48.0", - "@typescript-eslint/visitor-keys": "5.48.0", + "@typescript-eslint/types": "5.48.1", + "@typescript-eslint/visitor-keys": "5.48.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -33151,16 +33151,16 @@ } }, "@typescript-eslint/utils": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.0.tgz", - "integrity": "sha512-x2jrMcPaMfsHRRIkL+x96++xdzvrdBCnYRd5QiW5Wgo1OB4kDYPbC1XjWP/TNqlfK93K/lUL92erq5zPLgFScQ==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.1.tgz", + "integrity": "sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.48.0", - "@typescript-eslint/types": "5.48.0", - "@typescript-eslint/typescript-estree": "5.48.0", + "@typescript-eslint/scope-manager": "5.48.1", + "@typescript-eslint/types": "5.48.1", + "@typescript-eslint/typescript-estree": "5.48.1", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" @@ -33178,12 +33178,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.0.tgz", - "integrity": "sha512-5motVPz5EgxQ0bHjut3chzBkJ3Z3sheYVcSwS5BpHZpLqSptSmELNtGixmgj65+rIfhvtQTz5i9OP2vtzdDH7Q==", + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz", + "integrity": "sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.48.0", + "@typescript-eslint/types": "5.48.1", "eslint-visitor-keys": "^3.3.0" } }, @@ -37472,9 +37472,9 @@ "dev": true }, "element-internals-polyfill": { - "version": "1.1.17", - "resolved": "https://registry.npmjs.org/element-internals-polyfill/-/element-internals-polyfill-1.1.17.tgz", - "integrity": "sha512-sMDJyJiwvcHB6wLnyG+y/9FRxi/9OyI8bmjyw18K6b5iVlBjmA5CJVTFz4K2I7R53yqevK8WkTrfBmSHJXH9Rw==" + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/element-internals-polyfill/-/element-internals-polyfill-1.1.18.tgz", + "integrity": "sha512-ULyzpzblTZfMPEt83NphWeREajgaKQBNSTXvNBcjTeriIy7GsuAHFUZ0CpHnlDIVdvPlWcewfu7n7vVfiifZlQ==" }, "element-resize-detector": { "version": "1.2.4", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 2f0ec3c4c4..d71a1eadcb 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -62,7 +62,7 @@ "@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.17", + "element-internals-polyfill": "^1.1.18", "lit": "^2.5.0", "lodash": "^4.17.21", "openapi-typescript-fetch": "^1.1.3", @@ -74,7 +74,7 @@ "@babel/core": "^7.20.12", "@mdx-js/react": "^2.2.1", "@open-wc/testing": "^3.1.7", - "@playwright/test": "^1.29.1", + "@playwright/test": "^1.29.2", "@storybook/addon-a11y": "^6.5.15", "@storybook/addon-actions": "^6.5.14", "@storybook/addon-essentials": "^6.5.15", @@ -86,8 +86,8 @@ "@types/lodash-es": "^4.17.6", "@types/mocha": "^10.0.0", "@types/uuid": "^9.0.0", - "@typescript-eslint/eslint-plugin": "^5.48.0", - "@typescript-eslint/parser": "^5.48.0", + "@typescript-eslint/eslint-plugin": "^5.48.1", + "@typescript-eslint/parser": "^5.48.1", "@web/dev-server-esbuild": "^0.3.3", "@web/dev-server-import-maps": "^0.0.7", "@web/test-runner": "^0.15.0", diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.store.ts index 3e4f355374..6bfc435fed 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.store.ts @@ -3,7 +3,7 @@ import { UmbDataStoreBase } from '../../../core/stores/store'; import { ApiError, DocumentTypeResource, DocumentTypeTreeItem, ProblemDetails } from '@umbraco-cms/backend-api'; import type { DocumentTypeDetails } from '@umbraco-cms/models'; -const isDocumentTypeDetails = ( +export const isDocumentTypeDetails = ( documentType: DocumentTypeDetails | DocumentTypeTreeItem ): documentType is DocumentTypeDetails => { return (documentType as DocumentTypeDetails).properties !== undefined; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts index 86d909b900..519cf7110f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts @@ -23,4 +23,9 @@ export class UmbWorkspaceDocumentTypeContext extends UmbWorkspaceContentContext< constructor(host: UmbControllerHostInterface) { super(host, DefaultDocumentTypeData, 'umbDocumentTypeStore', 'documentType'); } + + + public setPropertyValue(alias: string, value: unknown) { + throw new Error("setPropertyValue is not implemented for UmbWorkspaceDocumentTypeContext") + } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.store.ts index a42b8deb66..39c9bb050e 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.store.ts @@ -3,7 +3,7 @@ import { UmbNodeStoreBase } from '../../../core/stores/store'; import type { DocumentDetails } from '@umbraco-cms/models'; import { ApiError, DocumentResource, DocumentTreeItem, FolderTreeItem, ProblemDetails } from '@umbraco-cms/backend-api'; -const isDocumentDetails = (document: DocumentDetails | DocumentTreeItem): document is DocumentDetails => { +export const isDocumentDetails = (document: DocumentDetails | DocumentTreeItem): document is DocumentDetails => { return (document as DocumentDetails).data !== undefined; }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts index 6d18c7adb8..45c863497f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts @@ -1,7 +1,8 @@ import { UmbWorkspaceContentContext } from '../../../shared/components/workspace/workspace-content/workspace-content.context'; -import { STORE_ALIAS } from 'src/backoffice/documents/documents/document.store'; +import { isDocumentDetails, STORE_ALIAS as DOCUMENT_STORE_ALIAS } from 'src/backoffice/documents/documents/document.store'; import type { UmbDocumentStore, UmbDocumentStoreItemType } from 'src/backoffice/documents/documents/document.store'; import { UmbControllerHostInterface } from 'src/core/controller/controller-host.mixin'; +import type { DocumentDetails } from '@umbraco-cms/models'; const DefaultDocumentData = { key: '', @@ -34,11 +35,26 @@ const DefaultDocumentData = { export class UmbWorkspaceDocumentContext extends UmbWorkspaceContentContext { constructor(host: UmbControllerHostInterface) { - super(host, DefaultDocumentData, STORE_ALIAS, 'document'); + super(host, DefaultDocumentData, DOCUMENT_STORE_ALIAS, 'document'); } + public setPropertyValue(alias: string, value: unknown) { + const data = this.getData(); + // TODO: make sure to check that we have a details model: + // TODO: make a Method to cast + if(isDocumentDetails(data)) { + const newDataSet = (data as DocumentDetails).data.map((entry) => { + if (entry.alias === alias) { + return {alias: alias, value: value}; + } + return entry; + }); + this.update({data: newDataSet} as Partial); + } + } + /* concept notes: @@ -51,4 +67,4 @@ export class UmbWorkspaceDocumentContext extends UmbWorkspaceContentContext { - public readonly storeAlias = 'umbDataTypeStore'; + public readonly storeAlias = STORE_ALIAS; /** * @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable. diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/content-property/content-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/content-property/content-property.element.ts index 8ee254f491..233e42ab64 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/content-property/content-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/content-property/content-property.element.ts @@ -2,14 +2,13 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html } from 'lit'; import { ifDefined } from 'lit-html/directives/if-defined.js'; import { customElement, property, state } from 'lit/decorators.js'; -import { EMPTY, of, switchMap } from 'rxjs'; import { UmbDataTypeStore } from '../../../settings/data-types/data-type.store'; -import type { ContentProperty, ManifestTypes } from '@umbraco-cms/models'; -import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; +import type { ContentProperty, DataTypeDetails } from '@umbraco-cms/models'; -import '../entity-property/entity-property.element'; +import '../workspace-property/workspace-property.element'; import { UmbLitElement } from '@umbraco-cms/element'; +import { UmbObserverController } from '@umbraco-cms/observable-api'; @customElement('umb-content-property') export class UmbContentPropertyElement extends UmbLitElement { @@ -22,18 +21,22 @@ export class UmbContentPropertyElement extends UmbLitElement { `, ]; + // TODO: Consider if we just need to get the DataType Key?.. private _property?: ContentProperty; @property({ type: Object, attribute: false }) public get property(): ContentProperty | undefined { return this._property; } public set property(value: ContentProperty | undefined) { + const oldProperty = this._property; this._property = value; - this._observeDataType(); + if(this._property?.dataTypeKey !== oldProperty?.dataTypeKey) { + this._observeDataType(this._property?.dataTypeKey); + } } @property() - value?: string; + value?: object | string; @state() private _propertyEditorUIAlias?: string; @@ -42,43 +45,40 @@ export class UmbContentPropertyElement extends UmbLitElement { private _dataTypeData?: any; private _dataTypeStore?: UmbDataTypeStore; + private _dataTypeObserver?: UmbObserverController; constructor() { super(); this.consumeContext('umbDataTypeStore', (instance) => { this._dataTypeStore = instance; - this._observeDataType(); + this._observeDataType(this._property?.dataTypeKey); }); } - private _observeDataType() { - if (!this._dataTypeStore || !this._property) return; + private _observeDataType(dataTypeKey?: string) { + if (!this._dataTypeStore) return; - this.observe( - this._dataTypeStore.getByKey(this._property.dataTypeKey).pipe( - switchMap((dataType) => { - if (!dataType?.propertyEditorUIAlias) return EMPTY; - this._dataTypeData = dataType.data; - return umbExtensionsRegistry.getByAlias(dataType.propertyEditorUIAlias) ?? of(null); - }) - ), - (manifest) => { - if (manifest?.type === 'propertyEditorUI') { - this._propertyEditorUIAlias = manifest.alias; + this._dataTypeObserver?.destroy(); + if(dataTypeKey) { + this._dataTypeObserver = this.observe( + this._dataTypeStore.getByKey(dataTypeKey), + (dataType) => { + this._dataTypeData = dataType?.data; + this._propertyEditorUIAlias = dataType?.propertyEditorUIAlias || undefined; } - } - ); + ); + } } render() { - return html``; + .value=${this.value} + .config=${this._dataTypeData}>`; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-property/entity-propery.stories.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-property/entity-propery.stories.ts deleted file mode 100644 index f0e4721217..0000000000 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-property/entity-propery.stories.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Meta, Story } from '@storybook/web-components'; -import { html } from 'lit-html'; - -import type { UmbEntityPropertyElement } from './entity-property.element'; -import './entity-property.element'; - -export default { - title: 'Components/Entity Property', - component: 'umb-entity-property', - id: 'umb-entity-property', -} as Meta; - -export const AAAOverview: Story = () => - html` `; -AAAOverview.storyName = 'Overview'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-property/workspace-property.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-property/workspace-property.context.ts deleted file mode 100644 index 6b165db95c..0000000000 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-property/workspace-property.context.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { BehaviorSubject, Observable } from "rxjs"; - -export type WorkspacePropertyData = { - alias?: string | null; - label?: string | null; - value?: ValueType | null; -}; - - - -export class UmbWorkspacePropertyContext { - - - private _data: BehaviorSubject>; - public readonly data: Observable>; - - - #defaultValue!: ValueType | null; - - - constructor(defaultValue: ValueType | null) { - - this.#defaultValue = defaultValue; - - // TODO: How do we connect this value with parent context? - // Ensuring the property editor value-property is updated... - - this._data = new BehaviorSubject({value: defaultValue} as WorkspacePropertyData); - this.data = this._data.asObservable(); - } - - /* - hostConnected() { - - } - - hostDisconnected() { - - } - */ - - public getData() { - return this._data.getValue(); - } - - - public update(data: Partial>) { - this._data.next({ ...this.getData(), ...data }); - } - - public resetValue() { - console.log("property context reset") - - this.update({value: this.#defaultValue}) - } - - - // TODO: how can we make sure to call this. - public destroy(): void { - this._data.unsubscribe(); - } - -} - diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts new file mode 100644 index 0000000000..9dcbc36281 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts @@ -0,0 +1,89 @@ +import { UmbWorkspaceContentContext } from "../workspace/workspace-content/workspace-content.context"; +import type { DataTypeDetails } from "@umbraco-cms/models"; +import { UmbControllerHostInterface } from "src/core/controller/controller-host.mixin"; +import { CreateObservablePart, UniqueBehaviorSubject } from "src/core/observable-api/unique-behavior-subject"; +import { UmbContextProviderController } from "src/core/context-api/provide/context-provider.controller"; +import { UmbContextConsumerController } from "src/core/context-api/consume/context-consumer.controller"; + + + + + + + + + + + +// If we get this from the server then we can consider using TypeScripts Partial<> around the model from the Management-API. +export type WorkspacePropertyData = { + alias?: string; + label?: string; + description?: string; + value?: ValueType | null; + config?: DataTypeDetails['data'];// This could potentially then come from hardcoded JS object and not the DataType store. +}; + +export class UmbWorkspacePropertyContext { + + + private _providerController: UmbContextProviderController; + + private _data = new UniqueBehaviorSubject>({}); + + public readonly alias = CreateObservablePart(this._data, data => data.alias); + public readonly label = CreateObservablePart(this._data, data => data.label); + public readonly description = CreateObservablePart(this._data, data => data.description); + public readonly value = CreateObservablePart(this._data, data => data.value); + public readonly config = CreateObservablePart(this._data, data => data.config); + + private _workspaceContext?: UmbWorkspaceContentContext; + + + constructor(host:UmbControllerHostInterface) { + + new UmbContextConsumerController(host, 'umbWorkspaceContext', (workspaceContext) => { + this._workspaceContext = workspaceContext; + }); + + this._providerController = new UmbContextProviderController(host, 'umbPropertyContext', this); + + + } + + public setAlias(alias: WorkspacePropertyData['alias']) { + this._data.update({alias: alias}); + } + public setLabel(label: WorkspacePropertyData['label']) { + this._data.update({label: label}); + } + public setDescription(description: WorkspacePropertyData['description']) { + this._data.update({description: description}); + } + public setValue(value: WorkspacePropertyData['value']) { + + if(value === this._data.getValue().value) return; + + this._data.update({value: value}); + + const alias = this._data.getValue().alias; + if(alias) { + this._workspaceContext?.setPropertyValue(alias, value); + } + } + public setConfig(config: WorkspacePropertyData['config']) { + this._data.update({config: config}); + } + + public resetValue() { + this.setValue(null);// TODO: Consider if this can be configured/provided from Property Editor or DataType Configuration or even locally specified in DocumentType. + } + + // TODO: how can we make sure to call this. + public destroy(): void { + this._data.unsubscribe(); + this._providerController.destroy(); // This would also be handled by the controller host, but if someone wanted to replace/remove this context without the host being destroyed. Then we have clean up out selfs here. + } + +} + diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-property/entity-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts similarity index 59% rename from src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-property/entity-property.element.ts rename to src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts index 5817a831d4..16cfea9da7 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-property/entity-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts @@ -1,10 +1,11 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; -import { css, html, PropertyValueMap } from 'lit'; +import { css, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; +import { ifDefined } from 'lit-html/directives/if-defined.js'; import { UmbWorkspacePropertyContext } from './workspace-property.context'; import { createExtensionElement } from '@umbraco-cms/extensions-api'; import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; -import type { ManifestPropertyEditorUI, ManifestTypes } from '@umbraco-cms/models'; +import type { DataTypePropertyData, ManifestPropertyEditorUI, ManifestTypes } from '@umbraco-cms/models'; import '../../property-actions/shared/property-action-menu/property-action-menu.element'; import 'src/backoffice/shared/components/workspace/workspace-property-layout/workspace-property-layout.element'; @@ -12,14 +13,13 @@ import { UmbObserverController } from 'src/core/observable-api/observer.controll import { UmbLitElement } from '@umbraco-cms/element'; /** - * @element umb-entity-property + * @element umb-workspace-property * @description - Component for displaying a entity property. The Element will render a Property Editor based on the Property Editor UI alias passed to the element. * The element will also render all Property Actions related to the Property Editor. */ -// TODO: get rid of the other mixins: -@customElement('umb-entity-property') -export class UmbEntityPropertyElement extends UmbLitElement { +@customElement('umb-workspace-property') +export class UmbWorkspacePropertyElement extends UmbLitElement { static styles = [ UUITextStyles, css` @@ -48,6 +48,12 @@ export class UmbEntityPropertyElement extends UmbLitElement { `, ]; + @state() + private _label?:string; + + @state() + private _description?:string; + /** * Label. Name of the property * @type {string} @@ -55,7 +61,9 @@ export class UmbEntityPropertyElement extends UmbLitElement { * @default '' */ @property({ type: String }) - public label = ''; + public set label(label: string) { + this._propertyContext.setLabel(label); + } /** * Description: render a description underneath the label. @@ -64,7 +72,9 @@ export class UmbEntityPropertyElement extends UmbLitElement { * @default '' */ @property({ type: String }) - public description = ''; + public set description(description: string) { + this._propertyContext.setDescription(description); + } /** * Alias @@ -74,7 +84,9 @@ export class UmbEntityPropertyElement extends UmbLitElement { * @default '' */ @property({ type: String }) - public alias = ''; + public set alias(alias: string) { + this._propertyContext.setAlias(alias); + } /** * Property Editor UI Alias. Render the Property Editor UI registered for this alias. @@ -85,10 +97,8 @@ export class UmbEntityPropertyElement extends UmbLitElement { */ private _propertyEditorUIAlias = ''; @property({ type: String, attribute: 'property-editor-ui-alias' }) - public get propertyEditorUIAlias(): string { - return this._propertyEditorUIAlias; - } public set propertyEditorUIAlias(value: string) { + if(this._propertyEditorUIAlias === value) return; this._propertyEditorUIAlias = value; this._observePropertyEditorUI(); } @@ -96,12 +106,14 @@ export class UmbEntityPropertyElement extends UmbLitElement { /** * Property Editor UI Alias. Render the Property Editor UI registered for this alias. * @public - * @type {string} + * @type {unknown} * @attr - * @default '' + * @default undefined */ - @property({ type: Object, attribute: false }) - public value?: any; + @property({attribute: false }) + public set value(value: unknown) { + this._propertyContext.setValue(value); + } /** * Config. Configuration to pass to the Property Editor UI. This is also the configuration data stored on the Data Type. @@ -111,82 +123,93 @@ export class UmbEntityPropertyElement extends UmbLitElement { * @default '' */ @property({ type: Object, attribute: false }) - public config?: any; + public set config(value: DataTypePropertyData[]) { + this._propertyContext.setConfig(value); + } // TODO: make interface for UMBPropertyEditorElement @state() private _element?: { value?: any; config?: any } & HTMLElement; // TODO: invent interface for propertyEditorUI. - // TODO: How to get proper default value? - private _propertyContext = new UmbWorkspacePropertyContext(""); + private _propertyContext = new UmbWorkspacePropertyContext(this); private propertyEditorUIObserver?: UmbObserverController; + private _valueObserver?: UmbObserverController; + private _configObserver?: UmbObserverController; + constructor() { super(); - this.provideContext('umbPropertyContext', this._propertyContext); - - this._observePropertyEditorUI(); - this.addEventListener('property-editor-change', this._onPropertyEditorChange as any as EventListener); + this.observe(this._propertyContext.label, (label) => { + this._label = label; + }); + this.observe(this._propertyContext.label, (description) => { + this._description = description; + }); + } + private _onPropertyEditorChange = (e: CustomEvent) => { + const target = e.composedPath()[0] as any; + + this.value = target.value;// Sets value in context. + e.stopPropagation(); + }; + private _observePropertyEditorUI() { this.propertyEditorUIObserver?.destroy(); - this.propertyEditorUIObserver = this.observe(umbExtensionsRegistry.getByTypeAndAlias('propertyEditorUI', this.propertyEditorUIAlias), (manifest) => { - this._gotEditor(manifest); + this.propertyEditorUIObserver = this.observe(umbExtensionsRegistry.getByTypeAndAlias('propertyEditorUI', this._propertyEditorUIAlias), (manifest) => { + this._gotEditorUI(manifest); }); } - private _gotEditor(propertyEditorUIManifest?: ManifestPropertyEditorUI | null) { - if (!propertyEditorUIManifest) { - // TODO: if dataTypeKey didn't exist in store, we should do some nice UI. + private _gotEditorUI(manifest?: ManifestPropertyEditorUI | null) { + if (!manifest) { + // TODO: if propertyEditorUIAlias didn't exist in store, we should do some nice fail UI. return; } - createExtensionElement(propertyEditorUIManifest) + createExtensionElement(manifest) .then((el) => { const oldValue = this._element; + + oldValue?.removeEventListener('change', this._onPropertyEditorChange as any as EventListener); + this._element = el; - if (this._element) { - this._element.value = this.value; // Be aware its duplicated code - this._element.config = this.config; // Be aware its duplicated code + this._valueObserver?.destroy(); + this._configObserver?.destroy(); + + if(this._element) { + this._element.addEventListener('change', this._onPropertyEditorChange as any as EventListener); + + this._valueObserver = this.observe(this._propertyContext.value, (value) => { + if(this._element) { + this._element.value = value; + } + }); + this._configObserver = this.observe(this._propertyContext.config, (config) => { + if(this._element) { + this._element.config = config; + } + }); } + this.requestUpdate('element', oldValue); + }) .catch(() => { // TODO: loading JS failed so we should do some nice UI. (This does only happen if extension has a js prop, otherwise we concluded that no source was needed resolved the load.) }); } - private _onPropertyEditorChange = (e: CustomEvent) => { - const target = e.composedPath()[0] as any; - this.value = target.value; - this.dispatchEvent(new CustomEvent('property-value-change', { bubbles: true, composed: true })); - e.stopPropagation(); - }; - - /** Lit does not currently handle dynamic tag names, therefor we are doing some manual rendering */ - // TODO: Refactor into a base class for dynamic-tag element? we will be using this a lot for extensions. - // This could potentially hook into Lit and parse all properties defined in the specific class on to the dynamic-element. (see static elementProperties: PropertyDeclarationMap;) - willUpdate(changedProperties: PropertyValueMap | Map) { - super.willUpdate(changedProperties); - - if (changedProperties.has('value') && this._element) { - this._element.value = this.value; // Be aware its duplicated code - } - - if (changedProperties.has('config') && this._element) { - this._element.config = this.config; // Be aware its duplicated code - } - } render() { return html` - + ${this._renderPropertyActionMenu()}
${this._element}
@@ -194,11 +217,11 @@ export class UmbEntityPropertyElement extends UmbLitElement { } private _renderPropertyActionMenu() { - return html`${this.propertyEditorUIAlias + return html`${this._propertyEditorUIAlias ? html`` : ''}`; } @@ -206,6 +229,6 @@ export class UmbEntityPropertyElement extends UmbLitElement { declare global { interface HTMLElementTagNameMap { - 'umb-entity-property': UmbEntityPropertyElement; + 'umb-workspace-property': UmbWorkspacePropertyElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.stories.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.stories.ts new file mode 100644 index 0000000000..9897607990 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.stories.ts @@ -0,0 +1,20 @@ +import { Meta, Story } from '@storybook/web-components'; +import { html } from 'lit-html'; + +import type { UmbWorkspacePropertyElement } from './workspace-property.element'; +import './workspace-property.element'; + +export default { + title: 'Components/Entity Property', + component: 'umb-workspace-property', + id: 'umb-workspace-property', +} as Meta; + +export const AAAOverview: Story = () => + html` `; +AAAOverview.storyName = 'Overview'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/edit/workspace-view-content-edit.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/edit/workspace-view-content-edit.element.ts index c45c5074b1..e004fa983a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/edit/workspace-view-content-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/edit/workspace-view-content-edit.element.ts @@ -2,6 +2,7 @@ import { css, html } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, state } from 'lit/decorators.js'; import { distinctUntilChanged } from 'rxjs'; +import { repeat } from 'lit/directives/repeat.js'; import type { UmbWorkspaceContentContext } from '../../workspace-content.context'; import type { ContentProperty, ContentPropertyData, DocumentDetails, MediaDetails } from '@umbraco-cms/models'; @@ -40,11 +41,22 @@ export class UmbWorkspaceViewContentEditElement extends UmbLitElement { private _observeContent() { if (!this._workspaceContext) return; + /* + TODO: Property-Context: This observer gets all changes, We need to fix this. it should be simpler. + It should look at length and aliases? as long as they are identical nothing should change. + As they would update them selfs? + + Should use a Observable for this._workspaceContext.properties + */ this.observe( this._workspaceContext.data.pipe(distinctUntilChanged()), (content) => { this._properties = content.properties; this._data = content.data; + /* + Maybe we should not give the value, but the umb-content-property should get the context and observe its own data. + This would become a more specific Observer therefor better performance?.. Note to self: Debate with Mads how he sees this perspective. + */ } ); } @@ -52,12 +64,14 @@ export class UmbWorkspaceViewContentEditElement extends UmbLitElement { render() { return html` - ${this._properties?.map( - (property: ContentProperty) => html` - property.alias, + (property) => + html` data.alias === property.alias)?.value}> - ` + ` )} `; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts index 9d491762c9..0eac7ff837 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts @@ -1,20 +1,27 @@ import { v4 as uuidv4 } from 'uuid'; +import { BehaviorSubject, Observable } from 'rxjs'; import { UmbNotificationService } from '../../../../../core/notification'; import { UmbNotificationDefaultData } from '../../../../../core/notification/layouts/default'; -import { UmbWorkspaceContext } from '../workspace-context/workspace.context'; import { UmbNodeStoreBase } from '@umbraco-cms/stores/store'; -import { ContentTreeItem } from '@umbraco-cms/backend-api'; import { UmbControllerHostInterface } from 'src/core/controller/controller-host.mixin'; import { UmbContextConsumerController } from 'src/core/context-api/consume/context-consumer.controller'; import { UmbObserverController } from '@umbraco-cms/observable-api'; import { UmbContextProviderController } from 'src/core/context-api/provide/context-provider.controller'; +import { EntityTreeItem } from '@umbraco-cms/backend-api'; // TODO: Consider if its right to have this many class-inheritance of WorkspaceContext // TODO: Could we extract this code into a 'Manager' of its own, which will be instantiated by the concrete Workspace Context. This will be more transparent and 'reuseable' -export class UmbWorkspaceContentContext< - ContentTypeType extends ContentTreeItem = ContentTreeItem, +export abstract class UmbWorkspaceContentContext< + ContentTypeType extends EntityTreeItem = EntityTreeItem, StoreType extends UmbNodeStoreBase = UmbNodeStoreBase -> extends UmbWorkspaceContext { +> { + + protected _host: UmbControllerHostInterface; + + // TODO: figure out how fine grained we want to make our observables. + // TODO: add interface + protected _data!:BehaviorSubject; + public readonly data: Observable; protected _notificationService?: UmbNotificationService; @@ -32,7 +39,14 @@ export class UmbWorkspaceContentContext< storeAlias: string, entityType: string ) { - super(host, defaultData); + + this._host = host; + + //TODO: Use the UniqueBehaviorSubject, and separate observables for each part? + + this._data = new BehaviorSubject(defaultData); + this.data = this._data.asObservable(); + this.entityType = entityType; new UmbContextConsumerController( host, @@ -42,25 +56,31 @@ export class UmbWorkspaceContentContext< } ); - this.entityType = entityType; - new UmbContextConsumerController(host, storeAlias, (_instance: StoreType) => { this._store = _instance; if (!this._store) { // TODO: make sure to break the application in a good way. return; } - this._readyToLoad(); + this._observeStore(); // TODO: first provide when we have umbNotificationService as well. new UmbContextProviderController(this._host, 'umbWorkspaceContext', this); }); } + + public getData() { + return this._data.getValue(); + } + public update(data: Partial) { + this._data.next({ ...this.getData(), ...data }); + } + load(entityKey: string) { this.#isNew = false; this.entityKey = entityKey; - this._readyToLoad(); + this._observeStore(); } create(parentKey: string | null) { @@ -69,14 +89,14 @@ export class UmbWorkspaceContentContext< console.log("I'm new, and I will be created under ", parentKey) } - protected _readyToLoad(): void { + protected _observeStore(): void { if(!this._store || !this.entityKey) { return; } if(!this.#isNew) { this._storeSubscription?.destroy(); - this._storeSubscription = new UmbObserverController(this._host, this._store.getByKey(this.entityKey), + this._storeSubscription = new UmbObserverController(this._host, this._store.getByKey(this.entityKey), (content) => { if (!content) return; // TODO: Handle nicely if there is no content data. this.update(content as any); @@ -88,6 +108,9 @@ export class UmbWorkspaceContentContext< return this._store; } + abstract setPropertyValue(alias: string, value: unknown):void; + + public save(): Promise { if(!this._store) { // TODO: more beautiful error: @@ -104,4 +127,11 @@ export class UmbWorkspaceContentContext< this._notificationService?.peek('danger', { data }); }); } -} \ No newline at end of file + + + + // TODO: how can we make sure to call this. + public destroy(): void { + this._data.unsubscribe(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.element.ts index 25294a57b5..0593b4ae60 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.element.ts @@ -1,9 +1,6 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html } from 'lit'; -import { customElement, property, state } from 'lit/decorators.js'; -import { distinctUntilChanged } from 'rxjs'; -import type { UmbWorkspaceContentContext } from './workspace-content.context'; -import type { DocumentDetails, MediaDetails } from '@umbraco-cms/models'; +import { customElement, property } from 'lit/decorators.js'; import '../workspace-layout/workspace-layout.element'; import '../../variant-selector/variant-selector.element'; @@ -12,11 +9,8 @@ import '../../variant-selector/variant-selector.element'; // TODO: Make this dynamic, use load-extensions method to loop over extensions for this node. import './views/edit/workspace-view-content-edit.element'; import './views/info/workspace-view-content-info.element'; -import type { UmbNodeStoreBase } from '@umbraco-cms/stores/store'; import { UmbLitElement } from '@umbraco-cms/element'; -type ContentTypeTypes = DocumentDetails | MediaDetails; - /** * TODO: IMPORTANT TODO: Get rid of the content workspace. Instead we aim to get separate components that can be composed by each workspace. * Example. Document Workspace would use a Variant-component(variant component would talk directly to the workspace-context) @@ -49,52 +43,6 @@ export class UmbWorkspaceContentElement extends UmbLitElement { @property() alias!: string; - // TODO: use a NodeDetails type here: - @state() - _content?: ContentTypeTypes; - - private _workspaceContext?: UmbWorkspaceContentContext>; - - constructor() { - super(); - - this.consumeContext('umbWorkspaceContext', (instance) => { - this._workspaceContext = instance; - this._observeWorkspace(); - }); - - this.addEventListener('property-value-change', this._onPropertyValueChange); - } - - private async _observeWorkspace() { - if (!this._workspaceContext) return; - - this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (data) => { - this._content = data; - }); - } - - private _onPropertyValueChange = (e: Event) => { - const target = e.composedPath()[0] as any; - - // TODO: Set value. - const property = this._content?.properties.find((x) => x.alias === target.alias); - if (property) { - this._setPropertyValue(property.alias, target.value); - } else { - console.error('property was not found', target.alias); - } - }; - - // TODO: How do we ensure this is a change of this document and not nested documents? Should the event be stopped at this spot at avoid such. - private _setPropertyValue(alias: string, value: unknown) { - this._content?.data.forEach((data) => { - if (data.alias === alias) { - data.value = value; - } - }); - } - render() { return html` diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/notes-from-structure-talk.txt b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/notes-from-structure-talk.txt deleted file mode 100644 index ff458e753e..0000000000 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/notes-from-structure-talk.txt +++ /dev/null @@ -1,131 +0,0 @@ - -CollectionContext { - - // RxJS store.. - this._data = new BehaviorSubject(defaultData); - this.data = this._data.asObservable(); - - getStore(); - -} - - -DocumentCollectionContext extends CollectionContext { - - // RxJS store.. - this._data = new BehaviorSubject(defaultData); - this.data = this._data.asObservable(); - - getStore(); - publish(); - update(); - -} - - -MediaCollectionContext extends CollectionContext { - - // RxJS store.. - this._data = new BehaviorSubject(defaultData); - this.data = this._data.asObservable(); - - getStore(); - update(); - save(); - -} - - -VendrCollectionContext extends CollectionContext { - - // RxJS store.. - this._data = new BehaviorSubject(defaultData); - this.data = this._data.asObservable(); - - getStore(); - update(); - save(); - -} - - - - - - - -DocumentContext { - - // Validation? - - // RxJS store.. - this._data = new BehaviorSubject(defaultData); - this.data = this._data.asObservable(); - - getStore(); - - setPublishDate() - - addVariant(name) { - this.data.name = name; - } - - save() { - this.backendStore.save() - } - -} - - -PropertyContext { - - // Validation? - -} - - -BlockContext { - - // Validation? - - this._data = new BehaviorSubject(defaultData); - this.data = this._data.asObservable(); - - this._liveEditing = true; - - setName(name) { - this.update({name: name}) - if(this._liveEditing) { - this.save(); - } - } - - save() { - // - this.parentData.block[123] = this.data.getData(); - this.parentDatarxJS.update(this.parentData); - } - -} - - -var myBlockContext = new BlockContext(documentData.blocks[1]); - - -// Property Editor Edit Element Name: - -myBlockContext.data.subscribe((blockData) => { - this.input.value = blockData.name; -}) -this.input.addEventListener("change", () => { - myBlockContext.setName(this.input.value); - // RXJS update?? ^^ -}) - - - - -blockContext.setName('sdaafgdss'); -// Does does other update? - - diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace.context.ts deleted file mode 100644 index 1277e47ace..0000000000 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace.context.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { BehaviorSubject, Observable } from "rxjs"; -import { UmbControllerHostInterface } from "src/core/controller/controller-host.mixin"; - - -export abstract class UmbWorkspaceContext { - - protected _host: UmbControllerHostInterface; - - // TODO: figure out how fine grained we want to make our observables. - // TODO: add interface - protected _data!:BehaviorSubject; - public readonly data: Observable; - - - constructor(host:UmbControllerHostInterface, defaultData: DataType) { - this._host = host; - - this._data = new BehaviorSubject(defaultData); - this.data = this._data.asObservable(); - } - - - public getData() { - return this._data.getValue(); - } - - public update(data: Partial) { - this._data.next({ ...this.getData(), ...data }); - } - - - - - // TODO: how can we make sure to call this. - public destroy(): void { - this._data.unsubscribe(); - } - -} - diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/clear/property-action-clear.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/clear/property-action-clear.element.ts index 5371832894..6df5a91cc9 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/clear/property-action-clear.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/clear/property-action-clear.element.ts @@ -1,35 +1,46 @@ import { html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; -import { UmbPropertyActionMenuContext } from '../shared/property-action-menu/property-action-menu.context'; +//import type { UmbPropertyActionMenuContext } from '../shared/property-action-menu/property-action-menu.context'; import { UmbPropertyAction } from '../shared/property-action/property-action.model'; +import type { UmbWorkspacePropertyContext } from '../../components/workspace-property/workspace-property.context'; import { UmbLitElement } from '@umbraco-cms/element'; @customElement('umb-property-action-clear') export class UmbPropertyActionClearElement extends UmbLitElement implements UmbPropertyAction { + @property() value = ''; - private _propertyActionMenuContext?: UmbPropertyActionMenuContext; + // THESE OUT COMMENTED CODE IS USED FOR THE EXAMPLE BELOW, TODO: Should be transferred to some documentation. + //private _propertyActionMenuContext?: UmbPropertyActionMenuContext; + private _propertyContext?: UmbWorkspacePropertyContext; constructor() { super(); + /* this.consumeContext('umbPropertyActionMenu', (propertyActionsContext: UmbPropertyActionMenuContext) => { this._propertyActionMenuContext = propertyActionsContext; }); + */ + this.consumeContext('umbPropertyContext', (propertyContext: UmbWorkspacePropertyContext) => { + this._propertyContext = propertyContext; + }); } private _handleLabelClick() { this._clearValue(); - // TODO: how do we want to close the menu? Testing an event based approach and context api approach - // this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true })); - this._propertyActionMenuContext?.close(); + this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true })); + // Or you can do this: + //this._propertyActionMenuContext?.close(); } private _clearValue() { // TODO: how do we want to update the value? Testing an event based approach. We need to test an api based approach too. - this.value = ''; - this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true })); + //this.value = '';// This is though bad as it assumes we are dealing with a string. So wouldn't work as a generalized element. + //this.dispatchEvent(new CustomEvent('property-value-change')); + // Or you can do this: + this._propertyContext?.resetValue();// This resets value to what the property wants. } render() { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.context.ts index 1face67dbd..4cd8caa24a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.context.ts @@ -1,13 +1,22 @@ -import { Observable, ReplaySubject } from 'rxjs'; +import { BehaviorSubject } from 'rxjs'; +import { UmbContextProviderController } from 'src/core/context-api/provide/context-provider.controller'; +import type { UmbControllerHostInterface } from 'src/core/controller/controller-host.mixin'; export class UmbPropertyActionMenuContext { - private _isOpen: ReplaySubject = new ReplaySubject(1); - public readonly isOpen: Observable = this._isOpen.asObservable(); + private _isOpen = new BehaviorSubject(false); + public readonly isOpen = this._isOpen.asObservable(); + + constructor(host: UmbControllerHostInterface) { + new UmbContextProviderController(host, 'umbPropertyActionMenu', this); + } + + toggle() { + this._isOpen.next(!this._isOpen.getValue()); + } open() { this._isOpen.next(true); } - close() { this._isOpen.next(false); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.element.ts index ef4caef8e7..6898826005 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.element.ts @@ -8,6 +8,7 @@ import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; import '../property-action/property-action.element'; import { UmbLitElement } from '@umbraco-cms/element'; +import { UmbObserverController } from '@umbraco-cms/observable-api'; @customElement('umb-property-action-menu') export class UmbPropertyActionMenuElement extends UmbLitElement { @@ -40,87 +41,91 @@ export class UmbPropertyActionMenuElement extends UmbLitElement { `, ]; - @property() - public propertyEditorUIAlias = ''; + // TODO: we need to investigate context api vs values props and events @property() public value?: string; + @property() + set propertyEditorUIAlias(alias: string) { + this._observeActions(alias); + } + + private _actionsObserver?: UmbObserverController; + @state() private _actions: Array = []; @state() private _open = false; - private _propertyActionMenuContext = new UmbPropertyActionMenuContext(); + private _propertyActionMenuContext = new UmbPropertyActionMenuContext(this); - connectedCallback(): void { - super.connectedCallback(); + constructor() { + super(); - this._observePropertyActions(); - this._observePropertyActionMenuOpenState(); + this.observe(this._propertyActionMenuContext.isOpen, (value) => { + this._open = value; + }); - this.provideContext('umbPropertyActionMenu', this._propertyActionMenuContext); + this.addEventListener('close', (e) => { + this._propertyActionMenuContext.close(); + e.stopPropagation(); + }); } - private _observePropertyActions() { - this.observe( + private _observeActions(alias: string) { + this._actionsObserver?.destroy(); + this._actionsObserver = this.observe( umbExtensionsRegistry .extensionsOfType('propertyAction') .pipe( - map((propertyActions) => - propertyActions.filter((propertyAction) => - propertyAction.meta.propertyEditors.includes(this.propertyEditorUIAlias) + map((propertyActions) => { + return propertyActions.filter((propertyAction) => + propertyAction.meta.propertyEditors.includes(alias) ) + } ) ), - (propertyActionManifests) => { - this._actions = propertyActionManifests; + (manifests) => { + this._actions = manifests; } ); } - private _observePropertyActionMenuOpenState() { - this.observe(this._propertyActionMenuContext.isOpen, (value) => { - this._open = value; - }); - } - private _toggleMenu() { - this._open ? this._propertyActionMenuContext.close() : this._propertyActionMenuContext.open(); + this._propertyActionMenuContext.toggle(); } private _handleClose(event: CustomEvent) { - this._open = false; + this._propertyActionMenuContext.close(); event.stopPropagation(); } render() { - return html` - ${this._actions.length > 0 - ? html` - - - - + return (this._actions.length > 0) ? + html` + + + + - - - ` - : ''} - `; + + + ` + : ''; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/shared/property-editor-config/property-editor-config.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/shared/property-editor-config/property-editor-config.element.ts index f6dc453062..216e33b789 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/shared/property-editor-config/property-editor-config.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/shared/property-editor-config/property-editor-config.element.ts @@ -5,7 +5,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import type { PropertyEditorConfigDefaultData, PropertyEditorConfigProperty } from '@umbraco-cms/models'; import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; -import '../../../components/entity-property/entity-property.element'; +import '../../../components/workspace-property/workspace-property.element'; import { UmbLitElement } from '@umbraco-cms/element'; /** @@ -104,12 +104,12 @@ export class UmbPropertyEditorConfigElement extends UmbLitElement { ? html` ${this._properties?.map( (property) => html` - + .value=${this._getValue(property)}> ` )} ` diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/content-picker/property-editor-ui-content-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/content-picker/property-editor-ui-content-picker.element.ts index 7e0f41cf04..e3d5dfa1c4 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/content-picker/property-editor-ui-content-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/content-picker/property-editor-ui-content-picker.element.ts @@ -90,7 +90,7 @@ export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement { private _setValue(newValue: Array) { this.value = newValue; this._observePickedDocuments(); - this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('property-value-change')); } private _renderItem(item: FolderTreeItem) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/number/property-editor-ui-number.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/number/property-editor-ui-number.element.ts index 8461d2c513..7c704fbc8c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/number/property-editor-ui-number.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/number/property-editor-ui-number.element.ts @@ -21,7 +21,7 @@ export class UmbPropertyEditorUINumberElement extends LitElement { private onInput(e: InputEvent) { this.value = (e.target as HTMLInputElement).value; - this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('property-value-change')); } render() { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/text-box/property-editor-ui-text-box.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/text-box/property-editor-ui-text-box.element.ts index 4ea729ad37..302a353da3 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/text-box/property-editor-ui-text-box.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/text-box/property-editor-ui-text-box.element.ts @@ -21,7 +21,7 @@ export class UmbPropertyEditorUITextBoxElement extends LitElement { private onInput(e: InputEvent) { this.value = (e.target as HTMLInputElement).value; - this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('property-value-change')); } render() { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/textarea/property-editor-ui-textarea.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/textarea/property-editor-ui-textarea.element.ts index 2378ec6e56..df1980458f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/textarea/property-editor-ui-textarea.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/textarea/property-editor-ui-textarea.element.ts @@ -1,8 +1,9 @@ import { css, html } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property } from 'lit/decorators.js'; -import { UmbWorkspacePropertyContext } from 'src/backoffice/shared/components/entity-property/workspace-property.context'; +import type { UmbWorkspacePropertyContext } from 'src/backoffice/shared/components/workspace-property/workspace-property.context'; import { UmbLitElement } from '@umbraco-cms/element'; +import { UUITextareaElement } from '@umbraco-ui/uui'; @customElement('umb-property-editor-ui-textarea') export class UmbPropertyEditorUITextareaElement extends UmbLitElement { @@ -26,21 +27,19 @@ export class UmbPropertyEditorUITextareaElement extends UmbLitElement { constructor() { super(); - this.consumeContext('umbPropertyContext', (instance) => { + this.consumeContext('umbPropertyContext', (instance: UmbWorkspacePropertyContext) => { this.propertyContext = instance; }); } private onInput(e: InputEvent) { - this.value = (e.target as HTMLInputElement).value; - this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true })); + this.value = (e.target as UUITextareaElement).value as string; + this.dispatchEvent(new CustomEvent('property-value-change')); } render() { return html` - - ${this.config?.map((property: any) => html`
${property.alias}: ${property.value}
`)} - `; + `; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.context.ts index beea232e4b..067c239697 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.context.ts @@ -21,4 +21,9 @@ export class UmbWorkspaceUserGroupContext extends UmbWorkspaceContentContext< constructor(host: UmbControllerHostInterface) { super(host, DefaultDataTypeData, 'umbUserStore', 'userGroup'); } + + + public setPropertyValue(alias: string, value: unknown) { + throw new Error("setPropertyValue is not implemented for UmbWorkspaceUserGroupContext") + } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.context.ts index 2016e0609b..5e59f0246c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.context.ts @@ -24,4 +24,8 @@ export class UmbWorkspaceUserContext extends UmbWorkspaceContentContext = [ parentKey: null, isFolder: false, propertyEditorModelAlias: 'Umbraco.TextArea', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.Textarea', + propertyEditorUIAlias: 'Umb.PropertyEditorUI.TextArea', data: [ { alias: 'maxChars', diff --git a/src/Umbraco.Web.UI.Client/src/core/models/index.ts b/src/Umbraco.Web.UI.Client/src/core/models/index.ts index 72c3a34458..6fe815ed33 100644 --- a/src/Umbraco.Web.UI.Client/src/core/models/index.ts +++ b/src/Umbraco.Web.UI.Client/src/core/models/index.ts @@ -24,6 +24,13 @@ export interface Entity { parentKey: string | null; } +export interface ContentDetails extends ContentTreeItem { + isTrashed: boolean; // TODO: remove only temp part of refactor + properties: Array; + //data: Array; + //layout?: any; // TODO: define layout type - make it non-optional +} + export interface UserEntity extends Entity { type: 'user'; } diff --git a/src/Umbraco.Web.UI.Client/src/core/observable-api/unique-behavior-subject.ts b/src/Umbraco.Web.UI.Client/src/core/observable-api/unique-behavior-subject.ts new file mode 100644 index 0000000000..c3a763b325 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/observable-api/unique-behavior-subject.ts @@ -0,0 +1,88 @@ +import { BehaviorSubject, distinctUntilChanged, map, Observable, shareReplay } from "rxjs"; + + +function deepFreeze(inObj: T): T { + Object.freeze(inObj); + + Object.getOwnPropertyNames(inObj).forEach(function (prop) { + // eslint-disable-next-line no-prototype-builtins + if ((inObj as any).hasOwnProperty(prop) + && (inObj as any)[prop] != null + && typeof (inObj as any)[prop] === 'object' + && !Object.isFrozen((inObj as any)[prop])) { + deepFreeze((inObj as any)[prop]); + } + }); + return inObj; +} + + +export function naiveObjectComparison(objOne: any, objTwo: any): boolean { + return JSON.stringify(objOne) === JSON.stringify(objTwo); +} + + + + +type MappingFunction = (mappable: T) => R; +type MemoizationFunction = (previousResult: R, currentResult: R) => boolean; + +function defaultMemoization(previousValue: any, currentValue: any): boolean { + if (typeof previousValue === 'object' && typeof currentValue === 'object') { + return naiveObjectComparison(previousValue, currentValue); + } + return previousValue === currentValue; +} + +/** + * @export + * @method CreateObservablePart + * @param {Observable} source - RxJS Subject to use for this Observable. + * @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. + * @description - Creates a RxJS Observable from RxJS Subject. + * @example Example create a Observable for part of the data Subject. + * public readonly myPart = CreateObservablePart(this._data, (data) => data.myPart); + */ +export function CreateObservablePart ( + source$: Observable, + mappingFunction: MappingFunction, + memoizationFunction?: MemoizationFunction + ): Observable { + return source$.pipe( + map(mappingFunction), + distinctUntilChanged(memoizationFunction || defaultMemoization), + shareReplay(1) + ) +} + + +/** + * @export + * @class UniqueBehaviorSubject + * @extends {BehaviorSubject} + * @description - A RxJS BehaviorSubject which deepFreezes the data to ensure its not manipulated from any implementations. + * Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content. + */ +export class UniqueBehaviorSubject extends BehaviorSubject { + constructor(initialData: T) { + super(deepFreeze(initialData)); + } + + next(newData: T): void { + const frozenData = deepFreeze(newData); + // Only update data if its different than current data. + if (!naiveObjectComparison(frozenData, this.getValue())) { + super.next(frozenData); + } + } + + /** + * Partial update data set, only works for Objects. + * TODO: consider moving this into a specific class for Objects? + * Consider doing similar for Array? + */ + update(data: Partial) { + this.next({ ...this.getValue(), ...data }); + } +}