diff --git a/src/Umbraco.Web.UI.Client/devops/eslint/rules/no-direct-api-import.cjs b/src/Umbraco.Web.UI.Client/devops/eslint/rules/no-direct-api-import.cjs index b051ad64fa..9f3a3912dc 100644 --- a/src/Umbraco.Web.UI.Client/devops/eslint/rules/no-direct-api-import.cjs +++ b/src/Umbraco.Web.UI.Client/devops/eslint/rules/no-direct-api-import.cjs @@ -1,3 +1,4 @@ +/** @type {import('eslint').Rule.RuleModule} */ module.exports = { meta: { docs: { diff --git a/src/Umbraco.Web.UI.Client/eslint.config.js b/src/Umbraco.Web.UI.Client/eslint.config.js index 3edaa890a5..6206318d6e 100644 --- a/src/Umbraco.Web.UI.Client/eslint.config.js +++ b/src/Umbraco.Web.UI.Client/eslint.config.js @@ -4,8 +4,6 @@ import importPlugin from 'eslint-plugin-import'; import localRules from 'eslint-plugin-local-rules'; import wcPlugin from 'eslint-plugin-wc'; import litPlugin from 'eslint-plugin-lit'; -import litA11yPlugin from 'eslint-plugin-lit-a11y'; -import storybookPlugin from 'eslint-plugin-storybook'; import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; import tseslint from 'typescript-eslint'; @@ -13,16 +11,21 @@ export default [ // Recommended config applied to all files js.configs.recommended, ...tseslint.configs.recommended, + wcPlugin.configs['flat/recommended'], + litPlugin.configs['flat/recommended'], + localRules.configs.all, eslintPluginPrettierRecommended, // Global ignores { ignores: [ + '**/eslint.config.js', '**/rollup.config.js', '**/vite.config.ts', 'src/external', 'src/packages/core/icon-registry/icons', 'src/packages/core/icon-registry/icons.ts', + 'src/**/*.test.ts', ], }, @@ -40,28 +43,19 @@ export default [ plugins: { import: importPlugin, 'local-rules': localRules, - wc: wcPlugin, - lit: litPlugin, - 'lit-a11y': litA11yPlugin, - storybook: storybookPlugin, }, rules: { semi: ['warn', 'always'], + "prettier/prettier": ["warn", { "endOfLine": "auto" }], 'no-unused-vars': 'off', //Let '@typescript-eslint/no-unused-vars' catch the errors to allow unused function parameters (ex: in interfaces) 'no-var': 'error', + ...importPlugin.configs.recommended.rules, + 'import/namespace': 'off', 'import/no-unresolved': 'off', 'import/order': ['warn', { groups: ['builtin', 'parent', 'sibling', 'index', 'external'] }], 'import/no-self-import': 'error', 'import/no-cycle': ['error', { maxDepth: 6, allowUnsafeDynamicCyclicDependency: true }], - 'local-rules/bad-type-import': 'error', - 'local-rules/enforce-element-suffix-on-element-class-name': 'error', - 'local-rules/enforce-umb-prefix-on-element-name': 'error', - 'local-rules/ensure-relative-import-use-js-extension': 'error', - 'local-rules/no-direct-api-import': 'warn', - 'local-rules/prefer-import-aliases': 'error', 'local-rules/prefer-static-styles-last': 'warn', - 'local-rules/umb-class-prefix': 'error', - 'local-rules/no-relative-import-to-import-map-module': 'error', 'local-rules/enforce-umbraco-external-imports': [ 'error', { diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 0eb3414ca9..277cef9094 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "@umbraco-cms/backoffice", "version": "14.2.0", + "hasInstallScript": true, "license": "MIT", "workspaces": [ "./src/packages/block", @@ -16,15 +17,14 @@ "./src/packages/documents", "./src/packages/health-check", "./src/packages/language", - "./src/packages/tags", - "./src/packages/umbraco-news", - "./src/packages/webhook", - "./src/packages/health-check", + "./src/packages/media", + "./src/packages/multi-url-picker", + "./src/packages/property-editors", "./src/packages/tags", "./src/packages/templating", - "./src/packages/property-editors", - "./src/packages/media", - "./src/packages/user" + "./src/packages/umbraco-news", + "./src/packages/user", + "./src/packages/webhook" ], "dependencies": { "@types/diff": "^5.2.1", @@ -75,14 +75,12 @@ "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-lit": "^1.14.0", - "eslint-plugin-lit-a11y": "^4.1.3", "eslint-plugin-local-rules": "^3.0.2", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-storybook": "^0.8.0", "eslint-plugin-wc": "^2.1.0", "glob": "^10.3.10", "globals": "^15.7.0", - "lucide-static": "^0.379.0", + "lucide-static": "^0.407.0", "msw": "^1.3.2", "playwright-msw": "^3.0.1", "prettier": "3.3.2", @@ -93,7 +91,7 @@ "rollup-plugin-esbuild": "^6.1.1", "rollup-plugin-import-css": "^3.5.0", "rollup-plugin-web-worker-loader": "^1.6.1", - "simple-icons": "^13.0.0", + "simple-icons": "^13.1.0", "storybook": "^7.6.17", "tiny-glob": "^0.2.9", "tsc-alias": "^1.8.8", @@ -2696,9 +2694,9 @@ "dev": true }, "node_modules/@hey-api/openapi-ts": { - "version": "0.48.1", - "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.48.1.tgz", - "integrity": "sha512-iZBEmS12EWn4yl/nYMui+PA3hprjFR9z+9p+p+s3f0VRXPw+uZWO0yuIfCcsAw1n0isikw2uJEY4qxwlnI07AQ==", + "version": "0.48.2", + "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.48.2.tgz", + "integrity": "sha512-pjJb0kzjTyKAdKGfi3GkOqD4AklImS5xz7xx1grZQMlNv87iV12g2pccEsml6WYDxQ1qJ7DFWa8FexAPDbzvZg==", "dev": true, "dependencies": { "@apidevtools/json-schema-ref-parser": "11.6.4", @@ -4837,9 +4835,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", - "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.1.tgz", + "integrity": "sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==", "cpu": [ "arm" ], @@ -4850,9 +4848,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", - "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.1.tgz", + "integrity": "sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==", "cpu": [ "arm64" ], @@ -4863,9 +4861,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", - "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.1.tgz", + "integrity": "sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==", "cpu": [ "arm64" ], @@ -4876,9 +4874,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", - "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.1.tgz", + "integrity": "sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==", "cpu": [ "x64" ], @@ -4889,9 +4887,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", - "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.1.tgz", + "integrity": "sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==", "cpu": [ "arm" ], @@ -4902,9 +4900,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", - "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.1.tgz", + "integrity": "sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==", "cpu": [ "arm" ], @@ -4915,9 +4913,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", - "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.1.tgz", + "integrity": "sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==", "cpu": [ "arm64" ], @@ -4928,9 +4926,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", - "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.1.tgz", + "integrity": "sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==", "cpu": [ "arm64" ], @@ -4941,9 +4939,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", - "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.1.tgz", + "integrity": "sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==", "cpu": [ "ppc64" ], @@ -4954,9 +4952,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", - "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.1.tgz", + "integrity": "sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==", "cpu": [ "riscv64" ], @@ -4967,9 +4965,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", - "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.1.tgz", + "integrity": "sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==", "cpu": [ "s390x" ], @@ -4980,9 +4978,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", - "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.1.tgz", + "integrity": "sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==", "cpu": [ "x64" ], @@ -4993,9 +4991,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", - "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.1.tgz", + "integrity": "sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==", "cpu": [ "x64" ], @@ -5006,9 +5004,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", - "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.1.tgz", + "integrity": "sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==", "cpu": [ "arm64" ], @@ -5019,9 +5017,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", - "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.1.tgz", + "integrity": "sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==", "cpu": [ "ia32" ], @@ -5032,9 +5030,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", - "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.1.tgz", + "integrity": "sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==", "cpu": [ "x64" ], @@ -5045,10 +5043,13 @@ ] }, "node_modules/@shikijs/core": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.10.1.tgz", - "integrity": "sha512-qdiJS5a/QGCff7VUFIqd0hDdWly9rDp8lhVmXVrS11aazX8LOTRLHAXkkEeONNsS43EcCd7gax9LLoOz4vlFQA==", - "dev": true + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.10.3.tgz", + "integrity": "sha512-D45PMaBaeDHxww+EkcDQtDAtzv00Gcsp72ukBtaLSmqRvh0WgGMq3Al0rl1QQBZfuneO75NXMIzEZGFitThWbg==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.4" + } }, "node_modules/@sinclair/typebox": { "version": "0.27.8", @@ -6368,15 +6369,6 @@ "url": "https://opencollective.com/storybook" } }, - "node_modules/@thepassle/axobject-query": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@thepassle/axobject-query/-/axobject-query-4.0.0.tgz", - "integrity": "sha512-/LHo+2jOdxs2WtbGocr3/lDSzsnjgCV6DSoBf4Y1Q0D24Hu67NPWuneoJimfHu5auqqSWi1fAvtln2013VxVqg==", - "dev": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", @@ -6668,6 +6660,15 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/http-assert": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.5.tgz", @@ -6799,9 +6800,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.14.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", - "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", + "version": "20.14.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", + "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -6981,16 +6982,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.15.0.tgz", - "integrity": "sha512-uiNHpyjZtFrLwLDpHnzaDlP3Tt6sGMqTCiqmxaN4n4RP0EfYZDODJyddiFDF44Hjwxr5xAcaYxVKm9QKQFJFLA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.0.tgz", + "integrity": "sha512-py1miT6iQpJcs1BiJjm54AMzeuMPBSPuKPlnT8HlfudbcS5rYeX5jajpLf3mrdRh9dA/Ec2FVUY0ifeVNDIhZw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.15.0", - "@typescript-eslint/type-utils": "7.15.0", - "@typescript-eslint/utils": "7.15.0", - "@typescript-eslint/visitor-keys": "7.15.0", + "@typescript-eslint/scope-manager": "7.16.0", + "@typescript-eslint/type-utils": "7.16.0", + "@typescript-eslint/utils": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -7014,13 +7015,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.15.0.tgz", - "integrity": "sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz", + "integrity": "sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/visitor-keys": "7.15.0" + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -7031,9 +7032,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.15.0.tgz", - "integrity": "sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.0.tgz", + "integrity": "sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -7044,13 +7045,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.15.0.tgz", - "integrity": "sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz", + "integrity": "sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/visitor-keys": "7.15.0", + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -7072,15 +7073,15 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.15.0.tgz", - "integrity": "sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.0.tgz", + "integrity": "sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.15.0", - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/typescript-estree": "7.15.0" + "@typescript-eslint/scope-manager": "7.16.0", + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/typescript-estree": "7.16.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -7094,12 +7095,12 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.15.0.tgz", - "integrity": "sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz", + "integrity": "sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/types": "7.16.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -7159,15 +7160,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.15.0.tgz", - "integrity": "sha512-k9fYuQNnypLFcqORNClRykkGOMOj+pV6V91R4GO/l1FDGwpqmSwoOQrOHo3cGaH63e+D3ZiCAOsuS/D2c99j/A==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.0.tgz", + "integrity": "sha512-ar9E+k7CU8rWi2e5ErzQiC93KKEFAXA2Kky0scAlPcxYblLt8+XZuHUZwlyfXILyQa95P6lQg+eZgh/dDs3+Vw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.15.0", - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/typescript-estree": "7.15.0", - "@typescript-eslint/visitor-keys": "7.15.0", + "@typescript-eslint/scope-manager": "7.16.0", + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/typescript-estree": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0", "debug": "^4.3.4" }, "engines": { @@ -7187,13 +7188,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.15.0.tgz", - "integrity": "sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz", + "integrity": "sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/visitor-keys": "7.15.0" + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -7204,9 +7205,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.15.0.tgz", - "integrity": "sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.0.tgz", + "integrity": "sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -7217,13 +7218,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.15.0.tgz", - "integrity": "sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz", + "integrity": "sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/visitor-keys": "7.15.0", + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -7245,12 +7246,12 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.15.0.tgz", - "integrity": "sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz", + "integrity": "sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/types": "7.16.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -7309,31 +7310,14 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.15.0.tgz", - "integrity": "sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.0.tgz", + "integrity": "sha512-j0fuUswUjDHfqV/UdW6mLtOQQseORqfdmoBNDFOqs9rvNVR2e+cmu6zJu/Ku4SDuqiJko6YnhwcL8x45r8Oqxg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.15.0", - "@typescript-eslint/utils": "7.15.0", + "@typescript-eslint/typescript-estree": "7.16.0", + "@typescript-eslint/utils": "7.16.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -7354,13 +7338,13 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.15.0.tgz", - "integrity": "sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz", + "integrity": "sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/visitor-keys": "7.15.0" + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -7371,9 +7355,9 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.15.0.tgz", - "integrity": "sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.0.tgz", + "integrity": "sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -7384,13 +7368,13 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.15.0.tgz", - "integrity": "sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz", + "integrity": "sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/visitor-keys": "7.15.0", + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -7412,15 +7396,15 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.15.0.tgz", - "integrity": "sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.0.tgz", + "integrity": "sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.15.0", - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/typescript-estree": "7.15.0" + "@typescript-eslint/scope-manager": "7.16.0", + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/typescript-estree": "7.16.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -7434,12 +7418,12 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.15.0.tgz", - "integrity": "sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz", + "integrity": "sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/types": "7.16.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -7498,147 +7482,6 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@umbraco-backoffice/block": { "resolved": "src/packages/block", "link": true @@ -7671,6 +7514,10 @@ "resolved": "src/packages/media", "link": true }, + "node_modules/@umbraco-backoffice/multi-url-picker": { + "resolved": "src/packages/multi-url-picker", + "link": true + }, "node_modules/@umbraco-backoffice/property-editors": { "resolved": "src/packages/property-editors", "link": true @@ -9569,15 +9416,6 @@ "node": ">=10" } }, - "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/array-back": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", @@ -10869,14 +10707,14 @@ } }, "node_modules/command-line-usage": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.1.tgz", - "integrity": "sha512-NCyznE//MuTjwi3y84QVUGEOT+P5oto1e1Pk/jFPVdPPfsG03qpTIl3yw6etR+v73d0lXsoojRpvbru2sqePxQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.2.tgz", + "integrity": "sha512-MwNFB8nxi3IVnzir+nkSIbDTU4H6ne26zqicO2eTt1wPrvdOAphPhnYqWOjxXKWYLNYDu4Z/r2ESEziEqKuOVg==", "dev": true, "dependencies": { "array-back": "^6.2.2", "chalk-template": "^0.4.0", - "table-layout": "^3.0.0", + "table-layout": "^3.0.2", "typical": "^7.1.1" }, "engines": { @@ -11590,36 +11428,10 @@ "node": ">=6.0.0" } }, - "node_modules/dom5": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dom5/-/dom5-3.0.1.tgz", - "integrity": "sha512-JPFiouQIr16VQ4dX6i0+Hpbg3H2bMKPmZ+WZgBOSSvOPx9QHwwY8sPzeM2baUtViESYto6wC2nuZOMC/6gulcA==", - "dev": true, - "dependencies": { - "@types/parse5": "^2.2.34", - "clone": "^2.1.0", - "parse5": "^4.0.0" - } - }, - "node_modules/dom5/node_modules/@types/parse5": { - "version": "2.2.34", - "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-2.2.34.tgz", - "integrity": "sha512-p3qOvaRsRpFyEmaS36RtLzpdxZZnmxGuT1GMgzkTtTJVFuEw7KFjGK83MFODpJExgX1bEzy9r0NYjMC3IMfi7w==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/dom5/node_modules/parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true - }, "node_modules/dompurify": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.5.tgz", - "integrity": "sha512-lwG+n5h8QNpxtyrJW/gJWckL+1/DQiYMX8f7t8Z2AZTPw1esVrqjI63i7Zc2Gz0aKzLVMYC1V1PL/ky+aY/NgA==" + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz", + "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==" }, "node_modules/dotenv": { "version": "16.4.5", @@ -11712,9 +11524,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.816", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.816.tgz", - "integrity": "sha512-EKH5X5oqC6hLmiS7/vYtZHZFTNdhsYG5NVPRN6Yn0kQHNBlT59+xSM8HBy66P5fxWpKgZbPqb+diC64ng295Jw==", + "version": "1.4.820", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.820.tgz", + "integrity": "sha512-kK/4O/YunacfboFEk/BDf7VO1HoPmDudLTJAU9NmXIOSjsV7qVIX3OrI4REZo0VmdqhcpUcncQc6N8Q3aEXlHg==", "dev": true }, "node_modules/element-internals-polyfill": { @@ -11722,12 +11534,6 @@ "resolved": "https://registry.npmjs.org/element-internals-polyfill/-/element-internals-polyfill-1.3.11.tgz", "integrity": "sha512-SQLQNVY4wMdpnP/F/HtalJbpEenQd46Avtjm5hvUdeTs3QU0zHFNX5/AmtQIPPcfzePb0ipCkQGY4GwYJIhLJA==" }, - "node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", - "dev": true - }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -12212,40 +12018,6 @@ "eslint": ">= 5" } }, - "node_modules/eslint-plugin-lit-a11y": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-lit-a11y/-/eslint-plugin-lit-a11y-4.1.3.tgz", - "integrity": "sha512-ndfGZU0RFKp9xqm17f94sQzjgVchDtYL1z/N0qG0otQiDtia1EUVCdd1eMlCeeUe6LTtfjV4iAsNU4r0bVB/Yw==", - "dev": true, - "dependencies": { - "@thepassle/axobject-query": "^4.0.0", - "aria-query": "^5.1.3", - "axe-core": "^4.3.3", - "dom5": "^3.0.1", - "emoji-regex": "^10.2.1", - "eslint-plugin-lit": "^1.10.1", - "eslint-rule-extender": "0.0.1", - "language-tags": "^1.0.5", - "parse5": "^7.1.2", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "requireindex": "~1.2.0" - }, - "peerDependencies": { - "eslint": ">= 5" - } - }, - "node_modules/eslint-plugin-lit-a11y/node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, "node_modules/eslint-plugin-local-rules": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/eslint-plugin-local-rules/-/eslint-plugin-local-rules-3.0.2.tgz", @@ -12282,33 +12054,6 @@ } } }, - "node_modules/eslint-plugin-storybook": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.8.0.tgz", - "integrity": "sha512-CZeVO5EzmPY7qghO2t64oaFM+8FTaD4uzOEjHKp516exyTKo+skKAL9GI3QALS2BXhyALJjNtwbmr1XinGE8bA==", - "dev": true, - "dependencies": { - "@storybook/csf": "^0.0.1", - "@typescript-eslint/utils": "^5.62.0", - "requireindex": "^1.2.0", - "ts-dedent": "^2.2.0" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "eslint": ">=6" - } - }, - "node_modules/eslint-plugin-storybook/node_modules/@storybook/csf": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.0.1.tgz", - "integrity": "sha512-USTLkZze5gkel8MYCujSRBVIrUQ3YPBrLOx7GNk/0wttvVtlzWXAq9eLbQ4p/NicGxP+3T7KPEMVV//g+yubpw==", - "dev": true, - "dependencies": { - "lodash": "^4.17.15" - } - }, "node_modules/eslint-plugin-wc": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.1.0.tgz", @@ -12322,19 +12067,6 @@ "eslint": ">=5" } }, - "node_modules/eslint-rule-extender": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/eslint-rule-extender/-/eslint-rule-extender-0.0.1.tgz", - "integrity": "sha512-F0j1Twve3lamL3J0rRSVAynlp58sDPG39JFcQrM+u9Na7PmCgiPHNODh6YE9mduaGcsn3NBqbf6LZRj0cLr8Ng==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kaicataldo" - } - }, "node_modules/eslint-scope": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", @@ -12470,9 +12202,9 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -13039,9 +12771,9 @@ "dev": true }, "node_modules/flow-parser": { - "version": "0.238.3", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.238.3.tgz", - "integrity": "sha512-hNUhucq8V6KWSX1skXUS3vnDmrRNuKWzDvEVK5b+n97uMF32zj2y8pmcLDQEqlY5u926B0GYGWT/3XhwDJfLOQ==", + "version": "0.239.1", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.239.1.tgz", + "integrity": "sha512-topOrETNxJ6T2gAnQiWqAlzGPj8uI2wtmNOlDIMNB+qyvGJZ6R++STbUOTAYmvPhOMz2gXnXPH0hOvURYmrBow==", "dev": true, "engines": { "node": ">=0.4.0" @@ -13380,9 +13112,9 @@ "dev": true }, "node_modules/glob": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", - "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", @@ -13395,9 +13127,6 @@ "bin": { "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -14721,15 +14450,15 @@ } }, "node_modules/jackspeak": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", - "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.2.tgz", + "integrity": "sha512-qH3nOSj8q/8+Eg8LUPOq3C+6HWkpUioIjDsq1+D4zY91oZvpPttw8GwtF1nReRYKXl+1AORyFqtm2f5Q1SB6/Q==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=14" + "node": "14 >=14.21 || 16 >=16.20 || >=18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -15365,24 +15094,6 @@ "node": ">= 0.6" } }, - "node_modules/language-subtag-registry": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", - "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", - "dev": true - }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", - "dev": true, - "dependencies": { - "language-subtag-registry": "^0.3.20" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/lazy-universal-dotenv": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/lazy-universal-dotenv/-/lazy-universal-dotenv-4.0.0.tgz", @@ -15679,9 +15390,9 @@ } }, "node_modules/lucide-static": { - "version": "0.379.0", - "resolved": "https://registry.npmjs.org/lucide-static/-/lucide-static-0.379.0.tgz", - "integrity": "sha512-oe0EPcRHVb0ua/XHs7B63fgtGI/8B/MchpXr6LZcXEQlPmqJeOua207Ju2f08g/yc8+LelngiFM0mD/B7O6mZA==", + "version": "0.407.0", + "resolved": "https://registry.npmjs.org/lucide-static/-/lucide-static-0.407.0.tgz", + "integrity": "sha512-RkFYdnnkQ5K9/V7V1yY+KiZn0Ugc9b0r9I19yuGu8TRA7HcNzcpuP8Fq0+V5ariS8JMzYMgv0sQ5Thaualskeg==", "dev": true }, "node_modules/lunr": { @@ -17535,9 +17246,9 @@ } }, "node_modules/outvariant": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.2.tgz", - "integrity": "sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", "dev": true }, "node_modules/p-event": { @@ -17784,12 +17495,12 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.0.tgz", - "integrity": "sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==", + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.2.tgz", + "integrity": "sha512-voV4dDrdVZVNz84n39LFKDaRzfwhdzJ7akpyXfTMxCgRUp07U3lcJUXRlhTKP17rgt09sUzLi5iCitpEAr+6ug==", "dev": true, "engines": { - "node": "14 || >=16.14" + "node": "14 || 16 || 18 || 20 || >=22" } }, "node_modules/path-to-regexp": { @@ -18271,9 +17982,9 @@ } }, "node_modules/qs": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.2.tgz", - "integrity": "sha512-x+NLUpx9SYrcwXtX7ob1gnkSems4i/mGZX5SlYxwIau6RrUSODO89TR/XDGGpn5RPWSYIB+aSfuSlV5+CmbTBg==", + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.3.tgz", + "integrity": "sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==", "dev": true, "dependencies": { "side-channel": "^1.0.6" @@ -18968,9 +18679,9 @@ } }, "node_modules/rollup": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", - "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.1.tgz", + "integrity": "sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -18983,22 +18694,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", + "@rollup/rollup-android-arm-eabi": "4.18.1", + "@rollup/rollup-android-arm64": "4.18.1", + "@rollup/rollup-darwin-arm64": "4.18.1", + "@rollup/rollup-darwin-x64": "4.18.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.1", + "@rollup/rollup-linux-arm-musleabihf": "4.18.1", + "@rollup/rollup-linux-arm64-gnu": "4.18.1", + "@rollup/rollup-linux-arm64-musl": "4.18.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.1", + "@rollup/rollup-linux-riscv64-gnu": "4.18.1", + "@rollup/rollup-linux-s390x-gnu": "4.18.1", + "@rollup/rollup-linux-x64-gnu": "4.18.1", + "@rollup/rollup-linux-x64-musl": "4.18.1", + "@rollup/rollup-win32-arm64-msvc": "4.18.1", + "@rollup/rollup-win32-ia32-msvc": "4.18.1", + "@rollup/rollup-win32-x64-msvc": "4.18.1", "fsevents": "~2.3.2" } }, @@ -19356,12 +19067,13 @@ } }, "node_modules/shiki": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.10.1.tgz", - "integrity": "sha512-uafV7WCgN4YYrccH6yxpnps6k38sSTlFRrwc4jycWmhWxJIm9dPrk+XkY1hZ2t0I7jmacMNb15Lf2fspa/Y3lg==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.10.3.tgz", + "integrity": "sha512-eneCLncGuvPdTutJuLyUGS8QNPAVFO5Trvld2wgEq1e002mwctAhJKeMGWtWVXOIEzmlcLRqcgPSorR6AVzOmQ==", "dev": true, "dependencies": { - "@shikijs/core": "1.10.1" + "@shikijs/core": "1.10.3", + "@types/hast": "^3.0.4" } }, "node_modules/side-channel": { @@ -19389,9 +19101,9 @@ "dev": true }, "node_modules/simple-icons": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-13.0.0.tgz", - "integrity": "sha512-emybHoFXO9IorOH8Uy8GwavS1oOQk6bVMxrIAXztNn/EcRc17yHmJM6XpUUd1CcDpax0dHAbpqvGp4hqWlQapg==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-13.1.0.tgz", + "integrity": "sha512-UUQ5ThpD4J9qkkwnLfloj+mIbJXRHJnMS24lcW+j9ROLpKXysJt2p42iJz/tRi3gEckYaR5+LA5Kb7CZ8Wq8rw==", "dev": true, "engines": { "node": ">=0.12.18" @@ -20051,9 +19763,9 @@ } }, "node_modules/text-decoder": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.0.tgz", - "integrity": "sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz", + "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==", "dev": true, "dependencies": { "b4a": "^1.6.4" @@ -20133,9 +19845,9 @@ "integrity": "sha512-okoJyxuPv1gzASxQDNgQbnUXOdAIyoOSXcXcZZu7tiW0PSKEdf3SdASxPBupRj+64/E3elHwVRnzSdo82Emqbg==" }, "node_modules/tinymce-i18n": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/tinymce-i18n/-/tinymce-i18n-24.7.1.tgz", - "integrity": "sha512-eiLiYT4lRDV/49IfaVssF+Ov6i3Q/eMosYZfNTrj3s2AP7TpCM+TNVi4gfIYDf0aejNfEnkbtMp1F/nURL08dw==" + "version": "24.7.8", + "resolved": "https://registry.npmjs.org/tinymce-i18n/-/tinymce-i18n-24.7.8.tgz", + "integrity": "sha512-WShFgJVHGQ3cQrZ6dHkFHb3S5o+1kLMpRa7qllUV+ZSqS1lldq1nadFgOHAr0KzWWYAMHDEow9O+cbKBlqMB+A==" }, "node_modules/tmp": { "version": "0.0.33", @@ -20376,27 +20088,6 @@ "node": ">=0.6.x" } }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -20573,14 +20264,14 @@ } }, "node_modules/typescript-eslint": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.15.0.tgz", - "integrity": "sha512-Ta40FhMXBCwHura4X4fncaCVkVcnJ9jnOq5+Lp4lN8F4DzHZtOwZdRvVBiNUGznUDHPwdGnrnwxmUOU2fFQqFA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.16.0.tgz", + "integrity": "sha512-kaVRivQjOzuoCXU6+hLnjo3/baxyzWVO5GrnExkFzETRYJKVHYkrJglOu2OCm8Hi9RPDWX1PTNNTpU5KRV0+RA==", "dev": true, "dependencies": { - "@typescript-eslint/eslint-plugin": "7.15.0", - "@typescript-eslint/parser": "7.15.0", - "@typescript-eslint/utils": "7.15.0" + "@typescript-eslint/eslint-plugin": "7.16.0", + "@typescript-eslint/parser": "7.16.0", + "@typescript-eslint/utils": "7.16.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -20599,13 +20290,13 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.15.0.tgz", - "integrity": "sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz", + "integrity": "sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/visitor-keys": "7.15.0" + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -20616,9 +20307,9 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.15.0.tgz", - "integrity": "sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.0.tgz", + "integrity": "sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -20629,13 +20320,13 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.15.0.tgz", - "integrity": "sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz", + "integrity": "sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/visitor-keys": "7.15.0", + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/visitor-keys": "7.16.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -20657,15 +20348,15 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.15.0.tgz", - "integrity": "sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.0.tgz", + "integrity": "sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.15.0", - "@typescript-eslint/types": "7.15.0", - "@typescript-eslint/typescript-estree": "7.15.0" + "@typescript-eslint/scope-manager": "7.16.0", + "@typescript-eslint/types": "7.16.0", + "@typescript-eslint/typescript-estree": "7.16.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -20679,12 +20370,12 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.15.0.tgz", - "integrity": "sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz", + "integrity": "sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/types": "7.16.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -22132,9 +21823,9 @@ } }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, "engines": { "node": ">=10.0.0" @@ -22298,6 +21989,9 @@ "src/packages/media": { "name": "@umbraco-backoffice/media" }, + "src/packages/multi-url-picker": { + "name": "@umbraco-backoffice/multi-url-picker" + }, "src/packages/property-editors": { "name": "@umbraco-backoffice/property-editors" }, diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 6fa4dad22a..6ad1b6d578 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -56,6 +56,7 @@ "./menu": "./dist-cms/packages/core/menu/index.js", "./modal": "./dist-cms/packages/core/modal/index.js", "./models": "./dist-cms/packages/core/models/index.js", + "./multi-url-picker": "./dist-cms/packages/multi-url-picker/index.js", "./notification": "./dist-cms/packages/core/notification/index.js", "./object-type": "./dist-cms/packages/object-type/index.js", "./package": "./dist-cms/packages/packages/package/index.js", @@ -135,15 +136,14 @@ "./src/packages/documents", "./src/packages/health-check", "./src/packages/language", - "./src/packages/tags", - "./src/packages/umbraco-news", - "./src/packages/webhook", - "./src/packages/health-check", + "./src/packages/media", + "./src/packages/multi-url-picker", + "./src/packages/property-editors", "./src/packages/tags", "./src/packages/templating", - "./src/packages/property-editors", - "./src/packages/media", - "./src/packages/user" + "./src/packages/umbraco-news", + "./src/packages/user", + "./src/packages/webhook" ], "scripts": { "backoffice:test:e2e": "npx playwright test", @@ -158,6 +158,7 @@ "check": "npm run lint:errors && npm run compile && npm run build-storybook && npm run generate:jsonschema:dist", "check:paths": "node ./devops/build/check-path-length.js dist-cms 120", "compile": "tsc", + "postinstall": "npm run generate:tsconfig", "dev": "vite", "dev:server": "VITE_UMBRACO_USE_MSW=off vite", "dev:mock": "VITE_UMBRACO_USE_MSW=on vite", @@ -244,14 +245,12 @@ "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-lit": "^1.14.0", - "eslint-plugin-lit-a11y": "^4.1.3", "eslint-plugin-local-rules": "^3.0.2", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-storybook": "^0.8.0", "eslint-plugin-wc": "^2.1.0", "glob": "^10.3.10", "globals": "^15.7.0", - "lucide-static": "^0.379.0", + "lucide-static": "^0.407.0", "msw": "^1.3.2", "playwright-msw": "^3.0.1", "prettier": "3.3.2", @@ -262,7 +261,7 @@ "rollup-plugin-esbuild": "^6.1.1", "rollup-plugin-import-css": "^3.5.0", "rollup-plugin-web-worker-loader": "^1.6.1", - "simple-icons": "^13.0.0", + "simple-icons": "^13.1.0", "storybook": "^7.6.17", "tiny-glob": "^0.2.9", "tsc-alias": "^1.8.8", diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/api-interceptor.controller.ts b/src/Umbraco.Web.UI.Client/src/apps/app/api-interceptor.controller.ts new file mode 100644 index 0000000000..874c96c2b0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/apps/app/api-interceptor.controller.ts @@ -0,0 +1,54 @@ +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { OpenAPI } from '@umbraco-cms/backoffice/external/backend-api'; +import { + extractUmbNotificationColor, + isUmbNotifications, + UMB_NOTIFICATION_CONTEXT, + UMB_NOTIFICATION_HEADER, +} from '@umbraco-cms/backoffice/notification'; + +/** + * Controller that adds interceptors to the OpenAPI client + */ +export class UmbApiInterceptorController extends UmbControllerBase { + constructor(host: UmbControllerHost) { + super(host); + this.#addUmbNotificationsInterceptor(); + } + + /** + * Interceptor which checks responses for the umb-notifications header and displays them as a notification if any. Removes the umb-notifications from the headers. + */ + #addUmbNotificationsInterceptor() { + OpenAPI.interceptors.response.use((response) => { + const umbNotifications = response.headers.get(UMB_NOTIFICATION_HEADER); + if (!umbNotifications) return response; + + const notifications = JSON.parse(umbNotifications); + if (!isUmbNotifications(notifications)) return response; + + this.getContext(UMB_NOTIFICATION_CONTEXT).then((notificationContext) => { + for (const notification of notifications) { + notificationContext.peek(extractUmbNotificationColor(notification.type), { + data: { headline: notification.category, message: notification.message }, + }); + } + }); + + const newHeader = new Headers(); + for (const header of response.headers.entries()) { + const [key, value] = header; + if (key !== UMB_NOTIFICATION_HEADER) newHeader.set(key, value); + } + + const newResponse = new Response(response.body, { + headers: newHeader, + status: response.status, + statusText: response.statusText, + }); + + return newResponse; + }); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts index cb53895f57..68dd2e5b1b 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts @@ -3,6 +3,7 @@ import type { UmbAppErrorElement } from './app-error.element.js'; import { UmbAppContext } from './app.context.js'; import { UmbServerConnection } from './server-connection.js'; import { UmbAppAuthController } from './app-auth.controller.js'; +import { UmbApiInterceptorController } from './api-interceptor.controller.js'; import type { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth'; import { UmbAuthContext } from '@umbraco-cms/backoffice/auth'; import { css, html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; @@ -147,6 +148,8 @@ export class UmbAppElement extends UmbLitElement { OpenAPI.BASE = window.location.origin; + new UmbApiInterceptorController(this); + new UmbBundleExtensionInitializer(this, umbExtensionsRegistry); new UUIIconRegistryEssential().attach(this); diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts index 0a0ec7595d..9ed617b177 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts @@ -1861,6 +1861,8 @@ export default { lastLogin: 'Seneste login', lastPasswordChangeDate: 'Kodeord sidst ændret', loginname: 'Brugernavn', + loginnameRequired: 'Påkrævet - indtast et brugernavn for denne bruger', + loginnameDescription: 'Brugernavnet bruges til at logge ind og til at identificere brugeren', mediastartnode: 'Startnode i mediearkivet', mediastartnodehelp: 'Begræns mediebiblioteket til en bestemt startnode', mediastartnodes: 'Medie startnoder', @@ -1963,7 +1965,12 @@ export default { '2faCodeInput': 'Indtast din verifikationskode', '2faCodeInputHelp': 'Indtast din verifikationskode fra din autentificeringsapp', '2faInvalidCode': 'Den indtastede kode er ugyldig', - emailRequired: 'Required - enter an email address for this user', + emailRequired: 'Påkrævet - indtast en emailadresse for denne bruger', + emailDescription: (usernameIsEmail: boolean) => { + return usernameIsEmail + ? 'Emailadressen bruges som brugernavn og til notifikationer og adgangskode gendannelse' + : 'Emailadressen bruges til notifikationer og adgangskode gendannelse'; + }, duplicateLogin: 'A user with this login already exists', nameRequired: 'Required - enter a name for this user', passwordRequiresDigit: "The password must have at least one digit ('0'-'9')", diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts index 020b2f5683..ff6d54933f 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts @@ -1841,6 +1841,11 @@ export default { changePhoto: 'Change photo', configureMfa: 'Configure MFA', emailRequired: 'Required - enter an email address for this user', + emailDescription: (usernameIsEmail: boolean) => { + return usernameIsEmail + ? 'The email address is used for notifications, password recovery, and as the username for logging in' + : 'The email address is used for notifications and password recovery'; + }, newPassword: 'New password', newPasswordFormatLengthTip: 'Minimum %0% character(s) to go!', newPasswordFormatNonAlphaTip: 'There should be at least %0% special character(s) in there.', @@ -1872,6 +1877,8 @@ export default { lastLogin: 'Last login', lastPasswordChangeDate: 'Password last changed', loginname: 'Username', + loginnameRequired: 'Required - enter a username for this user', + loginnameDescription: 'The username is used for logging in', mediastartnode: 'Media start node', mediastartnodehelp: 'Limit the media library to a specific start node', mediastartnodes: 'Media start nodes', diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts index 52d3d86a1a..882a508481 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts @@ -1894,6 +1894,11 @@ export default { changePhoto: 'Change photo', configureMfa: 'Configure MFA', emailRequired: 'Required - enter an email address for this user', + emailDescription: (usernameIsEmail: boolean) => { + return usernameIsEmail + ? 'The email address is used for notifications, password recovery, and as the username for logging in' + : 'The email address is used for notifications and password recovery'; + }, newPassword: 'New password', newPasswordFormatLengthTip: 'Minimum %0% character(s) to go!', newPasswordFormatNonAlphaTip: 'There should be at least %0% special character(s) in there.', @@ -1925,6 +1930,8 @@ export default { lastLogin: 'Last login', lastPasswordChangeDate: 'Password last changed', loginname: 'Username', + loginnameRequired: 'Required - enter a username for this user', + loginnameDescription: 'The username is used for logging in', mediastartnode: 'Media start node', mediastartnodehelp: 'Limit the media library to a specific start node', mediastartnodes: 'Media start nodes', diff --git a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/services.gen.ts b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/services.gen.ts index 04f5fac093..7a132b3073 100644 --- a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/services.gen.ts +++ b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/services.gen.ts @@ -3,7 +3,7 @@ import type { CancelablePromise } from './core/CancelablePromise'; import { OpenAPI } from './core/OpenAPI'; import { request as __request } from './core/request'; -import type { GetCultureData, GetCultureResponse, PostDataTypeData, PostDataTypeResponse, GetDataTypeByIdData, GetDataTypeByIdResponse, DeleteDataTypeByIdData, DeleteDataTypeByIdResponse, PutDataTypeByIdData, PutDataTypeByIdResponse, PostDataTypeByIdCopyData, PostDataTypeByIdCopyResponse, GetDataTypeByIdIsUsedData, GetDataTypeByIdIsUsedResponse, PutDataTypeByIdMoveData, PutDataTypeByIdMoveResponse, GetDataTypeByIdReferencesData, GetDataTypeByIdReferencesResponse, GetDataTypeConfigurationResponse, PostDataTypeFolderData, PostDataTypeFolderResponse, GetDataTypeFolderByIdData, GetDataTypeFolderByIdResponse, DeleteDataTypeFolderByIdData, DeleteDataTypeFolderByIdResponse, PutDataTypeFolderByIdData, PutDataTypeFolderByIdResponse, GetFilterDataTypeData, GetFilterDataTypeResponse, GetItemDataTypeData, GetItemDataTypeResponse, GetItemDataTypeSearchData, GetItemDataTypeSearchResponse, GetTreeDataTypeAncestorsData, GetTreeDataTypeAncestorsResponse, GetTreeDataTypeChildrenData, GetTreeDataTypeChildrenResponse, GetTreeDataTypeRootData, GetTreeDataTypeRootResponse, GetDictionaryData, GetDictionaryResponse, PostDictionaryData, PostDictionaryResponse, GetDictionaryByIdData, GetDictionaryByIdResponse, DeleteDictionaryByIdData, DeleteDictionaryByIdResponse, PutDictionaryByIdData, PutDictionaryByIdResponse, GetDictionaryByIdExportData, GetDictionaryByIdExportResponse, PutDictionaryByIdMoveData, PutDictionaryByIdMoveResponse, PostDictionaryImportData, PostDictionaryImportResponse, GetItemDictionaryData, GetItemDictionaryResponse, GetTreeDictionaryAncestorsData, GetTreeDictionaryAncestorsResponse, GetTreeDictionaryChildrenData, GetTreeDictionaryChildrenResponse, GetTreeDictionaryRootData, GetTreeDictionaryRootResponse, PostDocumentBlueprintData, PostDocumentBlueprintResponse, GetDocumentBlueprintByIdData, GetDocumentBlueprintByIdResponse, DeleteDocumentBlueprintByIdData, DeleteDocumentBlueprintByIdResponse, PutDocumentBlueprintByIdData, PutDocumentBlueprintByIdResponse, PutDocumentBlueprintByIdMoveData, PutDocumentBlueprintByIdMoveResponse, PostDocumentBlueprintFolderData, PostDocumentBlueprintFolderResponse, GetDocumentBlueprintFolderByIdData, GetDocumentBlueprintFolderByIdResponse, DeleteDocumentBlueprintFolderByIdData, DeleteDocumentBlueprintFolderByIdResponse, PutDocumentBlueprintFolderByIdData, PutDocumentBlueprintFolderByIdResponse, PostDocumentBlueprintFromDocumentData, PostDocumentBlueprintFromDocumentResponse, GetItemDocumentBlueprintData, GetItemDocumentBlueprintResponse, GetTreeDocumentBlueprintAncestorsData, GetTreeDocumentBlueprintAncestorsResponse, GetTreeDocumentBlueprintChildrenData, GetTreeDocumentBlueprintChildrenResponse, GetTreeDocumentBlueprintRootData, GetTreeDocumentBlueprintRootResponse, PostDocumentTypeData, PostDocumentTypeResponse, GetDocumentTypeByIdData, GetDocumentTypeByIdResponse, DeleteDocumentTypeByIdData, DeleteDocumentTypeByIdResponse, PutDocumentTypeByIdData, PutDocumentTypeByIdResponse, GetDocumentTypeByIdAllowedChildrenData, GetDocumentTypeByIdAllowedChildrenResponse, GetDocumentTypeByIdBlueprintData, GetDocumentTypeByIdBlueprintResponse, GetDocumentTypeByIdCompositionReferencesData, GetDocumentTypeByIdCompositionReferencesResponse, PostDocumentTypeByIdCopyData, PostDocumentTypeByIdCopyResponse, GetDocumentTypeByIdExportData, GetDocumentTypeByIdExportResponse, PutDocumentTypeByIdImportData, PutDocumentTypeByIdImportResponse, PutDocumentTypeByIdMoveData, PutDocumentTypeByIdMoveResponse, GetDocumentTypeAllowedAtRootData, GetDocumentTypeAllowedAtRootResponse, PostDocumentTypeAvailableCompositionsData, PostDocumentTypeAvailableCompositionsResponse, GetDocumentTypeConfigurationResponse, PostDocumentTypeFolderData, PostDocumentTypeFolderResponse, GetDocumentTypeFolderByIdData, GetDocumentTypeFolderByIdResponse, DeleteDocumentTypeFolderByIdData, DeleteDocumentTypeFolderByIdResponse, PutDocumentTypeFolderByIdData, PutDocumentTypeFolderByIdResponse, PostDocumentTypeImportData, PostDocumentTypeImportResponse, GetItemDocumentTypeData, GetItemDocumentTypeResponse, GetItemDocumentTypeSearchData, GetItemDocumentTypeSearchResponse, GetTreeDocumentTypeAncestorsData, GetTreeDocumentTypeAncestorsResponse, GetTreeDocumentTypeChildrenData, GetTreeDocumentTypeChildrenResponse, GetTreeDocumentTypeRootData, GetTreeDocumentTypeRootResponse, GetDocumentVersionData, GetDocumentVersionResponse, GetDocumentVersionByIdData, GetDocumentVersionByIdResponse, PutDocumentVersionByIdPreventCleanupData, PutDocumentVersionByIdPreventCleanupResponse, PostDocumentVersionByIdRollbackData, PostDocumentVersionByIdRollbackResponse, GetCollectionDocumentByIdData, GetCollectionDocumentByIdResponse, PostDocumentData, PostDocumentResponse, GetDocumentByIdData, GetDocumentByIdResponse, DeleteDocumentByIdData, DeleteDocumentByIdResponse, PutDocumentByIdData, PutDocumentByIdResponse, GetDocumentByIdAuditLogData, GetDocumentByIdAuditLogResponse, PostDocumentByIdCopyData, PostDocumentByIdCopyResponse, GetDocumentByIdDomainsData, GetDocumentByIdDomainsResponse, PutDocumentByIdDomainsData, PutDocumentByIdDomainsResponse, PutDocumentByIdMoveData, PutDocumentByIdMoveResponse, PutDocumentByIdMoveToRecycleBinData, PutDocumentByIdMoveToRecycleBinResponse, GetDocumentByIdNotificationsData, GetDocumentByIdNotificationsResponse, PutDocumentByIdNotificationsData, PutDocumentByIdNotificationsResponse, PostDocumentByIdPublicAccessData, PostDocumentByIdPublicAccessResponse, DeleteDocumentByIdPublicAccessData, DeleteDocumentByIdPublicAccessResponse, GetDocumentByIdPublicAccessData, GetDocumentByIdPublicAccessResponse, PutDocumentByIdPublicAccessData, PutDocumentByIdPublicAccessResponse, PutDocumentByIdPublishData, PutDocumentByIdPublishResponse, PutDocumentByIdPublishWithDescendantsData, PutDocumentByIdPublishWithDescendantsResponse, GetDocumentByIdReferencedByData, GetDocumentByIdReferencedByResponse, GetDocumentByIdReferencedDescendantsData, GetDocumentByIdReferencedDescendantsResponse, PutDocumentByIdUnpublishData, PutDocumentByIdUnpublishResponse, PutDocumentByIdValidateData, PutDocumentByIdValidateResponse, GetDocumentAreReferencedData, GetDocumentAreReferencedResponse, GetDocumentConfigurationResponse, PutDocumentSortData, PutDocumentSortResponse, GetDocumentUrlsData, GetDocumentUrlsResponse, PostDocumentValidateData, PostDocumentValidateResponse, GetItemDocumentData, GetItemDocumentResponse, GetItemDocumentSearchData, GetItemDocumentSearchResponse, DeleteRecycleBinDocumentResponse, DeleteRecycleBinDocumentByIdData, DeleteRecycleBinDocumentByIdResponse, GetRecycleBinDocumentByIdOriginalParentData, GetRecycleBinDocumentByIdOriginalParentResponse, PutRecycleBinDocumentByIdRestoreData, PutRecycleBinDocumentByIdRestoreResponse, GetRecycleBinDocumentChildrenData, GetRecycleBinDocumentChildrenResponse, GetRecycleBinDocumentRootData, GetRecycleBinDocumentRootResponse, GetTreeDocumentAncestorsData, GetTreeDocumentAncestorsResponse, GetTreeDocumentChildrenData, GetTreeDocumentChildrenResponse, GetTreeDocumentRootData, GetTreeDocumentRootResponse, PostDynamicRootQueryData, PostDynamicRootQueryResponse, GetDynamicRootStepsResponse, GetHealthCheckGroupData, GetHealthCheckGroupResponse, GetHealthCheckGroupByNameData, GetHealthCheckGroupByNameResponse, PostHealthCheckGroupByNameCheckData, PostHealthCheckGroupByNameCheckResponse, PostHealthCheckExecuteActionData, PostHealthCheckExecuteActionResponse, GetHelpData, GetHelpResponse, GetImagingResizeUrlsData, GetImagingResizeUrlsResponse, GetImportAnalyzeData, GetImportAnalyzeResponse, GetIndexerData, GetIndexerResponse, GetIndexerByIndexNameData, GetIndexerByIndexNameResponse, PostIndexerByIndexNameRebuildData, PostIndexerByIndexNameRebuildResponse, GetInstallSettingsResponse, PostInstallSetupData, PostInstallSetupResponse, PostInstallValidateDatabaseData, PostInstallValidateDatabaseResponse, GetItemLanguageData, GetItemLanguageResponse, GetItemLanguageDefaultResponse, GetLanguageData, GetLanguageResponse, PostLanguageData, PostLanguageResponse, GetLanguageByIsoCodeData, GetLanguageByIsoCodeResponse, DeleteLanguageByIsoCodeData, DeleteLanguageByIsoCodeResponse, PutLanguageByIsoCodeData, PutLanguageByIsoCodeResponse, GetLogViewerLevelData, GetLogViewerLevelResponse, GetLogViewerLevelCountData, GetLogViewerLevelCountResponse, GetLogViewerLogData, GetLogViewerLogResponse, GetLogViewerMessageTemplateData, GetLogViewerMessageTemplateResponse, GetLogViewerSavedSearchData, GetLogViewerSavedSearchResponse, PostLogViewerSavedSearchData, PostLogViewerSavedSearchResponse, GetLogViewerSavedSearchByNameData, GetLogViewerSavedSearchByNameResponse, DeleteLogViewerSavedSearchByNameData, DeleteLogViewerSavedSearchByNameResponse, GetLogViewerValidateLogsSizeData, GetLogViewerValidateLogsSizeResponse, GetManifestManifestResponse, GetManifestManifestPrivateResponse, GetManifestManifestPublicResponse, GetItemMediaTypeData, GetItemMediaTypeResponse, GetItemMediaTypeAllowedData, GetItemMediaTypeAllowedResponse, GetItemMediaTypeFoldersData, GetItemMediaTypeFoldersResponse, GetItemMediaTypeSearchData, GetItemMediaTypeSearchResponse, PostMediaTypeData, PostMediaTypeResponse, GetMediaTypeByIdData, GetMediaTypeByIdResponse, DeleteMediaTypeByIdData, DeleteMediaTypeByIdResponse, PutMediaTypeByIdData, PutMediaTypeByIdResponse, GetMediaTypeByIdAllowedChildrenData, GetMediaTypeByIdAllowedChildrenResponse, GetMediaTypeByIdCompositionReferencesData, GetMediaTypeByIdCompositionReferencesResponse, PostMediaTypeByIdCopyData, PostMediaTypeByIdCopyResponse, GetMediaTypeByIdExportData, GetMediaTypeByIdExportResponse, PutMediaTypeByIdImportData, PutMediaTypeByIdImportResponse, PutMediaTypeByIdMoveData, PutMediaTypeByIdMoveResponse, GetMediaTypeAllowedAtRootData, GetMediaTypeAllowedAtRootResponse, PostMediaTypeAvailableCompositionsData, PostMediaTypeAvailableCompositionsResponse, PostMediaTypeFolderData, PostMediaTypeFolderResponse, GetMediaTypeFolderByIdData, GetMediaTypeFolderByIdResponse, DeleteMediaTypeFolderByIdData, DeleteMediaTypeFolderByIdResponse, PutMediaTypeFolderByIdData, PutMediaTypeFolderByIdResponse, PostMediaTypeImportData, PostMediaTypeImportResponse, GetTreeMediaTypeAncestorsData, GetTreeMediaTypeAncestorsResponse, GetTreeMediaTypeChildrenData, GetTreeMediaTypeChildrenResponse, GetTreeMediaTypeRootData, GetTreeMediaTypeRootResponse, GetCollectionMediaData, GetCollectionMediaResponse, GetItemMediaData, GetItemMediaResponse, GetItemMediaSearchData, GetItemMediaSearchResponse, PostMediaData, PostMediaResponse, GetMediaByIdData, GetMediaByIdResponse, DeleteMediaByIdData, DeleteMediaByIdResponse, PutMediaByIdData, PutMediaByIdResponse, GetMediaByIdAuditLogData, GetMediaByIdAuditLogResponse, PutMediaByIdMoveData, PutMediaByIdMoveResponse, PutMediaByIdMoveToRecycleBinData, PutMediaByIdMoveToRecycleBinResponse, GetMediaByIdReferencedByData, GetMediaByIdReferencedByResponse, GetMediaByIdReferencedDescendantsData, GetMediaByIdReferencedDescendantsResponse, PutMediaByIdValidateData, PutMediaByIdValidateResponse, GetMediaAreReferencedData, GetMediaAreReferencedResponse, GetMediaConfigurationResponse, PutMediaSortData, PutMediaSortResponse, GetMediaUrlsData, GetMediaUrlsResponse, PostMediaValidateData, PostMediaValidateResponse, DeleteRecycleBinMediaResponse, DeleteRecycleBinMediaByIdData, DeleteRecycleBinMediaByIdResponse, GetRecycleBinMediaByIdOriginalParentData, GetRecycleBinMediaByIdOriginalParentResponse, PutRecycleBinMediaByIdRestoreData, PutRecycleBinMediaByIdRestoreResponse, GetRecycleBinMediaChildrenData, GetRecycleBinMediaChildrenResponse, GetRecycleBinMediaRootData, GetRecycleBinMediaRootResponse, GetTreeMediaAncestorsData, GetTreeMediaAncestorsResponse, GetTreeMediaChildrenData, GetTreeMediaChildrenResponse, GetTreeMediaRootData, GetTreeMediaRootResponse, GetItemMemberGroupData, GetItemMemberGroupResponse, GetMemberGroupData, GetMemberGroupResponse, PostMemberGroupData, PostMemberGroupResponse, GetMemberGroupByIdData, GetMemberGroupByIdResponse, DeleteMemberGroupByIdData, DeleteMemberGroupByIdResponse, PutMemberGroupByIdData, PutMemberGroupByIdResponse, GetTreeMemberGroupRootData, GetTreeMemberGroupRootResponse, GetItemMemberTypeData, GetItemMemberTypeResponse, GetItemMemberTypeSearchData, GetItemMemberTypeSearchResponse, PostMemberTypeData, PostMemberTypeResponse, GetMemberTypeByIdData, GetMemberTypeByIdResponse, DeleteMemberTypeByIdData, DeleteMemberTypeByIdResponse, PutMemberTypeByIdData, PutMemberTypeByIdResponse, GetMemberTypeByIdCompositionReferencesData, GetMemberTypeByIdCompositionReferencesResponse, PostMemberTypeByIdCopyData, PostMemberTypeByIdCopyResponse, PostMemberTypeAvailableCompositionsData, PostMemberTypeAvailableCompositionsResponse, GetTreeMemberTypeRootData, GetTreeMemberTypeRootResponse, GetFilterMemberData, GetFilterMemberResponse, GetItemMemberData, GetItemMemberResponse, GetItemMemberSearchData, GetItemMemberSearchResponse, PostMemberData, PostMemberResponse, GetMemberByIdData, GetMemberByIdResponse, DeleteMemberByIdData, DeleteMemberByIdResponse, PutMemberByIdData, PutMemberByIdResponse, PutMemberByIdValidateData, PutMemberByIdValidateResponse, GetMemberConfigurationResponse, PostMemberValidateData, PostMemberValidateResponse, PostModelsBuilderBuildResponse, GetModelsBuilderDashboardResponse, GetModelsBuilderStatusResponse, GetObjectTypesData, GetObjectTypesResponse, GetOembedQueryData, GetOembedQueryResponse, PostPackageByNameRunMigrationData, PostPackageByNameRunMigrationResponse, GetPackageConfigurationResponse, GetPackageCreatedData, GetPackageCreatedResponse, PostPackageCreatedData, PostPackageCreatedResponse, GetPackageCreatedByIdData, GetPackageCreatedByIdResponse, DeletePackageCreatedByIdData, DeletePackageCreatedByIdResponse, PutPackageCreatedByIdData, PutPackageCreatedByIdResponse, GetPackageCreatedByIdDownloadData, GetPackageCreatedByIdDownloadResponse, GetPackageMigrationStatusData, GetPackageMigrationStatusResponse, GetItemPartialViewData, GetItemPartialViewResponse, PostPartialViewData, PostPartialViewResponse, GetPartialViewByPathData, GetPartialViewByPathResponse, DeletePartialViewByPathData, DeletePartialViewByPathResponse, PutPartialViewByPathData, PutPartialViewByPathResponse, PutPartialViewByPathRenameData, PutPartialViewByPathRenameResponse, PostPartialViewFolderData, PostPartialViewFolderResponse, GetPartialViewFolderByPathData, GetPartialViewFolderByPathResponse, DeletePartialViewFolderByPathData, DeletePartialViewFolderByPathResponse, GetPartialViewSnippetData, GetPartialViewSnippetResponse, GetPartialViewSnippetByIdData, GetPartialViewSnippetByIdResponse, GetTreePartialViewAncestorsData, GetTreePartialViewAncestorsResponse, GetTreePartialViewChildrenData, GetTreePartialViewChildrenResponse, GetTreePartialViewRootData, GetTreePartialViewRootResponse, DeletePreviewResponse, PostPreviewResponse, GetProfilingStatusResponse, PutProfilingStatusData, PutProfilingStatusResponse, GetPropertyTypeIsUsedData, GetPropertyTypeIsUsedResponse, PostPublishedCacheCollectResponse, PostPublishedCacheRebuildResponse, PostPublishedCacheReloadResponse, GetPublishedCacheStatusResponse, GetRedirectManagementData, GetRedirectManagementResponse, GetRedirectManagementByIdData, GetRedirectManagementByIdResponse, DeleteRedirectManagementByIdData, DeleteRedirectManagementByIdResponse, GetRedirectManagementStatusResponse, PostRedirectManagementStatusData, PostRedirectManagementStatusResponse, GetItemRelationTypeData, GetItemRelationTypeResponse, GetRelationTypeData, GetRelationTypeResponse, GetRelationTypeByIdData, GetRelationTypeByIdResponse, GetRelationByRelationTypeIdData, GetRelationByRelationTypeIdResponse, GetItemScriptData, GetItemScriptResponse, PostScriptData, PostScriptResponse, GetScriptByPathData, GetScriptByPathResponse, DeleteScriptByPathData, DeleteScriptByPathResponse, PutScriptByPathData, PutScriptByPathResponse, PutScriptByPathRenameData, PutScriptByPathRenameResponse, PostScriptFolderData, PostScriptFolderResponse, GetScriptFolderByPathData, GetScriptFolderByPathResponse, DeleteScriptFolderByPathData, DeleteScriptFolderByPathResponse, GetTreeScriptAncestorsData, GetTreeScriptAncestorsResponse, GetTreeScriptChildrenData, GetTreeScriptChildrenResponse, GetTreeScriptRootData, GetTreeScriptRootResponse, GetSearcherData, GetSearcherResponse, GetSearcherBySearcherNameQueryData, GetSearcherBySearcherNameQueryResponse, GetSecurityConfigurationResponse, PostSecurityForgotPasswordData, PostSecurityForgotPasswordResponse, PostSecurityForgotPasswordResetData, PostSecurityForgotPasswordResetResponse, PostSecurityForgotPasswordVerifyData, PostSecurityForgotPasswordVerifyResponse, GetSegmentData, GetSegmentResponse, GetServerConfigurationResponse, GetServerInformationResponse, GetServerStatusResponse, GetServerTroubleshootingResponse, GetItemStaticFileData, GetItemStaticFileResponse, GetTreeStaticFileAncestorsData, GetTreeStaticFileAncestorsResponse, GetTreeStaticFileChildrenData, GetTreeStaticFileChildrenResponse, GetTreeStaticFileRootData, GetTreeStaticFileRootResponse, GetItemStylesheetData, GetItemStylesheetResponse, PostStylesheetData, PostStylesheetResponse, GetStylesheetByPathData, GetStylesheetByPathResponse, DeleteStylesheetByPathData, DeleteStylesheetByPathResponse, PutStylesheetByPathData, PutStylesheetByPathResponse, PutStylesheetByPathRenameData, PutStylesheetByPathRenameResponse, PostStylesheetFolderData, PostStylesheetFolderResponse, GetStylesheetFolderByPathData, GetStylesheetFolderByPathResponse, DeleteStylesheetFolderByPathData, DeleteStylesheetFolderByPathResponse, GetTreeStylesheetAncestorsData, GetTreeStylesheetAncestorsResponse, GetTreeStylesheetChildrenData, GetTreeStylesheetChildrenResponse, GetTreeStylesheetRootData, GetTreeStylesheetRootResponse, GetTagData, GetTagResponse, GetTelemetryData, GetTelemetryResponse, GetTelemetryLevelResponse, PostTelemetryLevelData, PostTelemetryLevelResponse, GetItemTemplateData, GetItemTemplateResponse, GetItemTemplateSearchData, GetItemTemplateSearchResponse, PostTemplateData, PostTemplateResponse, GetTemplateByIdData, GetTemplateByIdResponse, DeleteTemplateByIdData, DeleteTemplateByIdResponse, PutTemplateByIdData, PutTemplateByIdResponse, GetTemplateConfigurationResponse, PostTemplateQueryExecuteData, PostTemplateQueryExecuteResponse, GetTemplateQuerySettingsResponse, GetTreeTemplateAncestorsData, GetTreeTemplateAncestorsResponse, GetTreeTemplateChildrenData, GetTreeTemplateChildrenResponse, GetTreeTemplateRootData, GetTreeTemplateRootResponse, PostTemporaryFileData, PostTemporaryFileResponse, GetTemporaryFileByIdData, GetTemporaryFileByIdResponse, DeleteTemporaryFileByIdData, DeleteTemporaryFileByIdResponse, GetTemporaryFileConfigurationResponse, PostUpgradeAuthorizeResponse, GetUpgradeSettingsResponse, PostUserDataData, PostUserDataResponse, GetUserDataData, GetUserDataResponse, PutUserDataData, PutUserDataResponse, GetUserDataByIdData, GetUserDataByIdResponse, GetFilterUserGroupData, GetFilterUserGroupResponse, GetItemUserGroupData, GetItemUserGroupResponse, DeleteUserGroupData, DeleteUserGroupResponse, PostUserGroupData, PostUserGroupResponse, GetUserGroupData, GetUserGroupResponse, GetUserGroupByIdData, GetUserGroupByIdResponse, DeleteUserGroupByIdData, DeleteUserGroupByIdResponse, PutUserGroupByIdData, PutUserGroupByIdResponse, DeleteUserGroupByIdUsersData, DeleteUserGroupByIdUsersResponse, PostUserGroupByIdUsersData, PostUserGroupByIdUsersResponse, GetFilterUserData, GetFilterUserResponse, GetItemUserData, GetItemUserResponse, PostUserData, PostUserResponse, DeleteUserData, DeleteUserResponse, GetUserData, GetUserResponse, GetUserByIdData, GetUserByIdResponse, DeleteUserByIdData, DeleteUserByIdResponse, PutUserByIdData, PutUserByIdResponse, GetUserById2FaData, GetUserById2FaResponse, DeleteUserById2FaByProviderNameData, DeleteUserById2FaByProviderNameResponse, GetUserByIdCalculateStartNodesData, GetUserByIdCalculateStartNodesResponse, PostUserByIdChangePasswordData, PostUserByIdChangePasswordResponse, PostUserByIdResetPasswordData, PostUserByIdResetPasswordResponse, DeleteUserAvatarByIdData, DeleteUserAvatarByIdResponse, PostUserAvatarByIdData, PostUserAvatarByIdResponse, GetUserConfigurationResponse, GetUserCurrentResponse, GetUserCurrent2FaResponse, DeleteUserCurrent2FaByProviderNameData, DeleteUserCurrent2FaByProviderNameResponse, PostUserCurrent2FaByProviderNameData, PostUserCurrent2FaByProviderNameResponse, GetUserCurrent2FaByProviderNameData, GetUserCurrent2FaByProviderNameResponse, PostUserCurrentAvatarData, PostUserCurrentAvatarResponse, PostUserCurrentChangePasswordData, PostUserCurrentChangePasswordResponse, GetUserCurrentConfigurationResponse, GetUserCurrentLoginProvidersResponse, GetUserCurrentPermissionsData, GetUserCurrentPermissionsResponse, GetUserCurrentPermissionsDocumentData, GetUserCurrentPermissionsDocumentResponse, GetUserCurrentPermissionsMediaData, GetUserCurrentPermissionsMediaResponse, PostUserDisableData, PostUserDisableResponse, PostUserEnableData, PostUserEnableResponse, PostUserInviteData, PostUserInviteResponse, PostUserInviteCreatePasswordData, PostUserInviteCreatePasswordResponse, PostUserInviteResendData, PostUserInviteResendResponse, PostUserInviteVerifyData, PostUserInviteVerifyResponse, PostUserSetUserGroupsData, PostUserSetUserGroupsResponse, PostUserUnlockData, PostUserUnlockResponse, GetItemWebhookData, GetItemWebhookResponse, GetWebhookData, GetWebhookResponse, PostWebhookData, PostWebhookResponse, GetWebhookByIdData, GetWebhookByIdResponse, DeleteWebhookByIdData, DeleteWebhookByIdResponse, PutWebhookByIdData, PutWebhookByIdResponse, GetWebhookEventsData, GetWebhookEventsResponse } from './types.gen'; +import type { GetCultureData, GetCultureResponse, PostDataTypeData, PostDataTypeResponse, GetDataTypeByIdData, GetDataTypeByIdResponse, DeleteDataTypeByIdData, DeleteDataTypeByIdResponse, PutDataTypeByIdData, PutDataTypeByIdResponse, PostDataTypeByIdCopyData, PostDataTypeByIdCopyResponse, GetDataTypeByIdIsUsedData, GetDataTypeByIdIsUsedResponse, PutDataTypeByIdMoveData, PutDataTypeByIdMoveResponse, GetDataTypeByIdReferencesData, GetDataTypeByIdReferencesResponse, GetDataTypeConfigurationResponse, PostDataTypeFolderData, PostDataTypeFolderResponse, GetDataTypeFolderByIdData, GetDataTypeFolderByIdResponse, DeleteDataTypeFolderByIdData, DeleteDataTypeFolderByIdResponse, PutDataTypeFolderByIdData, PutDataTypeFolderByIdResponse, GetFilterDataTypeData, GetFilterDataTypeResponse, GetItemDataTypeData, GetItemDataTypeResponse, GetItemDataTypeSearchData, GetItemDataTypeSearchResponse, GetTreeDataTypeAncestorsData, GetTreeDataTypeAncestorsResponse, GetTreeDataTypeChildrenData, GetTreeDataTypeChildrenResponse, GetTreeDataTypeRootData, GetTreeDataTypeRootResponse, GetDictionaryData, GetDictionaryResponse, PostDictionaryData, PostDictionaryResponse, GetDictionaryByIdData, GetDictionaryByIdResponse, DeleteDictionaryByIdData, DeleteDictionaryByIdResponse, PutDictionaryByIdData, PutDictionaryByIdResponse, GetDictionaryByIdExportData, GetDictionaryByIdExportResponse, PutDictionaryByIdMoveData, PutDictionaryByIdMoveResponse, PostDictionaryImportData, PostDictionaryImportResponse, GetItemDictionaryData, GetItemDictionaryResponse, GetTreeDictionaryAncestorsData, GetTreeDictionaryAncestorsResponse, GetTreeDictionaryChildrenData, GetTreeDictionaryChildrenResponse, GetTreeDictionaryRootData, GetTreeDictionaryRootResponse, PostDocumentBlueprintData, PostDocumentBlueprintResponse, GetDocumentBlueprintByIdData, GetDocumentBlueprintByIdResponse, DeleteDocumentBlueprintByIdData, DeleteDocumentBlueprintByIdResponse, PutDocumentBlueprintByIdData, PutDocumentBlueprintByIdResponse, PutDocumentBlueprintByIdMoveData, PutDocumentBlueprintByIdMoveResponse, PostDocumentBlueprintFolderData, PostDocumentBlueprintFolderResponse, GetDocumentBlueprintFolderByIdData, GetDocumentBlueprintFolderByIdResponse, DeleteDocumentBlueprintFolderByIdData, DeleteDocumentBlueprintFolderByIdResponse, PutDocumentBlueprintFolderByIdData, PutDocumentBlueprintFolderByIdResponse, PostDocumentBlueprintFromDocumentData, PostDocumentBlueprintFromDocumentResponse, GetItemDocumentBlueprintData, GetItemDocumentBlueprintResponse, GetTreeDocumentBlueprintAncestorsData, GetTreeDocumentBlueprintAncestorsResponse, GetTreeDocumentBlueprintChildrenData, GetTreeDocumentBlueprintChildrenResponse, GetTreeDocumentBlueprintRootData, GetTreeDocumentBlueprintRootResponse, PostDocumentTypeData, PostDocumentTypeResponse, GetDocumentTypeByIdData, GetDocumentTypeByIdResponse, DeleteDocumentTypeByIdData, DeleteDocumentTypeByIdResponse, PutDocumentTypeByIdData, PutDocumentTypeByIdResponse, GetDocumentTypeByIdAllowedChildrenData, GetDocumentTypeByIdAllowedChildrenResponse, GetDocumentTypeByIdBlueprintData, GetDocumentTypeByIdBlueprintResponse, GetDocumentTypeByIdCompositionReferencesData, GetDocumentTypeByIdCompositionReferencesResponse, PostDocumentTypeByIdCopyData, PostDocumentTypeByIdCopyResponse, GetDocumentTypeByIdExportData, GetDocumentTypeByIdExportResponse, PutDocumentTypeByIdImportData, PutDocumentTypeByIdImportResponse, PutDocumentTypeByIdMoveData, PutDocumentTypeByIdMoveResponse, GetDocumentTypeAllowedAtRootData, GetDocumentTypeAllowedAtRootResponse, PostDocumentTypeAvailableCompositionsData, PostDocumentTypeAvailableCompositionsResponse, GetDocumentTypeConfigurationResponse, PostDocumentTypeFolderData, PostDocumentTypeFolderResponse, GetDocumentTypeFolderByIdData, GetDocumentTypeFolderByIdResponse, DeleteDocumentTypeFolderByIdData, DeleteDocumentTypeFolderByIdResponse, PutDocumentTypeFolderByIdData, PutDocumentTypeFolderByIdResponse, PostDocumentTypeImportData, PostDocumentTypeImportResponse, GetItemDocumentTypeData, GetItemDocumentTypeResponse, GetItemDocumentTypeSearchData, GetItemDocumentTypeSearchResponse, GetTreeDocumentTypeAncestorsData, GetTreeDocumentTypeAncestorsResponse, GetTreeDocumentTypeChildrenData, GetTreeDocumentTypeChildrenResponse, GetTreeDocumentTypeRootData, GetTreeDocumentTypeRootResponse, GetDocumentVersionData, GetDocumentVersionResponse, GetDocumentVersionByIdData, GetDocumentVersionByIdResponse, PutDocumentVersionByIdPreventCleanupData, PutDocumentVersionByIdPreventCleanupResponse, PostDocumentVersionByIdRollbackData, PostDocumentVersionByIdRollbackResponse, GetCollectionDocumentByIdData, GetCollectionDocumentByIdResponse, PostDocumentData, PostDocumentResponse, GetDocumentByIdData, GetDocumentByIdResponse, DeleteDocumentByIdData, DeleteDocumentByIdResponse, PutDocumentByIdData, PutDocumentByIdResponse, GetDocumentByIdAuditLogData, GetDocumentByIdAuditLogResponse, PostDocumentByIdCopyData, PostDocumentByIdCopyResponse, GetDocumentByIdDomainsData, GetDocumentByIdDomainsResponse, PutDocumentByIdDomainsData, PutDocumentByIdDomainsResponse, PutDocumentByIdMoveData, PutDocumentByIdMoveResponse, PutDocumentByIdMoveToRecycleBinData, PutDocumentByIdMoveToRecycleBinResponse, GetDocumentByIdNotificationsData, GetDocumentByIdNotificationsResponse, PutDocumentByIdNotificationsData, PutDocumentByIdNotificationsResponse, PostDocumentByIdPublicAccessData, PostDocumentByIdPublicAccessResponse, DeleteDocumentByIdPublicAccessData, DeleteDocumentByIdPublicAccessResponse, GetDocumentByIdPublicAccessData, GetDocumentByIdPublicAccessResponse, PutDocumentByIdPublicAccessData, PutDocumentByIdPublicAccessResponse, PutDocumentByIdPublishData, PutDocumentByIdPublishResponse, PutDocumentByIdPublishWithDescendantsData, PutDocumentByIdPublishWithDescendantsResponse, GetDocumentByIdReferencedByData, GetDocumentByIdReferencedByResponse, GetDocumentByIdReferencedDescendantsData, GetDocumentByIdReferencedDescendantsResponse, PutDocumentByIdUnpublishData, PutDocumentByIdUnpublishResponse, PutDocumentByIdValidateData, PutDocumentByIdValidateResponse, GetDocumentAreReferencedData, GetDocumentAreReferencedResponse, GetDocumentConfigurationResponse, PutDocumentSortData, PutDocumentSortResponse, GetDocumentUrlsData, GetDocumentUrlsResponse, PostDocumentValidateData, PostDocumentValidateResponse, GetItemDocumentData, GetItemDocumentResponse, GetItemDocumentSearchData, GetItemDocumentSearchResponse, DeleteRecycleBinDocumentResponse, DeleteRecycleBinDocumentByIdData, DeleteRecycleBinDocumentByIdResponse, GetRecycleBinDocumentByIdOriginalParentData, GetRecycleBinDocumentByIdOriginalParentResponse, PutRecycleBinDocumentByIdRestoreData, PutRecycleBinDocumentByIdRestoreResponse, GetRecycleBinDocumentChildrenData, GetRecycleBinDocumentChildrenResponse, GetRecycleBinDocumentRootData, GetRecycleBinDocumentRootResponse, GetTreeDocumentAncestorsData, GetTreeDocumentAncestorsResponse, GetTreeDocumentChildrenData, GetTreeDocumentChildrenResponse, GetTreeDocumentRootData, GetTreeDocumentRootResponse, PostDynamicRootQueryData, PostDynamicRootQueryResponse, GetDynamicRootStepsResponse, GetHealthCheckGroupData, GetHealthCheckGroupResponse, GetHealthCheckGroupByNameData, GetHealthCheckGroupByNameResponse, PostHealthCheckGroupByNameCheckData, PostHealthCheckGroupByNameCheckResponse, PostHealthCheckExecuteActionData, PostHealthCheckExecuteActionResponse, GetHelpData, GetHelpResponse, GetImagingResizeUrlsData, GetImagingResizeUrlsResponse, GetImportAnalyzeData, GetImportAnalyzeResponse, GetIndexerData, GetIndexerResponse, GetIndexerByIndexNameData, GetIndexerByIndexNameResponse, PostIndexerByIndexNameRebuildData, PostIndexerByIndexNameRebuildResponse, GetInstallSettingsResponse, PostInstallSetupData, PostInstallSetupResponse, PostInstallValidateDatabaseData, PostInstallValidateDatabaseResponse, GetItemLanguageData, GetItemLanguageResponse, GetItemLanguageDefaultResponse, GetLanguageData, GetLanguageResponse, PostLanguageData, PostLanguageResponse, GetLanguageByIsoCodeData, GetLanguageByIsoCodeResponse, DeleteLanguageByIsoCodeData, DeleteLanguageByIsoCodeResponse, PutLanguageByIsoCodeData, PutLanguageByIsoCodeResponse, GetLogViewerLevelData, GetLogViewerLevelResponse, GetLogViewerLevelCountData, GetLogViewerLevelCountResponse, GetLogViewerLogData, GetLogViewerLogResponse, GetLogViewerMessageTemplateData, GetLogViewerMessageTemplateResponse, GetLogViewerSavedSearchData, GetLogViewerSavedSearchResponse, PostLogViewerSavedSearchData, PostLogViewerSavedSearchResponse, GetLogViewerSavedSearchByNameData, GetLogViewerSavedSearchByNameResponse, DeleteLogViewerSavedSearchByNameData, DeleteLogViewerSavedSearchByNameResponse, GetLogViewerValidateLogsSizeData, GetLogViewerValidateLogsSizeResponse, GetManifestManifestResponse, GetManifestManifestPrivateResponse, GetManifestManifestPublicResponse, GetItemMediaTypeData, GetItemMediaTypeResponse, GetItemMediaTypeAllowedData, GetItemMediaTypeAllowedResponse, GetItemMediaTypeFoldersData, GetItemMediaTypeFoldersResponse, GetItemMediaTypeSearchData, GetItemMediaTypeSearchResponse, PostMediaTypeData, PostMediaTypeResponse, GetMediaTypeByIdData, GetMediaTypeByIdResponse, DeleteMediaTypeByIdData, DeleteMediaTypeByIdResponse, PutMediaTypeByIdData, PutMediaTypeByIdResponse, GetMediaTypeByIdAllowedChildrenData, GetMediaTypeByIdAllowedChildrenResponse, GetMediaTypeByIdCompositionReferencesData, GetMediaTypeByIdCompositionReferencesResponse, PostMediaTypeByIdCopyData, PostMediaTypeByIdCopyResponse, GetMediaTypeByIdExportData, GetMediaTypeByIdExportResponse, PutMediaTypeByIdImportData, PutMediaTypeByIdImportResponse, PutMediaTypeByIdMoveData, PutMediaTypeByIdMoveResponse, GetMediaTypeAllowedAtRootData, GetMediaTypeAllowedAtRootResponse, PostMediaTypeAvailableCompositionsData, PostMediaTypeAvailableCompositionsResponse, GetMediaTypeConfigurationResponse, PostMediaTypeFolderData, PostMediaTypeFolderResponse, GetMediaTypeFolderByIdData, GetMediaTypeFolderByIdResponse, DeleteMediaTypeFolderByIdData, DeleteMediaTypeFolderByIdResponse, PutMediaTypeFolderByIdData, PutMediaTypeFolderByIdResponse, PostMediaTypeImportData, PostMediaTypeImportResponse, GetTreeMediaTypeAncestorsData, GetTreeMediaTypeAncestorsResponse, GetTreeMediaTypeChildrenData, GetTreeMediaTypeChildrenResponse, GetTreeMediaTypeRootData, GetTreeMediaTypeRootResponse, GetCollectionMediaData, GetCollectionMediaResponse, GetItemMediaData, GetItemMediaResponse, GetItemMediaSearchData, GetItemMediaSearchResponse, PostMediaData, PostMediaResponse, GetMediaByIdData, GetMediaByIdResponse, DeleteMediaByIdData, DeleteMediaByIdResponse, PutMediaByIdData, PutMediaByIdResponse, GetMediaByIdAuditLogData, GetMediaByIdAuditLogResponse, PutMediaByIdMoveData, PutMediaByIdMoveResponse, PutMediaByIdMoveToRecycleBinData, PutMediaByIdMoveToRecycleBinResponse, GetMediaByIdReferencedByData, GetMediaByIdReferencedByResponse, GetMediaByIdReferencedDescendantsData, GetMediaByIdReferencedDescendantsResponse, PutMediaByIdValidateData, PutMediaByIdValidateResponse, GetMediaAreReferencedData, GetMediaAreReferencedResponse, GetMediaConfigurationResponse, PutMediaSortData, PutMediaSortResponse, GetMediaUrlsData, GetMediaUrlsResponse, PostMediaValidateData, PostMediaValidateResponse, DeleteRecycleBinMediaResponse, DeleteRecycleBinMediaByIdData, DeleteRecycleBinMediaByIdResponse, GetRecycleBinMediaByIdOriginalParentData, GetRecycleBinMediaByIdOriginalParentResponse, PutRecycleBinMediaByIdRestoreData, PutRecycleBinMediaByIdRestoreResponse, GetRecycleBinMediaChildrenData, GetRecycleBinMediaChildrenResponse, GetRecycleBinMediaRootData, GetRecycleBinMediaRootResponse, GetTreeMediaAncestorsData, GetTreeMediaAncestorsResponse, GetTreeMediaChildrenData, GetTreeMediaChildrenResponse, GetTreeMediaRootData, GetTreeMediaRootResponse, GetItemMemberGroupData, GetItemMemberGroupResponse, GetMemberGroupData, GetMemberGroupResponse, PostMemberGroupData, PostMemberGroupResponse, GetMemberGroupByIdData, GetMemberGroupByIdResponse, DeleteMemberGroupByIdData, DeleteMemberGroupByIdResponse, PutMemberGroupByIdData, PutMemberGroupByIdResponse, GetTreeMemberGroupRootData, GetTreeMemberGroupRootResponse, GetItemMemberTypeData, GetItemMemberTypeResponse, GetItemMemberTypeSearchData, GetItemMemberTypeSearchResponse, PostMemberTypeData, PostMemberTypeResponse, GetMemberTypeByIdData, GetMemberTypeByIdResponse, DeleteMemberTypeByIdData, DeleteMemberTypeByIdResponse, PutMemberTypeByIdData, PutMemberTypeByIdResponse, GetMemberTypeByIdCompositionReferencesData, GetMemberTypeByIdCompositionReferencesResponse, PostMemberTypeByIdCopyData, PostMemberTypeByIdCopyResponse, PostMemberTypeAvailableCompositionsData, PostMemberTypeAvailableCompositionsResponse, GetMemberTypeConfigurationResponse, GetTreeMemberTypeRootData, GetTreeMemberTypeRootResponse, GetFilterMemberData, GetFilterMemberResponse, GetItemMemberData, GetItemMemberResponse, GetItemMemberSearchData, GetItemMemberSearchResponse, PostMemberData, PostMemberResponse, GetMemberByIdData, GetMemberByIdResponse, DeleteMemberByIdData, DeleteMemberByIdResponse, PutMemberByIdData, PutMemberByIdResponse, PutMemberByIdValidateData, PutMemberByIdValidateResponse, GetMemberConfigurationResponse, PostMemberValidateData, PostMemberValidateResponse, PostModelsBuilderBuildResponse, GetModelsBuilderDashboardResponse, GetModelsBuilderStatusResponse, GetObjectTypesData, GetObjectTypesResponse, GetOembedQueryData, GetOembedQueryResponse, PostPackageByNameRunMigrationData, PostPackageByNameRunMigrationResponse, GetPackageConfigurationResponse, GetPackageCreatedData, GetPackageCreatedResponse, PostPackageCreatedData, PostPackageCreatedResponse, GetPackageCreatedByIdData, GetPackageCreatedByIdResponse, DeletePackageCreatedByIdData, DeletePackageCreatedByIdResponse, PutPackageCreatedByIdData, PutPackageCreatedByIdResponse, GetPackageCreatedByIdDownloadData, GetPackageCreatedByIdDownloadResponse, GetPackageMigrationStatusData, GetPackageMigrationStatusResponse, GetItemPartialViewData, GetItemPartialViewResponse, PostPartialViewData, PostPartialViewResponse, GetPartialViewByPathData, GetPartialViewByPathResponse, DeletePartialViewByPathData, DeletePartialViewByPathResponse, PutPartialViewByPathData, PutPartialViewByPathResponse, PutPartialViewByPathRenameData, PutPartialViewByPathRenameResponse, PostPartialViewFolderData, PostPartialViewFolderResponse, GetPartialViewFolderByPathData, GetPartialViewFolderByPathResponse, DeletePartialViewFolderByPathData, DeletePartialViewFolderByPathResponse, GetPartialViewSnippetData, GetPartialViewSnippetResponse, GetPartialViewSnippetByIdData, GetPartialViewSnippetByIdResponse, GetTreePartialViewAncestorsData, GetTreePartialViewAncestorsResponse, GetTreePartialViewChildrenData, GetTreePartialViewChildrenResponse, GetTreePartialViewRootData, GetTreePartialViewRootResponse, DeletePreviewResponse, PostPreviewResponse, GetProfilingStatusResponse, PutProfilingStatusData, PutProfilingStatusResponse, GetPropertyTypeIsUsedData, GetPropertyTypeIsUsedResponse, PostPublishedCacheCollectResponse, PostPublishedCacheRebuildResponse, PostPublishedCacheReloadResponse, GetPublishedCacheStatusResponse, GetRedirectManagementData, GetRedirectManagementResponse, GetRedirectManagementByIdData, GetRedirectManagementByIdResponse, DeleteRedirectManagementByIdData, DeleteRedirectManagementByIdResponse, GetRedirectManagementStatusResponse, PostRedirectManagementStatusData, PostRedirectManagementStatusResponse, GetItemRelationTypeData, GetItemRelationTypeResponse, GetRelationTypeData, GetRelationTypeResponse, GetRelationTypeByIdData, GetRelationTypeByIdResponse, GetRelationByRelationTypeIdData, GetRelationByRelationTypeIdResponse, GetItemScriptData, GetItemScriptResponse, PostScriptData, PostScriptResponse, GetScriptByPathData, GetScriptByPathResponse, DeleteScriptByPathData, DeleteScriptByPathResponse, PutScriptByPathData, PutScriptByPathResponse, PutScriptByPathRenameData, PutScriptByPathRenameResponse, PostScriptFolderData, PostScriptFolderResponse, GetScriptFolderByPathData, GetScriptFolderByPathResponse, DeleteScriptFolderByPathData, DeleteScriptFolderByPathResponse, GetTreeScriptAncestorsData, GetTreeScriptAncestorsResponse, GetTreeScriptChildrenData, GetTreeScriptChildrenResponse, GetTreeScriptRootData, GetTreeScriptRootResponse, GetSearcherData, GetSearcherResponse, GetSearcherBySearcherNameQueryData, GetSearcherBySearcherNameQueryResponse, GetSecurityConfigurationResponse, PostSecurityForgotPasswordData, PostSecurityForgotPasswordResponse, PostSecurityForgotPasswordResetData, PostSecurityForgotPasswordResetResponse, PostSecurityForgotPasswordVerifyData, PostSecurityForgotPasswordVerifyResponse, GetSegmentData, GetSegmentResponse, GetServerConfigurationResponse, GetServerInformationResponse, GetServerStatusResponse, GetServerTroubleshootingResponse, GetItemStaticFileData, GetItemStaticFileResponse, GetTreeStaticFileAncestorsData, GetTreeStaticFileAncestorsResponse, GetTreeStaticFileChildrenData, GetTreeStaticFileChildrenResponse, GetTreeStaticFileRootData, GetTreeStaticFileRootResponse, GetItemStylesheetData, GetItemStylesheetResponse, PostStylesheetData, PostStylesheetResponse, GetStylesheetByPathData, GetStylesheetByPathResponse, DeleteStylesheetByPathData, DeleteStylesheetByPathResponse, PutStylesheetByPathData, PutStylesheetByPathResponse, PutStylesheetByPathRenameData, PutStylesheetByPathRenameResponse, PostStylesheetFolderData, PostStylesheetFolderResponse, GetStylesheetFolderByPathData, GetStylesheetFolderByPathResponse, DeleteStylesheetFolderByPathData, DeleteStylesheetFolderByPathResponse, GetTreeStylesheetAncestorsData, GetTreeStylesheetAncestorsResponse, GetTreeStylesheetChildrenData, GetTreeStylesheetChildrenResponse, GetTreeStylesheetRootData, GetTreeStylesheetRootResponse, GetTagData, GetTagResponse, GetTelemetryData, GetTelemetryResponse, GetTelemetryLevelResponse, PostTelemetryLevelData, PostTelemetryLevelResponse, GetItemTemplateData, GetItemTemplateResponse, GetItemTemplateSearchData, GetItemTemplateSearchResponse, PostTemplateData, PostTemplateResponse, GetTemplateByIdData, GetTemplateByIdResponse, DeleteTemplateByIdData, DeleteTemplateByIdResponse, PutTemplateByIdData, PutTemplateByIdResponse, GetTemplateConfigurationResponse, PostTemplateQueryExecuteData, PostTemplateQueryExecuteResponse, GetTemplateQuerySettingsResponse, GetTreeTemplateAncestorsData, GetTreeTemplateAncestorsResponse, GetTreeTemplateChildrenData, GetTreeTemplateChildrenResponse, GetTreeTemplateRootData, GetTreeTemplateRootResponse, PostTemporaryFileData, PostTemporaryFileResponse, GetTemporaryFileByIdData, GetTemporaryFileByIdResponse, DeleteTemporaryFileByIdData, DeleteTemporaryFileByIdResponse, GetTemporaryFileConfigurationResponse, PostUpgradeAuthorizeResponse, GetUpgradeSettingsResponse, PostUserDataData, PostUserDataResponse, GetUserDataData, GetUserDataResponse, PutUserDataData, PutUserDataResponse, GetUserDataByIdData, GetUserDataByIdResponse, GetFilterUserGroupData, GetFilterUserGroupResponse, GetItemUserGroupData, GetItemUserGroupResponse, DeleteUserGroupData, DeleteUserGroupResponse, PostUserGroupData, PostUserGroupResponse, GetUserGroupData, GetUserGroupResponse, GetUserGroupByIdData, GetUserGroupByIdResponse, DeleteUserGroupByIdData, DeleteUserGroupByIdResponse, PutUserGroupByIdData, PutUserGroupByIdResponse, DeleteUserGroupByIdUsersData, DeleteUserGroupByIdUsersResponse, PostUserGroupByIdUsersData, PostUserGroupByIdUsersResponse, GetFilterUserData, GetFilterUserResponse, GetItemUserData, GetItemUserResponse, PostUserData, PostUserResponse, DeleteUserData, DeleteUserResponse, GetUserData, GetUserResponse, GetUserByIdData, GetUserByIdResponse, DeleteUserByIdData, DeleteUserByIdResponse, PutUserByIdData, PutUserByIdResponse, GetUserById2FaData, GetUserById2FaResponse, DeleteUserById2FaByProviderNameData, DeleteUserById2FaByProviderNameResponse, GetUserByIdCalculateStartNodesData, GetUserByIdCalculateStartNodesResponse, PostUserByIdChangePasswordData, PostUserByIdChangePasswordResponse, PostUserByIdResetPasswordData, PostUserByIdResetPasswordResponse, DeleteUserAvatarByIdData, DeleteUserAvatarByIdResponse, PostUserAvatarByIdData, PostUserAvatarByIdResponse, GetUserConfigurationResponse, GetUserCurrentResponse, GetUserCurrent2FaResponse, DeleteUserCurrent2FaByProviderNameData, DeleteUserCurrent2FaByProviderNameResponse, PostUserCurrent2FaByProviderNameData, PostUserCurrent2FaByProviderNameResponse, GetUserCurrent2FaByProviderNameData, GetUserCurrent2FaByProviderNameResponse, PostUserCurrentAvatarData, PostUserCurrentAvatarResponse, PostUserCurrentChangePasswordData, PostUserCurrentChangePasswordResponse, GetUserCurrentConfigurationResponse, GetUserCurrentLoginProvidersResponse, GetUserCurrentPermissionsData, GetUserCurrentPermissionsResponse, GetUserCurrentPermissionsDocumentData, GetUserCurrentPermissionsDocumentResponse, GetUserCurrentPermissionsMediaData, GetUserCurrentPermissionsMediaResponse, PostUserDisableData, PostUserDisableResponse, PostUserEnableData, PostUserEnableResponse, PostUserInviteData, PostUserInviteResponse, PostUserInviteCreatePasswordData, PostUserInviteCreatePasswordResponse, PostUserInviteResendData, PostUserInviteResendResponse, PostUserInviteVerifyData, PostUserInviteVerifyResponse, PostUserSetUserGroupsData, PostUserSetUserGroupsResponse, PostUserUnlockData, PostUserUnlockResponse, GetItemWebhookData, GetItemWebhookResponse, GetWebhookData, GetWebhookResponse, PostWebhookData, PostWebhookResponse, GetWebhookByIdData, GetWebhookByIdResponse, DeleteWebhookByIdData, DeleteWebhookByIdResponse, PutWebhookByIdData, PutWebhookByIdResponse, GetWebhookEventsData, GetWebhookEventsResponse } from './types.gen'; export class CultureService { /** @@ -3754,6 +3754,21 @@ export class MediaTypeService { }); } + /** + * @returns unknown OK + * @throws ApiError + */ + public static getMediaTypeConfiguration(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/umbraco/management/api/v1/media-type/configuration', + errors: { + 401: 'The resource is protected and requires an authentication token', + 403: 'The authenticated user do not have access to this resource' + } + }); + } + /** * @param data The data for the request. * @param data.requestBody @@ -4929,6 +4944,21 @@ export class MemberTypeService { }); } + /** + * @returns unknown OK + * @throws ApiError + */ + public static getMemberTypeConfiguration(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/umbraco/management/api/v1/member-type/configuration', + errors: { + 401: 'The resource is protected and requires an authentication token', + 403: 'The authenticated user do not have access to this resource' + } + }); + } + /** * @param data The data for the request. * @param data.skip diff --git a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/types.gen.ts b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/types.gen.ts index 12f84d2636..2d29b8b87e 100644 --- a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/types.gen.ts +++ b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/types.gen.ts @@ -438,6 +438,9 @@ export type CultureReponseModel = { export type CurrenUserConfigurationResponseModel = { keepUserLoggedIn: boolean; + /** + * @deprecated + */ usernameIsEmail: boolean; passwordConfiguration: PasswordConfigurationResponseModel; }; @@ -633,6 +636,9 @@ export type DocumentConfigurationResponseModel = { disableUnpublishWhenReferenced: boolean; allowEditInvariantFromNonDefault: boolean; allowNonExistingSegmentsCreation: boolean; + /** + * @deprecated + */ reservedFieldNames: Array<(string)>; }; @@ -730,6 +736,7 @@ export type DocumentTypeConfigurationResponseModel = { dataTypesCanBeChanged: DataTypeChangeModeModel; disableTemplates: boolean; useSegments: boolean; + reservedFieldNames: Array<(string)>; }; export type DocumentTypeItemResponseModel = { @@ -1153,6 +1160,9 @@ export type MediaCollectionResponseModel = { export type MediaConfigurationResponseModel = { disableDeleteWhenReferenced: boolean; disableUnpublishWhenReferenced: boolean; + /** + * @deprecated + */ reservedFieldNames: Array<(string)>; }; @@ -1219,6 +1229,10 @@ export type MediaTypeCompositionResponseModel = { icon: string; }; +export type MediaTypeConfigurationResponseModel = { + reservedFieldNames: Array<(string)>; +}; + export type MediaTypeItemResponseModel = { id: string; name: string; @@ -1319,6 +1333,9 @@ export type MediaVariantResponseModel = { }; export type MemberConfigurationResponseModel = { + /** + * @deprecated + */ reservedFieldNames: Array<(string)>; }; @@ -1372,6 +1389,10 @@ export type MemberTypeCompositionResponseModel = { icon: string; }; +export type MemberTypeConfigurationResponseModel = { + reservedFieldNames: Array<(string)>; +}; + export type MemberTypeItemResponseModel = { id: string; name: string; @@ -2597,6 +2618,7 @@ export type UpgradeSettingsResponseModel = { export type UserConfigurationResponseModel = { canInviteUsers: boolean; + usernameIsEmail: boolean; passwordConfiguration: PasswordConfigurationResponseModel; }; @@ -3850,6 +3872,8 @@ export type PostMediaTypeAvailableCompositionsData = { export type PostMediaTypeAvailableCompositionsResponse = Array<(AvailableMediaTypeCompositionResponseModel)>; +export type GetMediaTypeConfigurationResponse = MediaTypeConfigurationResponseModel; + export type PostMediaTypeFolderData = { requestBody?: CreateFolderRequestModel; }; @@ -4190,6 +4214,8 @@ export type PostMemberTypeAvailableCompositionsData = { export type PostMemberTypeAvailableCompositionsResponse = Array<(AvailableMemberTypeCompositionResponseModel)>; +export type GetMemberTypeConfigurationResponse = MemberTypeConfigurationResponseModel; + export type GetTreeMemberTypeRootData = { skip?: number; take?: number; @@ -8784,6 +8810,24 @@ export type $OpenApiTs = { }; }; }; + '/umbraco/management/api/v1/media-type/configuration': { + get: { + res: { + /** + * OK + */ + 200: MediaTypeConfigurationResponseModel; + /** + * The resource is protected and requires an authentication token + */ + 401: unknown; + /** + * The authenticated user do not have access to this resource + */ + 403: unknown; + }; + }; + }; '/umbraco/management/api/v1/media-type/folder': { post: { req: PostMediaTypeFolderData; @@ -9913,6 +9957,24 @@ export type $OpenApiTs = { }; }; }; + '/umbraco/management/api/v1/member-type/configuration': { + get: { + res: { + /** + * OK + */ + 200: MemberTypeConfigurationResponseModel; + /** + * The resource is protected and requires an authentication token + */ + 401: unknown; + /** + * The authenticated user do not have access to this resource + */ + 403: unknown; + }; + }; + }; '/umbraco/management/api/v1/tree/member-type/root': { get: { req: GetTreeMemberTypeRootData; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.controller.ts index 6b122d0311..4893f340f9 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.controller.ts @@ -33,7 +33,7 @@ export abstract class UmbBaseExtensionsInitializer< #onChange?: (permittedManifests: Array) => void; protected _extensions: Array = []; #permittedExts: Array = []; - #exposedPermittedExts: Array = []; + #exposedPermittedExts?: Array; #changeDebounce?: number; asPromise(): Promise { @@ -90,6 +90,12 @@ export abstract class UmbBaseExtensionsInitializer< return; } + // If we get no manifests and we have not exposed any extensions yet, then we should notify to let the listener know that we have our first response. [NL] + if (manifests.length === 0 && this.#exposedPermittedExts === undefined) { + this.#exposedPermittedExts = []; + this.#onChange?.(this.#exposedPermittedExts); + } + // Clean up extensions that are no longer. this._extensions = this._extensions.filter((extension) => { if (!manifests.find((manifest) => manifest.alias === extension.alias)) { @@ -114,6 +120,7 @@ export abstract class UmbBaseExtensionsInitializer< protected _extensionChanged = (isPermitted: boolean, controller: ControllerType) => { let hasChanged = false; + // This might be called after this is destroyed, so we need to check if the _permittedExts is still available: const existingIndex = this.#permittedExts?.indexOf(controller as unknown as MyPermittedControllerType); if (isPermitted) { @@ -149,7 +156,7 @@ export abstract class UmbBaseExtensionsInitializer< // if so, look up the extension it overwrites, and remove it from the list. and check that for if it overwrites another extension and so on. if (extCtrl.overwrites.length > 0) { extCtrl.overwrites.forEach((overwrite) => { - this.#removeOverwrittenExtensions(this.#exposedPermittedExts, overwrite); + this.#removeOverwrittenExtensions(this.#exposedPermittedExts!, overwrite); }); } }); @@ -193,16 +200,16 @@ export abstract class UmbBaseExtensionsInitializer< // The this.#extensionRegistry is an indication of wether this is already destroyed. if (!this.#extensionRegistry) return; - const oldPermittedExtsLength = this.#exposedPermittedExts.length; + const oldPermittedExtsLength = this.#exposedPermittedExts?.length ?? 0; (this._extensions as any) = undefined; (this.#permittedExts as any) = undefined; - this.#exposedPermittedExts.length = 0; + this.#exposedPermittedExts = undefined; if (this.#changeDebounce) { cancelAnimationFrame(this.#changeDebounce); this.#changeDebounce = undefined; } if (oldPermittedExtsLength > 0) { - this.#onChange?.(this.#exposedPermittedExts); + this.#onChange?.([]); } this.#promiseResolvers.length = 0; this.#filter = undefined; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-block-inline.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-block-inline.element.ts index 81cb697ccc..08948600e1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-block-inline.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block-inline/block-grid-block-inline.element.ts @@ -3,7 +3,7 @@ import { UmbBlockGridInlinePropertyDatasetContext } from './block-grid-inline-pr import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import type { UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type'; -import type { UmbBlockViewUrlsPropType } from '@umbraco-cms/backoffice/block'; +import type { UmbBlockEditorCustomViewConfiguration } from '@umbraco-cms/backoffice/extension-registry'; import '../block-grid-areas-container/index.js'; import '../ref-grid-block/index.js'; @@ -17,7 +17,7 @@ export class UmbBlockGridBlockInlineElement extends UmbLitElement { label?: string; @property({ attribute: false }) - urls?: UmbBlockViewUrlsPropType; + config?: UmbBlockEditorCustomViewConfiguration; @state() _inlineProperty: UmbPropertyTypeModel | undefined; @@ -39,7 +39,7 @@ export class UmbBlockGridBlockInlineElement extends UmbLitElement { } override render() { - return html` + return html` diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block/block-grid-block.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block/block-grid-block.element.ts index 78f3e3e48b..67cd1c81e7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block/block-grid-block.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-block/block-grid-block.element.ts @@ -1,7 +1,8 @@ import { UMB_BLOCK_GRID_ENTRY_CONTEXT } from '../../context/block-grid-entry.context-token.js'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; -import type { UmbBlockDataType, UmbBlockViewUrlsPropType } from '@umbraco-cms/backoffice/block'; +import type { UmbBlockEditorCustomViewConfiguration } from '@umbraco-cms/backoffice/extension-registry'; +import type { UmbBlockDataType } from '@umbraco-cms/backoffice/block'; import '@umbraco-cms/backoffice/ufm'; import '../block-grid-areas-container/index.js'; @@ -17,7 +18,7 @@ export class UmbBlockGridBlockElement extends UmbLitElement { label?: string; @property({ attribute: false }) - urls?: UmbBlockViewUrlsPropType; + config?: UmbBlockEditorCustomViewConfiguration; @state() _content?: UmbBlockDataType; @@ -37,7 +38,7 @@ export class UmbBlockGridBlockElement extends UmbLitElement { } override render() { - return html` + return html` `; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts index 0e86d455e8..4778221731 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts @@ -2,13 +2,17 @@ import { UmbBlockGridEntryContext } from '../../context/block-grid-entry.context import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { html, css, customElement, property, state, nothing } from '@umbraco-cms/backoffice/external/lit'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; -import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; -import type { UmbBlockViewPropsType } from '@umbraco-cms/backoffice/block'; -import type { UmbBlockGridLayoutModel } from '@umbraco-cms/backoffice/block-grid'; +import type { + ManifestBlockEditorCustomView, + UmbBlockEditorCustomViewProperties, + UmbPropertyEditorUiElement, +} from '@umbraco-cms/backoffice/extension-registry'; +import { stringOrStringArrayContains } from '@umbraco-cms/backoffice/utils'; +import { UMB_BLOCK_GRID, type UmbBlockGridLayoutModel } from '@umbraco-cms/backoffice/block-grid'; + import '../block-grid-block-inline/index.js'; import '../block-grid-block/index.js'; import '../block-scale-handler/index.js'; - /** * @element umb-block-grid-entry */ @@ -40,6 +44,9 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper #context = new UmbBlockGridEntryContext(this); #renderTimeout: number | undefined; + @state() + _contentTypeAlias?: string; + @state() _columnSpan?: number; @@ -82,11 +89,13 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper @state() _inlineCreateAboveWidth?: string; - // TODO: use this type on the Element Interface for the Manifest. @state() - _blockViewProps: UmbBlockViewPropsType = { contentUdi: undefined!, urls: {} }; // Set to undefined cause it will be set before we render. + _blockViewProps: UmbBlockEditorCustomViewProperties = { + contentUdi: undefined!, + config: { showContentEdit: false, showSettingsEdit: false }, + }; // Set to undefined cause it will be set before we render. - #updateBlockViewProps(incoming: Partial>) { + #updateBlockViewProps(incoming: Partial>) { this._blockViewProps = { ...this._blockViewProps, ...incoming }; this.requestUpdate('_blockViewProps'); } @@ -95,57 +104,119 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper super(); // Misc: - this.observe(this.#context.showContentEdit, (show) => { - this._showContentEdit = show; - }); - this.observe(this.#context.settingsElementTypeKey, (key) => { - this._hasSettings = !!key; - }); - this.observe(this.#context.canScale, (canScale) => { - this._canScale = canScale; - }); - this.observe(this.#context.label, (label) => { - this.#updateBlockViewProps({ label }); - this._label = label; - }); - this.observe(this.#context.contentElementTypeIcon, (icon) => { - this.#updateBlockViewProps({ icon }); - this._icon = icon; - }); - this.observe(this.#context.inlineEditingMode, (mode) => { - this._inlineEditingMode = mode; - }); + this.observe( + this.#context.showContentEdit, + (showContentEdit) => { + this._showContentEdit = showContentEdit; + this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, showContentEdit } }); + }, + null, + ); + this.observe( + this.#context.settingsElementTypeKey, + (key) => { + this._hasSettings = !!key; + this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, showSettingsEdit: !!key } }); + }, + null, + ); + this.observe( + this.#context.canScale, + (canScale) => { + this._canScale = canScale; + }, + null, + ); + this.observe( + this.#context.blockType, + (blockType) => { + this.#updateBlockViewProps({ blockType }); + }, + null, + ); + // TODO: Implement index. + this.observe( + this.#context.label, + (label) => { + this.#updateBlockViewProps({ label }); + this._label = label; + }, + null, + ); + this.observe( + this.#context.contentElementTypeIcon, + (icon) => { + this.#updateBlockViewProps({ icon }); + this._icon = icon; + }, + null, + ); + this.observe( + this.#context.inlineEditingMode, + (mode) => { + this._inlineEditingMode = mode; + }, + null, + ); // Data: - this.observe(this.#context.layout, (layout) => { - this.#updateBlockViewProps({ layout }); - }); - this.observe(this.#context.content, (content) => { - this.#updateBlockViewProps({ content }); - }); - this.observe(this.#context.settings, (settings) => { - this.#updateBlockViewProps({ settings }); - }); + this.observe( + this.#context.layout, + (layout) => { + this.#updateBlockViewProps({ layout }); + }, + null, + ); + this.observe( + this.#context.content, + (content) => { + this.#updateBlockViewProps({ content }); + }, + null, + ); + this.observe( + this.#context.settings, + (settings) => { + this.#updateBlockViewProps({ settings }); + }, + null, + ); // Paths: - this.observe(this.#context.createBeforePath, (createPath) => { - //const oldValue = this._createBeforePath; - this._createBeforePath = createPath; - //this.requestUpdate('_createPath', oldValue); - }); - this.observe(this.#context.createAfterPath, (createPath) => { - //const oldValue = this._createAfterPath; - this._createAfterPath = createPath; - //this.requestUpdate('_createPath', oldValue); - }); - this.observe(this.#context.workspaceEditContentPath, (path) => { - this._workspaceEditContentPath = path; - this.#updateBlockViewProps({ urls: { ...this._blockViewProps.urls, editContent: path } }); - }); - this.observe(this.#context.workspaceEditSettingsPath, (path) => { - this._workspaceEditSettingsPath = path; - this.#updateBlockViewProps({ urls: { ...this._blockViewProps.urls, editSettings: path } }); - }); + this.observe( + this.#context.createBeforePath, + (createPath) => { + //const oldValue = this._createBeforePath; + this._createBeforePath = createPath; + //this.requestUpdate('_createPath', oldValue); + }, + null, + ); + this.observe( + this.#context.createAfterPath, + (createPath) => { + //const oldValue = this._createAfterPath; + this._createAfterPath = createPath; + //this.requestUpdate('_createPath', oldValue); + }, + null, + ); + this.observe( + this.#context.workspaceEditContentPath, + (path) => { + this._workspaceEditContentPath = path; + this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, editContentPath: path } }); + }, + null, + ); + this.observe( + this.#context.workspaceEditSettingsPath, + (path) => { + this._workspaceEditSettingsPath = path; + this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, editSettingsPath: path } }); + }, + null, + ); } override connectedCallback(): void { @@ -169,16 +240,25 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper }, 'rowSpan', ); - this.observe(this.#context.contentElementTypeKey, (contentElementTypeKey) => { - if (contentElementTypeKey) { - this.setAttribute('data-content-element-type-key', contentElementTypeKey); - } - }); - this.observe(this.#context.contentElementTypeAlias, (contentElementTypeAlias) => { - if (contentElementTypeAlias) { - this.setAttribute('data-content-element-type-alias', contentElementTypeAlias); - } - }); + this.observe( + this.#context.contentElementTypeKey, + (contentElementTypeKey) => { + if (contentElementTypeKey) { + this.setAttribute('data-content-element-type-key', contentElementTypeKey); + } + }, + 'contentElementTypeKey', + ); + this.observe( + this.#context.contentElementTypeAlias, + (contentElementTypeAlias) => { + if (contentElementTypeAlias) { + this._contentTypeAlias = contentElementTypeAlias; + this.setAttribute('data-content-element-type-alias', contentElementTypeAlias); + } + }, + 'contentElementTypeAlias', + ); this.#callUpdateInlineCreateButtons(); } @@ -225,18 +305,29 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper } }; + #extensionSlotFilterMethod = (manifest: ManifestBlockEditorCustomView) => { + if ( + manifest.forContentTypeAlias && + !stringOrStringArrayContains(manifest.forContentTypeAlias, this._contentTypeAlias!) + ) { + return false; + } + if (manifest.forBlockEditor && !stringOrStringArrayContains(manifest.forBlockEditor, UMB_BLOCK_GRID)) { + return false; + } + return true; + }; + #renderInlineEditBlock() { - return html``; + return html``; } #renderRefBlock() { - return html``; + return html``; } #renderBlock() { - return this.contentUdi + return this.contentUdi && this._contentTypeAlias ? html` ${this._createBeforePath && this._showInlineCreateBefore ? html`${this._inlineEditingMode ? this.#renderInlineEditBlock() : this.#renderRefBlock()} @@ -347,10 +439,6 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper border-color: var(--uui-color-interactive); } - uui-action-bar { - background-color: var(--uui-color-surface); - } - .umb-block-grid__block { height: 100%; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/types.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/types.ts index 7925df893a..392c67f0f2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/types.ts @@ -2,6 +2,7 @@ import type { UmbBlockLayoutBaseModel, UmbBlockValueType } from '@umbraco-cms/ba import type { UmbBlockTypeWithGroupKey } from '@umbraco-cms/backoffice/block-type'; export const UMB_BLOCK_GRID_TYPE = 'block-grid-type'; +export const UMB_BLOCK_GRID = 'block-grid'; // Configuration models: export interface UmbBlockGridTypeModel extends UmbBlockTypeWithGroupKey { diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts index e7bc5a771f..4d8764fec6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts @@ -1,11 +1,15 @@ -import { UmbBlockListEntryContext } from '../../context/block-list-entry.context.js'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { html, css, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; -import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; +import type { + ManifestBlockEditorCustomView, + UmbBlockEditorCustomViewProperties, + UmbPropertyEditorUiElement, +} from '@umbraco-cms/backoffice/extension-registry'; import '../ref-list-block/index.js'; import '../inline-list-block/index.js'; -import type { UmbBlockViewPropsType } from '@umbraco-cms/backoffice/block'; -import type { UmbBlockListLayoutModel } from '@umbraco-cms/backoffice/block-list'; +import { stringOrStringArrayContains } from '@umbraco-cms/backoffice/utils'; +import { UmbBlockListEntryContext } from '../../context/block-list-entry.context.js'; +import { UMB_BLOCK_LIST, type UmbBlockListLayoutModel } from '../../types.js'; /** * @element umb-block-list-entry @@ -34,6 +38,9 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper #context = new UmbBlockListEntryContext(this); + @state() + _contentTypeAlias?: string; + @state() _showContentEdit = false; @state() @@ -54,54 +61,144 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper @state() _inlineEditingMode?: boolean; - // TODO: use this type on the Element Interface for the Manifest. @state() - _blockViewProps: UmbBlockViewPropsType = { contentUdi: undefined!, urls: {} }; // Set to undefined cause it will be set before we render. + _blockViewProps: UmbBlockEditorCustomViewProperties = { + contentUdi: undefined!, + config: { showContentEdit: false, showSettingsEdit: false }, + }; // Set to undefined cause it will be set before we render. + + #updateBlockViewProps(incoming: Partial>) { + this._blockViewProps = { ...this._blockViewProps, ...incoming }; + this.requestUpdate('_blockViewProps'); + } constructor() { super(); - this.observe(this.#context.showContentEdit, (showContentEdit) => { - this._showContentEdit = showContentEdit; - }); - this.observe(this.#context.settingsElementTypeKey, (settingsElementTypeKey) => { - this._hasSettings = !!settingsElementTypeKey; - }); - this.observe(this.#context.label, (label) => { - this._label = label; - this._blockViewProps.label = label; - this.requestUpdate('_blockViewProps'); - }); - this.observe(this.#context.contentElementTypeIcon, (icon) => { - this._icon = icon; - this._blockViewProps.icon = icon; - this.requestUpdate('_blockViewProps'); - }); - this.observe(this.#context.inlineEditingMode, (inlineEditingMode) => { - this._inlineEditingMode = inlineEditingMode; - }); + this.observe( + this.#context.showContentEdit, + (showContentEdit) => { + this._showContentEdit = showContentEdit; + this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, showContentEdit } }); + }, + null, + ); + this.observe( + this.#context.settingsElementTypeKey, + (key) => { + this._hasSettings = !!key; + this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, showSettingsEdit: !!key } }); + }, + null, + ); + this.observe( + this.#context.blockType, + (blockType) => { + this.#updateBlockViewProps({ blockType }); + }, + null, + ); + // TODO: Implement index. + this.observe( + this.#context.label, + (label) => { + this.#updateBlockViewProps({ label }); + this._label = label; + }, + null, + ); + this.observe( + this.#context.contentElementTypeIcon, + (icon) => { + this.#updateBlockViewProps({ icon }); + this._icon = icon; + }, + null, + ); + this.observe( + this.#context.inlineEditingMode, + (inlineEditingMode) => { + this._inlineEditingMode = inlineEditingMode; + }, + null, + ); // Data props: - this.observe(this.#context.layout, (layout) => { - this._blockViewProps.layout = layout; - }); - this.observe(this.#context.content, (content) => { - this._blockViewProps.content = content; - }); - this.observe(this.#context.settings, (settings) => { - this._blockViewProps.settings = settings; - }); - this.observe(this.#context.workspaceEditContentPath, (path) => { - this._workspaceEditContentPath = path; - this._blockViewProps.urls.editContent = path; - this.requestUpdate('_blockViewProps'); - }); - this.observe(this.#context.workspaceEditSettingsPath, (path) => { - this._workspaceEditSettingsPath = path; - this._blockViewProps.urls.editSettings = path; - this.requestUpdate('_blockViewProps'); - }); + this.observe( + this.#context.layout, + (layout) => { + this.#updateBlockViewProps({ layout }); + }, + null, + ); + this.observe( + this.#context.content, + (content) => { + this.#updateBlockViewProps({ content }); + }, + null, + ); + this.observe( + this.#context.settings, + (settings) => { + this.#updateBlockViewProps({ settings }); + }, + null, + ); + this.observe( + this.#context.workspaceEditContentPath, + (path) => { + this._workspaceEditContentPath = path; + this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, editContentPath: path } }); + }, + null, + ); + this.observe( + this.#context.workspaceEditSettingsPath, + (path) => { + this._workspaceEditSettingsPath = path; + this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, editSettingsPath: path } }); + }, + null, + ); } + override connectedCallback(): void { + super.connectedCallback(); + // element styling: + this.observe( + this.#context.contentElementTypeKey, + (contentElementTypeKey) => { + if (contentElementTypeKey) { + this.setAttribute('data-content-element-type-key', contentElementTypeKey); + } + }, + 'contentElementTypeKey', + ); + this.observe( + this.#context.contentElementTypeAlias, + (contentElementTypeAlias) => { + if (contentElementTypeAlias) { + this._contentTypeAlias = contentElementTypeAlias; + this.setAttribute('data-content-element-type-alias', contentElementTypeAlias); + } + }, + 'contentElementTypeAlias', + ); + } + + #extensionSlotFilterMethod = (manifest: ManifestBlockEditorCustomView) => { + if ( + manifest.forContentTypeAlias && + !stringOrStringArrayContains(manifest.forContentTypeAlias, this._contentTypeAlias!) + ) { + return false; + } + if (manifest.forBlockEditor && !stringOrStringArrayContains(manifest.forBlockEditor, UMB_BLOCK_LIST)) { + return false; + } + return true; + }; + #renderRefBlock() { return html``; } @@ -116,6 +213,7 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper type="blockEditorCustomView" default-element=${this._inlineEditingMode ? 'umb-inline-list-block' : 'umb-ref-list-block'} .props=${this._blockViewProps} + .filter=${this.#extensionSlotFilterMethod} >${this._inlineEditingMode ? this.#renderInlineBlock() : this.#renderRefBlock()} 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 b3828ddd78..584de1645c 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 @@ -1,22 +1,21 @@ -import { UmbBlockListManagerContext } from '../../context/block-list-manager.context.js'; -import type { UmbBlockListEntryElement } from '../../components/block-list-entry/index.js'; -import type { UmbBlockListLayoutModel, UmbBlockListValueModel } from '../../types.js'; import { UmbBlockListEntriesContext } from '../../context/block-list-entries.context.js'; +import type { UmbBlockListLayoutModel, UmbBlockListValueModel } from '../../types.js'; +import type { UmbBlockListEntryElement } from '../../components/block-list-entry/index.js'; +import { UmbBlockListManagerContext } from '../../context/block-list-manager.context.js'; import { UMB_BLOCK_LIST_PROPERTY_EDITOR_ALIAS } from './manifests.js'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { html, customElement, property, state, repeat, css } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; +import type { UmbPropertyEditorUiElement, UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; import { UmbPropertyValueChangeEvent, type UmbPropertyEditorConfigCollection, } from '@umbraco-cms/backoffice/property-editor'; -import type { UmbBlockLayoutBaseModel } from '@umbraco-cms/backoffice/block'; -import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type'; import type { UmbNumberRangeValueType } from '@umbraco-cms/backoffice/models'; import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/router'; import type { UmbSorterConfig } from '@umbraco-cms/backoffice/sorter'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; +import type { UmbBlockLayoutBaseModel } from '@umbraco-cms/backoffice/block'; import '../../components/block-list-entry/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-type-configuration/property-editor-ui-block-list-type-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-type-configuration/property-editor-ui-block-list-type-configuration.element.ts index ef1f279157..639175c2fb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-type-configuration/property-editor-ui-block-list-type-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-type-configuration/property-editor-ui-block-list-type-configuration.element.ts @@ -1,7 +1,7 @@ -import type { UmbBlockTypeBaseModel, UmbInputBlockTypeElement } from '../../../block-type/index.js'; import '../../../block-type/components/input-block-type/index.js'; +import type { UmbInputBlockTypeElement } from '../../../block-type/index.js'; import { UMB_BLOCK_LIST_TYPE } from '../../types.js'; -import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; +import type { UmbBlockTypeBaseModel, UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbPropertyValueChangeEvent, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/types.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/types.ts index dc5894a548..4b58ab2ec2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/types.ts @@ -1,7 +1,8 @@ -import type { UmbBlockTypeBaseModel } from '../block-type/index.js'; +import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbBlockLayoutBaseModel, UmbBlockValueType } from '@umbraco-cms/backoffice/block'; export const UMB_BLOCK_LIST_TYPE = 'block-list-type'; +export const UMB_BLOCK_LIST = 'block-list'; export interface UmbBlockListTypeModel extends UmbBlockTypeBaseModel {} export interface UmbBlockListLayoutModel extends UmbBlockLayoutBaseModel {} diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts index 4d29189aee..7e276d7028 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts @@ -1,10 +1,14 @@ -import { UmbBlockRteEntryContext } from '../../context/block-rte-entry.context.js'; import type { UmbBlockRteLayoutModel } from '../../types.js'; +import { UmbBlockRteEntryContext } from '../../context/block-rte-entry.context.js'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { html, css, property, state, customElement } from '@umbraco-cms/backoffice/external/lit'; -import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; +import type { + UmbBlockEditorCustomViewProperties, + UmbPropertyEditorUiElement, +} from '@umbraco-cms/backoffice/extension-registry'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; + import '../ref-rte-block/index.js'; -import type { UmbBlockViewPropsType } from '@umbraco-cms/backoffice/block'; /** * @element umb-rte-block @@ -43,9 +47,16 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert @state() _workspaceEditSettingsPath?: string; - // TODO: use this type on the Element Interface for the Manifest. @state() - _blockViewProps: UmbBlockViewPropsType = { contentUdi: undefined!, urls: {} }; // Set to undefined cause it will be set before we render. + _blockViewProps: UmbBlockEditorCustomViewProperties = { + contentUdi: undefined!, + config: { showContentEdit: false, showSettingsEdit: false }, + }; // Set to undefined cause it will be set before we render. + + #updateBlockViewProps(incoming: Partial>) { + this._blockViewProps = { ...this._blockViewProps, ...incoming }; + this.requestUpdate('_blockViewProps'); + } constructor() { super(); @@ -55,49 +66,78 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert this.observe(this.#context.showContentEdit, (showContentEdit) => { this._showContentEdit = showContentEdit; + this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, showContentEdit } }); }); - this.observe(this.#context.settingsElementTypeKey, (settingsElementTypeKey) => { - this._hasSettings = !!settingsElementTypeKey; - }); - this.observe(this.#context.label, (label) => { - this._label = label; - this._blockViewProps.label = label; - this.requestUpdate('_blockViewProps'); - }); - this.observe(this.#context.contentElementTypeIcon, (icon) => { - this._icon = icon; - this._blockViewProps.icon = icon; - this.requestUpdate('_blockViewProps'); + this.observe(this.#context.settingsElementTypeKey, (key) => { + this._hasSettings = !!key; + this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, showSettingsEdit: !!key } }); }); + this.observe( + this.#context.blockType, + (blockType) => { + this.#updateBlockViewProps({ blockType }); + }, + null, + ); + // TODO: Implement index. + this.observe( + this.#context.label, + (label) => { + this.#updateBlockViewProps({ label }); + this._label = label; + }, + null, + ); + this.observe( + this.#context.contentElementTypeIcon, + (icon) => { + this.#updateBlockViewProps({ icon }); + this._icon = icon; + }, + null, + ); // Data props: - this.observe(this.#context.layout, (layout) => { - this._blockViewProps.layout = layout; - }); - this.observe(this.#context.content, (content) => { - this._blockViewProps.content = content; - }); - this.observe(this.#context.settings, (settings) => { - this._blockViewProps.settings = settings; - }); - this.observe(this.#context.workspaceEditContentPath, (path) => { - this._workspaceEditContentPath = path; - this._blockViewProps.urls.editContent = path; - this.requestUpdate('_blockViewProps'); - }); - this.observe(this.#context.workspaceEditSettingsPath, (path) => { - this._workspaceEditSettingsPath = path; - this._blockViewProps.urls.editSettings = path; - this.requestUpdate('_blockViewProps'); - }); + this.observe( + this.#context.layout, + (layout) => { + this.#updateBlockViewProps({ layout }); + }, + null, + ); + this.observe( + this.#context.content, + (content) => { + this.#updateBlockViewProps({ content }); + }, + null, + ); + this.observe( + this.#context.settings, + (settings) => { + this.#updateBlockViewProps({ settings }); + }, + null, + ); + this.observe( + this.#context.workspaceEditContentPath, + (path) => { + this._workspaceEditContentPath = path; + this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, editContentPath: path } }); + }, + null, + ); + this.observe( + this.#context.workspaceEditSettingsPath, + (path) => { + this._workspaceEditSettingsPath = path; + this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, editSettingsPath: path } }); + }, + null, + ); } override connectedCallback() { super.connectedCallback(); - - this.classList.add('uui-font'); - - this.classList.add('uui-text'); - this.setAttribute('contenteditable', 'false'); } @@ -107,27 +147,29 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert #renderBlock() { return html` - ${this.#renderRefBlock()} - - ${this._showContentEdit && this._workspaceEditContentPath - ? html` - - ` - : ''} - ${this._hasSettings && this._workspaceEditSettingsPath - ? html` - - ` - : ''} - this.#context.requestDelete()}> - - - +
+ ${this.#renderRefBlock()} + + ${this._showContentEdit && this._workspaceEditContentPath + ? html` + + ` + : ''} + ${this._hasSettings && this._workspaceEditSettingsPath + ? html` + + ` + : ''} + this.#context.requestDelete()}> + + + +
`; } @@ -136,6 +178,7 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert } static override styles = [ + UmbTextStyles, css` :host { position: relative; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts index 6156f9c6dc..66fbd90b8c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts @@ -1,8 +1,8 @@ -import { UMB_BLOCK_RTE_ENTRIES_CONTEXT } from '../context/block-rte-entries.context-token.js'; import { UMB_BLOCK_RTE_MANAGER_CONTEXT } from '../context/block-rte-manager.context-token.js'; +import { UMB_BLOCK_RTE_ENTRIES_CONTEXT } from '../context/block-rte-entries.context-token.js'; import { type TinyMcePluginArguments, UmbTinyMcePluginBase } from '@umbraco-cms/backoffice/tiny-mce'; import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; -import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type'; +import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; export default class UmbTinyMceMultiUrlPickerPlugin extends UmbTinyMcePluginBase { #localize = new UmbLocalizationController(this._host); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/types.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/types.ts index 45404b976e..b5cedd32da 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/types.ts @@ -1,4 +1,4 @@ -import type { UmbBlockTypeBaseModel } from '../block-type/index.js'; +import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbBlockLayoutBaseModel, UmbBlockValueType } from '@umbraco-cms/backoffice/block'; export const UMB_BLOCK_RTE_TYPE = 'block-rte-type'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/input-block-type/input-block-type.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/input-block-type/input-block-type.element.ts index d782049ca1..cade6212d8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/input-block-type/input-block-type.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/input-block-type/input-block-type.element.ts @@ -1,4 +1,4 @@ -import type { UmbBlockTypeBaseModel, UmbBlockTypeWithGroupKey } from '../../types.js'; +import type { UmbBlockTypeWithGroupKey } from '../../types.js'; import type { UmbBlockTypeCardElement } from '../block-type-card/index.js'; import { umbConfirmModal } from '@umbraco-cms/backoffice/modal'; import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; @@ -12,6 +12,7 @@ import { UMB_DOCUMENT_TYPE_PICKER_MODAL, } from '@umbraco-cms/backoffice/document-type'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; +import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; import '../block-type-card/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/types.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/types.ts index 06bb9dce3c..dccd64dad7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/types.ts @@ -1,17 +1,4 @@ -import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui'; - -export interface UmbBlockTypeBaseModel { - contentElementTypeKey: string; - settingsElementTypeKey?: string; - label?: string; - //view?: string; // TODO: remove/replace with custom element manifest type for block list. - //stylesheet?: string; // TODO: remove/replace with custom element manifest type for block list. - thumbnail?: string; - iconColor?: string; - backgroundColor?: string; - editorSize?: UUIModalSidebarSize; - forceHideContentEditorInOverlay: boolean; -} +import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; export interface UmbBlockTypeGroup { name?: string; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts index 3d1277e9a0..7585986cb5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts @@ -1,4 +1,4 @@ -import type { UmbBlockTypeBaseModel, UmbBlockTypeWithGroupKey } from '../types.js'; +import type { UmbBlockTypeWithGroupKey } from '../types.js'; import { UmbBlockTypeWorkspaceEditorElement } from './block-type-workspace-editor.element.js'; import type { UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property'; import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property'; @@ -13,7 +13,7 @@ import { } from '@umbraco-cms/backoffice/workspace'; import { UmbObjectState, appendToFrozenArray } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import type { ManifestWorkspace } from '@umbraco-cms/backoffice/extension-registry'; +import type { ManifestWorkspace, UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; export class UmbBlockTypeWorkspaceContext extends UmbSubmittableWorkspaceContextBase diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context-token.ts index 5e8190151a..3f625c69f8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context-token.ts @@ -1,8 +1,8 @@ import type { UmbBlockLayoutBaseModel } from '../types.js'; import type { UmbBlockEntriesContext } from './block-entries.context.js'; import type { UMB_BLOCK_MANAGER_CONTEXT } from './block-manager.context-token.js'; -import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; +import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; export const UMB_BLOCK_ENTRIES_CONTEXT = new UmbContextToken< UmbBlockEntriesContext< 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 c16929d7b3..4f8b41066f 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 @@ -1,10 +1,10 @@ -import type { UmbBlockDataType, UmbBlockLayoutBaseModel } from '../types.js'; import type { UmbBlockWorkspaceData } from '../workspace/block-workspace.modal-token.js'; -import { UMB_BLOCK_ENTRIES_CONTEXT } from './block-entries.context-token.js'; +import type { UmbBlockDataType, UmbBlockLayoutBaseModel } from '../types.js'; import type { UmbBlockDataObjectModel, UmbBlockManagerContext } from './block-manager.context.js'; +import { UMB_BLOCK_ENTRIES_CONTEXT } from './block-entries.context-token.js'; import { type Observable, UmbArrayState, UmbBasicState, UmbStringState } from '@umbraco-cms/backoffice/observable-api'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; -import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type'; +import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/router'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context-token.ts index adb50d369e..d7ca4cbef2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context-token.ts @@ -3,7 +3,7 @@ import type { UMB_BLOCK_ENTRIES_CONTEXT } from './block-entries.context-token.js import type { UmbBlockEntriesContext } from './block-entries.context.js'; import type { UmbBlockEntryContext } from './block-entry.context.js'; import type { UMB_BLOCK_MANAGER_CONTEXT } from './block-manager.context-token.js'; -import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type'; +import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export const UMB_BLOCK_ENTRY_CONTEXT = new UmbContextToken< 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 f2d136a648..697144899f 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 @@ -1,7 +1,6 @@ -import type { UmbBlockLayoutBaseModel, UmbBlockDataType } from '../types.js'; import type { UmbBlockManagerContext } from '../index.js'; +import type { UmbBlockLayoutBaseModel, UmbBlockDataType } from '../types.js'; import type { UmbBlockEntriesContext } from './block-entries.context.js'; -import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type'; import type { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -16,6 +15,7 @@ import { encodeFilePath } from '@umbraco-cms/backoffice/utils'; import { umbConfirmModal } from '@umbraco-cms/backoffice/modal'; import type { UmbContentTypeModel } from '@umbraco-cms/backoffice/content-type'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; +import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; export abstract class UmbBlockEntryContext< BlockManagerContextTokenType extends UmbContextToken, 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 afcef0d7b0..ffc8c85022 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 @@ -1,11 +1,11 @@ -import type { UmbBlockLayoutBaseModel, UmbBlockDataType } from '../types.js'; import type { UmbBlockWorkspaceData } from '../workspace/index.js'; +import type { UmbBlockLayoutBaseModel, UmbBlockDataType } from '../types.js'; import { UMB_BLOCK_MANAGER_CONTEXT } from './block-manager.context-token.js'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbArrayState, UmbClassState, UmbStringState } from '@umbraco-cms/backoffice/observable-api'; import { UmbDocumentTypeDetailRepository } from '@umbraco-cms/backoffice/document-type'; -import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type'; +import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbContentTypeModel } from '@umbraco-cms/backoffice/content-type'; import { UmbId } from '@umbraco-cms/backoffice/id'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/modals/block-catalogue/block-catalogue-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/modals/block-catalogue/block-catalogue-modal.token.ts index 0558d7d8ff..db261ccc3d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/modals/block-catalogue/block-catalogue-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/modals/block-catalogue/block-catalogue-modal.token.ts @@ -1,6 +1,7 @@ -import type { UmbBlockWorkspaceData } from '@umbraco-cms/backoffice/block'; -import type { UmbBlockTypeBaseModel, UmbBlockTypeGroup } from '@umbraco-cms/backoffice/block-type'; +import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry'; import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; +import type { UmbBlockWorkspaceData } from '@umbraco-cms/backoffice/block'; +import type { UmbBlockTypeGroup } from '@umbraco-cms/backoffice/block-type'; export interface UmbBlockCatalogueModalData { blocks: Array; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/types.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/types.ts index 433d8e28f7..7c11cec0ac 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/types.ts @@ -1,31 +1,8 @@ -export interface UmbBlockLayoutBaseModel { - contentUdi: string; - settingsUdi?: string | null; -} - -export interface UmbBlockDataType { - udi: string; - contentTypeKey: string; - [key: string]: unknown; -} +import type { UmbBlockDataType, UmbBlockLayoutBaseModel } from '@umbraco-cms/backoffice/extension-registry'; +export type { UmbBlockDataType, UmbBlockLayoutBaseModel } from '@umbraco-cms/backoffice/extension-registry'; export interface UmbBlockValueType { layout: { [key: string]: Array | undefined }; contentData: Array; settingsData: Array; } - -export interface UmbBlockViewUrlsPropType { - editContent?: string; - editSettings?: string; -} - -export interface UmbBlockViewPropsType { - label?: string; - icon?: string; - contentUdi: string; - layout?: BlockLayoutType; - content?: UmbBlockDataType; - settings?: UmbBlockDataType; - urls: UmbBlockViewUrlsPropType; -} 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 d72e461c42..0464b2fe88 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 @@ -141,6 +141,70 @@ export class UmbBlockWorkspaceContext { @@ -194,56 +258,6 @@ export class UmbBlockWorkspaceContext import('./custom-view.element.js'), + forContentTypeAlias: 'elementTypeHeadline', + forBlockEditor: 'block-grid', +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/block/manifests.ts index 80e1e2bf86..7427ab0b53 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/manifests.ts @@ -4,8 +4,11 @@ import { manifests as blockListManifests } from './block-list/manifests.js'; import { manifests as blockRteManifests } from './block-rte/manifests.js'; import { manifests as blockTypeManifests } from './block-type/manifests.js'; import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; +// TODO: Remove test custom view, or transfer to test or similar? +//import { manifest } from './custom-view/manifest.js'; export const manifests: Array = [ + //manifest, ...blockManifests, ...blockTypeManifests, ...blockListManifests, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/collection-view-bundle.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/collection-view-bundle.element.ts index 0d09882d0a..9936840c15 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/collection-view-bundle.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/collection-view-bundle.element.ts @@ -110,6 +110,13 @@ export class UmbCollectionViewBundleElement extends UmbLitElement { #onClick(view: UmbCollectionViewLayout) { this.#collectionContext?.setLastSelectedView(this._entityUnique, view.alias); + + setTimeout(() => { + // TODO: This ignorer is just neede for JSON SCHEMA TO WORK, As its not updated with latest TS jet. + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + this._popover?.hidePopover(); + }, 100); } override render() { @@ -151,15 +158,12 @@ export class UmbCollectionViewBundleElement extends UmbLitElement { css` :host { --uui-button-content-align: left; + --uui-menu-item-flat-structure: 1; } .filter-dropdown { padding: var(--uui-size-space-3); } - - umb-icon { - display: inline-block; - } `, ]; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/components/property-type-based-property/property-type-based-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/components/property-type-based-property/property-type-based-property.element.ts index 7093fe2200..d0dafc2fdc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/components/property-type-based-property/property-type-based-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/components/property-type-based-property/property-type-based-property.element.ts @@ -1,19 +1,17 @@ import type { UmbPropertyEditorConfig } from '../../../property-editor/index.js'; import type { UmbPropertyTypeModel } from '../../types.js'; +import { css, customElement, html, ifDefined, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbContentPropertyContext } from '@umbraco-cms/backoffice/content'; -import type { UmbDataTypeDetailModel } from '@umbraco-cms/backoffice/data-type'; import { UmbDataTypeDetailRepository } from '@umbraco-cms/backoffice/data-type'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { css, html, ifDefined, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import type { UmbDataTypeDetailModel } from '@umbraco-cms/backoffice/data-type'; +import type { UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; + @customElement('umb-property-type-based-property') export class UmbPropertyTypeBasedPropertyElement extends UmbLitElement { @property({ type: Object, attribute: false }) - public get property(): UmbPropertyTypeModel | undefined { - return this._property; - } public set property(value: UmbPropertyTypeModel | undefined) { const oldProperty = this._property; this._property = value; @@ -21,6 +19,9 @@ export class UmbPropertyTypeBasedPropertyElement extends UmbLitElement { this._observeDataType(this._property?.dataType.unique); } } + public get property(): UmbPropertyTypeModel | undefined { + return this._property; + } private _property?: UmbPropertyTypeModel; @property({ type: String, attribute: 'data-path' }) @@ -73,16 +74,19 @@ export class UmbPropertyTypeBasedPropertyElement extends UmbLitElement { } override render() { - return this._propertyEditorUiAlias && this._property?.alias - ? html`` - : ''; + if (!this._propertyEditorUiAlias || !this._property?.alias) return; + return html` + + + `; } static override styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/content-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/content-workspace.context-token.ts index cf2c295263..8977bc8f59 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/content-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/content-workspace.context-token.ts @@ -1,5 +1,5 @@ -import type { UmbContentWorkspaceContext } from './content-workspace-context.interface.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; +import type { UmbContentWorkspaceContext } from './content-workspace-context.interface.js'; export const UMB_CONTENT_WORKSPACE_CONTEXT = new UmbContextToken< UmbContentWorkspaceContext, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/index.ts index 662c027fcb..4c72bcced7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/index.ts @@ -1,2 +1,3 @@ export type * from './content-workspace-context.interface.js'; export * from './content-workspace.context-token.js'; +export * from './views/edit/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/manifests.ts index d30be86fd2..81565ef37a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/manifests.ts @@ -1,3 +1,3 @@ -import { contentEditorManifest } from './views/edit/manifest.js'; +import { manifests as contentEditorManifests } from './views/edit/manifests.js'; -export const manifests = [contentEditorManifest]; +export const manifests = [...contentEditorManifests]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/conditions/has-properties/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/conditions/has-properties/constants.ts new file mode 100644 index 0000000000..a37e6add77 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/conditions/has-properties/constants.ts @@ -0,0 +1 @@ +export const UMB_CONTENT_HAS_PROPERTIES_WORKSPACE_CONDITION = 'Umb.Condition.Workspace.ContentHasProperties'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/conditions/has-properties/content-has-properties.condition.manifest.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/conditions/has-properties/content-has-properties.condition.manifest.ts new file mode 100644 index 0000000000..654bbb853b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/conditions/has-properties/content-has-properties.condition.manifest.ts @@ -0,0 +1,8 @@ +import type { ManifestCondition } from '@umbraco-cms/backoffice/extension-api'; + +export const manifest: ManifestCondition = { + type: 'condition', + name: 'Content has properties Workspace Condition', + alias: 'Umb.Condition.Workspace.ContentHasProperties', + api: () => import('./content-has-properties.condition.js'), +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/conditions/has-properties/content-has-properties.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/conditions/has-properties/content-has-properties.condition.ts new file mode 100644 index 0000000000..34d6ebe6aa --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/conditions/has-properties/content-has-properties.condition.ts @@ -0,0 +1,30 @@ +import { UMB_CONTENT_WORKSPACE_CONTEXT } from '../../../../content-workspace.context-token.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { + UmbConditionConfigBase, + UmbConditionControllerArguments, + UmbExtensionCondition, +} from '@umbraco-cms/backoffice/extension-api'; +import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry'; + +export class UmbContentHasPropertiesWorkspaceCondition + extends UmbConditionBase + implements UmbExtensionCondition +{ + constructor(host: UmbControllerHost, args: UmbConditionControllerArguments) { + super(host, args); + + this.consumeContext(UMB_CONTENT_WORKSPACE_CONTEXT, (context) => { + this.observe( + context.structure.contentTypes, + (contentTypes) => { + const hasProperties = contentTypes.some((contentType) => contentType.properties.length > 0); + this.permitted = hasProperties; + }, + 'contentTypesObserver', + ); + }); + } +} + +export { UmbContentHasPropertiesWorkspaceCondition as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/conditions/has-properties/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/conditions/has-properties/index.ts new file mode 100644 index 0000000000..4f07201dcf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/conditions/has-properties/index.ts @@ -0,0 +1 @@ +export * from './constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/conditions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/conditions/manifests.ts new file mode 100644 index 0000000000..d4221afab0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/conditions/manifests.ts @@ -0,0 +1,3 @@ +import { manifest as hasPropertiesManifest } from './has-properties/content-has-properties.condition.manifest.js'; + +export const manifests = [hasPropertiesManifest]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/index.ts new file mode 100644 index 0000000000..1637f3a5e7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/index.ts @@ -0,0 +1 @@ +export * from './conditions/has-properties/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/manifest.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/manifests.ts similarity index 67% rename from src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/manifest.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/manifests.ts index 3cb898bd97..caa2ec28d8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/manifest.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/views/edit/manifests.ts @@ -1,6 +1,7 @@ +import { manifests as conditionsManifests } from './conditions/manifests.js'; import type { UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry'; -export const contentEditorManifest: UmbBackofficeManifestKind = { +const contentEditorManifest: UmbBackofficeManifestKind = { type: 'kind', alias: 'Umb.Kind.WorkspaceView.ContentEditor', matchKind: 'contentEditor', @@ -17,3 +18,5 @@ export const contentEditorManifest: UmbBackofficeManifestKind = { }, }, }; + +export const manifests = [contentEditorManifest, ...conditionsManifests]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-slot/extension-slot.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-slot/extension-slot.element.ts index b5e64b5d8b..ab38f6b6b9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-slot/extension-slot.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-slot/extension-slot.element.ts @@ -1,11 +1,11 @@ import { umbExtensionsRegistry } from '../../registry.js'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { TemplateResult } from '@umbraco-cms/backoffice/external/lit'; import { css, repeat, customElement, property, state, html } from '@umbraco-cms/backoffice/external/lit'; import { type UmbExtensionElementInitializer, UmbExtensionsElementInitializer, } from '@umbraco-cms/backoffice/extension-api'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; /** * @element umb-extension-slot @@ -24,7 +24,7 @@ export class UmbExtensionSlotElement extends UmbLitElement { #extensionsController?: UmbExtensionsElementInitializer; @state() - private _permitted: Array = []; + private _permitted?: Array; /** * The type or types of extensions to render. @@ -129,13 +129,15 @@ export class UmbExtensionSlotElement extends UmbLitElement { } override render() { - return this._permitted.length > 0 - ? repeat( - this._permitted, - (ext) => ext.alias, - (ext, i) => (this.renderMethod ? this.renderMethod(ext, i) : ext.component), - ) - : html``; + return this._permitted + ? this._permitted.length > 0 + ? repeat( + this._permitted, + (ext) => ext.alias, + (ext, i) => (this.renderMethod ? this.renderMethod(ext, i) : ext.component), + ) + : html`` + : ''; } static override styles = css` diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-with-api-slot/extension-with-api-slot.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-with-api-slot/extension-with-api-slot.element.ts index d7d5fc09ab..84738a56d2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-with-api-slot/extension-with-api-slot.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-with-api-slot/extension-with-api-slot.element.ts @@ -1,5 +1,4 @@ import { umbExtensionsRegistry } from '../../registry.js'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { TemplateResult } from '@umbraco-cms/backoffice/external/lit'; import { css, repeat, customElement, property, state, html } from '@umbraco-cms/backoffice/external/lit'; import { @@ -8,6 +7,7 @@ import { type UmbApiConstructorArgumentsMethodType, type ApiLoaderProperty, } from '@umbraco-cms/backoffice/extension-api'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; /** * @element umb-extension-with-api-slot @@ -26,7 +26,7 @@ export class UmbExtensionWithApiSlotElement extends UmbLitElement { #extensionsController?: UmbExtensionsElementAndApiInitializer; @state() - private _permitted: Array = []; + private _permitted?: Array; /** * The type or types of extensions to render. @@ -178,13 +178,15 @@ export class UmbExtensionWithApiSlotElement extends UmbLitElement { } override render() { - return this._permitted.length > 0 - ? repeat( - this._permitted, - (ext) => ext.alias, - (ext, i) => (this.renderMethod ? this.renderMethod(ext, i) : ext.component), - ) - : html``; + return this._permitted + ? this._permitted.length > 0 + ? repeat( + this._permitted, + (ext) => ext.alias, + (ext, i) => (this.renderMethod ? this.renderMethod(ext, i) : ext.component), + ) + : html`` + : ''; } static override styles = css` diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/block-editor-custom-view-element.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/block-editor-custom-view-element.interface.ts new file mode 100644 index 0000000000..d9b7a73de0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/block-editor-custom-view-element.interface.ts @@ -0,0 +1,55 @@ +import type { ManifestBlockEditorCustomView } from '../index.js'; +import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui'; +// Shared with the Property Editor +export interface UmbBlockTypeBaseModel { + contentElementTypeKey: string; + settingsElementTypeKey?: string; + label?: string; + thumbnail?: string; + iconColor?: string; + backgroundColor?: string; + editorSize?: UUIModalSidebarSize; + forceHideContentEditorInOverlay: boolean; +} + +// Shared with the Property Editor +export interface UmbBlockLayoutBaseModel { + contentUdi: string; + settingsUdi?: string | null; +} + +// Shared with the Property Editor +export interface UmbBlockDataType { + udi: string; + contentTypeKey: string; + [key: string]: unknown; +} + +export interface UmbBlockEditorCustomViewConfiguration { + editContentPath?: string; + editSettingsPath?: string; + showContentEdit: boolean; + showSettingsEdit: boolean; +} + +export interface UmbBlockEditorCustomViewProperties< + LayoutType extends UmbBlockLayoutBaseModel = UmbBlockLayoutBaseModel, + BlockType extends UmbBlockTypeBaseModel = UmbBlockTypeBaseModel, +> { + manifest?: ManifestBlockEditorCustomView; + config?: Partial; + blockType?: BlockType; + contentUdi?: string; + label?: string; + icon?: string; + index?: number; + layout?: LayoutType; + content?: UmbBlockDataType; + settings?: UmbBlockDataType; +} + +export interface UmbBlockEditorCustomViewElement< + LayoutType extends UmbBlockLayoutBaseModel = UmbBlockLayoutBaseModel, + BlockType extends UmbBlockTypeBaseModel = UmbBlockTypeBaseModel, +> extends UmbBlockEditorCustomViewProperties, + HTMLElement {} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/index.ts index 1721b7dfec..7585b5001b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/index.ts @@ -1,3 +1,4 @@ +export * from './block-editor-custom-view-element.interface.js'; export * from './dashboard-element.interface.js'; export * from './external-login-provider-element.interface.js'; export * from './menu-item-element.interface.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/block-editor-custom-view.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/block-editor-custom-view.model.ts index 002d6f0ecf..55a8208789 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/block-editor-custom-view.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/block-editor-custom-view.model.ts @@ -1,6 +1,20 @@ -import type { UmbPropertyEditorUiElement } from '../interfaces/index.js'; +import type { UmbBlockEditorCustomViewElement } from '../interfaces/index.js'; import type { ManifestElement } from '@umbraco-cms/backoffice/extension-api'; -export interface ManifestBlockEditorCustomView extends ManifestElement { +export interface ManifestBlockEditorCustomView extends ManifestElement { type: 'blockEditorCustomView'; + /** + * @property {string | Array } - Declare if this Custom View only must appear at specific Content Types by Alias. + * @description Optional condition if you like this custom view to only appear at for one or more specific Content Types. + * @example 'my-element-type-alias' + * @example ['my-element-type-alias-A', 'my-element-type-alias-B'] + */ + forContentTypeAlias?: string | Array; + /** + * @property {string | Array } - Declare if this Custom View only must appear at specific Block Editors. + * @description Optional condition if you like this custom view to only appear at a specific type of Block Editor. + * @example 'block-list' + * @example ['block-list', 'block-grid'] + */ + forBlockEditor?: string | Array; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/entity-action.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/entity-action.model.ts index a24470e952..ebb89ea663 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/entity-action.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/entity-action.model.ts @@ -1,6 +1,6 @@ import type { ConditionTypes } from '../conditions/types.js'; -import type { UmbEntityAction, UmbEntityActionElement } from '@umbraco-cms/backoffice/entity-action'; import type { ManifestElementAndApi, ManifestWithDynamicConditions } from '@umbraco-cms/backoffice/extension-api'; +import type { UmbEntityAction, UmbEntityActionElement } from '@umbraco-cms/backoffice/entity-action'; import type { UmbModalToken, UmbPickerModalData, UmbPickerModalValue } from '@umbraco-cms/backoffice/modal'; /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json index e6e06752d4..5058e26d8d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json @@ -1098,7 +1098,7 @@ }, { "name": "icon-home", - "file": "home.svg" + "file": "house.svg" }, { "name": "icon-hourglass", @@ -1810,8 +1810,7 @@ }, { "name": "icon-readonly", - "file": "ban.svg", - "legacy": true + "file": "pencil-off.svg" }, { "name": "icon-receipt-alt", diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts index 8f0b5beb5f..e9edf842e7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts @@ -1504,7 +1504,7 @@ name: "icon-re-post", path: () => import("./icons/icon-re-post.js"), },{ name: "icon-readonly", -legacy: true, + path: () => import("./icons/icon-readonly.js"), },{ name: "icon-receipt-alt", diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-activity.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-activity.ts index 758f7e1a7f..1a21c810d9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-activity.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-activity.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-alarm-clock.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-alarm-clock.ts index 67f9e0916e..c3f3de082a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-alarm-clock.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-alarm-clock.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` - - - + + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-block.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-block.ts index 1c4cac1797..2d49791dbd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-block.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-block.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-bookmark.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-bookmark.ts index 2e04cfd7bd..008f7092d9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-bookmark.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-bookmark.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - - + + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-brush.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-brush.ts index a601ee5fe8..c0ffaa941f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-brush.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-brush.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - + diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-bus.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-bus.ts index 75ea23029c..e71d0be5b8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-bus.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-bus.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - - - + + + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-connection.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-connection.ts index ca750a1948..585cc5c3b0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-connection.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-connection.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-diagonal-arrow-alt.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-diagonal-arrow-alt.ts index c53809934a..9033da3a2c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-diagonal-arrow-alt.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-diagonal-arrow-alt.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - - - - - - - - - + + + + + + + + + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-dock-connector.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-dock-connector.ts index 2a4cab3bd6..fdbb6d22a6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-dock-connector.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-dock-connector.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-employee.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-employee.ts index a84fc74533..781dfcda19 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-employee.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-employee.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linejoin="round" > - + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-factory.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-factory.ts index 05cb13ee9f..712b8dc83b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-factory.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-factory.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - - - - + + + + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hand-active.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hand-active.ts index 3dec0581ce..c4cdd9c341 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hand-active.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hand-active.ts @@ -1,4 +1,4 @@ -export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - - - - + + + + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hand-pointer-alt.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hand-pointer-alt.ts index c567e366dd..54adf21360 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hand-pointer-alt.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hand-pointer-alt.ts @@ -1,4 +1,4 @@ -export default ` +export default ` stroke-linejoin="round" > - - - + + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hand-pointer.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hand-pointer.ts index c567e366dd..54adf21360 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hand-pointer.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hand-pointer.ts @@ -1,4 +1,4 @@ -export default ` +export default ` stroke-linejoin="round" > - - - + + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-handshake.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-handshake.ts index cbd396e17b..61f8399b76 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-handshake.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-handshake.ts @@ -1,4 +1,4 @@ -export default ` +export default ` stroke-linejoin="round" > - + diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-handtool-alt.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-handtool-alt.ts index 832d692cb4..bb19cf1701 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-handtool-alt.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-handtool-alt.ts @@ -1,4 +1,4 @@ -export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - - + + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-handtool.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-handtool.ts index 832d692cb4..bb19cf1701 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-handtool.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-handtool.ts @@ -1,4 +1,4 @@ -export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - - + + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hard-drive-alt.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hard-drive-alt.ts index 93c3709476..13a2cfd5a8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hard-drive-alt.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hard-drive-alt.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hourglass.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hourglass.ts index 8af28790f5..d347c3a2ba 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hourglass.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-hourglass.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-keyboard.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-keyboard.ts index 538d00376f..ab9894defe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-keyboard.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-keyboard.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` > - + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-map-alt.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-map-alt.ts index 02202f680e..dde980bb27 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-map-alt.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-map-alt.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linejoin="round" > + - `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-music.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-music.ts index f4415ebd0c..8842de4d86 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-music.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-music.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - + + - `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-picture.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-picture.ts index 5cfa4cbf8b..7c5a2f979b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-picture.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-picture.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - + - + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-pin-location.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-pin-location.ts index efffb8683d..af8aee6c71 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-pin-location.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-pin-location.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - - + + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-power.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-power.ts index fb6a788a71..f25f7eda3f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-power.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-power.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-qr-code.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-qr-code.ts index c2bb35b9fc..3d1903d637 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-qr-code.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-qr-code.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-radio-alt.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-radio-alt.ts index ce1d915654..13d8ceebb8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-radio-alt.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-radio-alt.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - + + + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-receipt-alt.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-receipt-alt.ts index 761de66804..2e1474718f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-receipt-alt.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-receipt-alt.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linejoin="round" > - + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-refresh.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-refresh.ts index eee4d50421..b923432d2d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-refresh.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-refresh.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-ruler.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-ruler.ts index 3249acd1aa..ae8e417729 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-ruler.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-ruler.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-stop-hand.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-stop-hand.ts index 832d692cb4..bb19cf1701 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-stop-hand.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-stop-hand.ts @@ -1,4 +1,4 @@ -export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - - + + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-stop.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-stop.ts index 52afec12f4..cd799fae8b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-stop.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-stop.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` - + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-stream.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-stream.ts index 148adb300a..b95599c4f1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-stream.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-stream.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linejoin="round" > - + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-thumb-up.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-thumb-up.ts index 96b8890d7b..7fe7b96e80 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-thumb-up.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-thumb-up.ts @@ -1,4 +1,4 @@ -export default ` +export default ` stroke-linejoin="round" > - + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-thumbnail-list.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-thumbnail-list.ts index 6e1209cdd6..3a9a428755 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-thumbnail-list.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-thumbnail-list.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linejoin="round" > - + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-unlocked.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-unlocked.ts index 0838c1a266..596e1de667 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-unlocked.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-unlocked.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` +export default ` stroke-linecap="round" stroke-linejoin="round" > - - - - + + + + `; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-zom-out.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-zom-out.ts index 648b1548bf..b3823e5a9e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-zom-out.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-zom-out.ts @@ -1,4 +1,4 @@ -export default ` +export default ` +export default ` +export default ` \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/notification/extractUmbNotificationColor.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/notification/extractUmbNotificationColor.function.ts new file mode 100644 index 0000000000..105b025fc5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/notification/extractUmbNotificationColor.function.ts @@ -0,0 +1,18 @@ +import type { UmbNotificationColor } from './notification.context.js'; +import { EventMessageTypeModel } from '@umbraco-cms/backoffice/external/backend-api'; + +export function extractUmbNotificationColor(type: EventMessageTypeModel): UmbNotificationColor { + switch (type) { + case EventMessageTypeModel.ERROR: + return 'danger'; + case EventMessageTypeModel.WARNING: + return 'warning'; + case EventMessageTypeModel.INFO: + case EventMessageTypeModel.DEFAULT: + return 'default'; + case EventMessageTypeModel.SUCCESS: + return 'positive'; + default: + return ''; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/notification/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/notification/index.ts index 44692cbce0..ed8dcb76fc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/notification/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/notification/index.ts @@ -2,3 +2,6 @@ import './layouts/default/index.js'; export * from './notification.context.js'; export * from './notification-handler.js'; + +export * from './isUmbNotifications.function.js'; +export * from './extractUmbNotificationColor.function.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/notification/isUmbNotifications.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/notification/isUmbNotifications.function.ts new file mode 100644 index 0000000000..036185b355 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/notification/isUmbNotifications.function.ts @@ -0,0 +1,26 @@ +import { EventMessageTypeModel } from '@umbraco-cms/backoffice/external/backend-api'; + +function objectIsUmbNotification(notification: unknown): notification is UmbNotificationsEventModel { + if (typeof notification !== 'object' || notification === null) { + return false; + } + const object = notification as UmbNotificationsEventModel; + return ( + typeof object.category === 'string' && + typeof object.message === 'string' && + typeof object.type === 'string' && + Object.values(EventMessageTypeModel).includes(object.type) + ); +} + +export interface UmbNotificationsEventModel { + category: string; + message: string; + type: EventMessageTypeModel; +} + +export function isUmbNotifications(notifications: Array): notifications is Array { + return notifications.every(objectIsUmbNotification); +} + +export const UMB_NOTIFICATION_HEADER = 'umb-notifications'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/picker-input/picker-input.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/picker-input/picker-input.context.ts index 0022d3cdda..97c39f77f5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/picker-input/picker-input.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/picker-input/picker-input.context.ts @@ -99,6 +99,7 @@ export class UmbPickerInputContext< content: 'Are you sure you want to remove this item', confirmLabel: 'Remove', }); + this.#removeItem(unique); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property-layout/property-layout.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property-layout/property-layout.element.ts index ba194a6e06..1724d318a1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property-layout/property-layout.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property-layout/property-layout.element.ts @@ -51,7 +51,7 @@ export class UmbPropertyLayoutElement extends UmbLitElement { public description = ''; /** - * @description Make the property appear invalid + * @description Make the property appear invalid. * @type {boolean} * @attr * @default undefined @@ -59,11 +59,20 @@ export class UmbPropertyLayoutElement extends UmbLitElement { @property({ type: Boolean, reflect: true }) public invalid?: boolean; + /** + * @description Display a mandatory indicator. + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + public mandatory?: boolean; + override render() { // TODO: Only show alias on label if user has access to DocumentType within settings: return html`
- + ${this.localize.string(this.label)} ${when(this.invalid, () => html`!`)} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts index 9d1ea7be5d..76a0dbaa15 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.context.ts @@ -14,27 +14,39 @@ import type { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { UmbPropertyEditorConfigProperty } from '@umbraco-cms/backoffice/property-editor'; import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; -import type { UmbPropertyTypeAppearanceModel } from '@umbraco-cms/backoffice/content-type'; +import type { + UmbPropertyTypeAppearanceModel, + UmbPropertyTypeValidationModel, +} from '@umbraco-cms/backoffice/content-type'; export class UmbPropertyContext extends UmbContextBase> { #alias = new UmbStringState(undefined); public readonly alias = this.#alias.asObservable(); + #label = new UmbStringState(undefined); public readonly label = this.#label.asObservable(); + #description = new UmbStringState(undefined); public readonly description = this.#description.asObservable(); + #appearance = new UmbObjectState(undefined); public readonly appearance = this.#appearance.asObservable(); + #value = new UmbDeepState(undefined); public readonly value = this.#value.asObservable(); + #configValues = new UmbArrayState([], (x) => x.alias); public readonly configValues = this.#configValues.asObservable(); #config = new UmbClassState(undefined); public readonly config = this.#config.asObservable(); + #validation = new UmbObjectState(undefined); + public readonly validation = this.#validation.asObservable(); + private _editor = new UmbBasicState(undefined); public readonly editor = this._editor.asObservable(); + setEditor(editor: UmbPropertyEditorUiElement | undefined) { this._editor.setValue(editor ?? undefined); } @@ -108,24 +120,28 @@ export class UmbPropertyContext extends UmbContextBase extends UmbContextBase | undefined): void { this.#configValues.setValue(config ?? []); } public getConfig(): Array | undefined { return this.#configValues.getValue(); } + public setVariantId(variantId: UmbVariantId | undefined): void { this.#variantId.setValue(variantId); } @@ -156,6 +174,13 @@ export class UmbPropertyContext extends UmbContextBase { @@ -176,6 +194,7 @@ export class UmbPropertyElement extends UmbLitElement { }, null, ); + this.observe( this.#propertyContext.description, (description) => { @@ -183,6 +202,7 @@ export class UmbPropertyElement extends UmbLitElement { }, null, ); + this.observe( this.#propertyContext.variantDifference, (variantDifference) => { @@ -190,6 +210,7 @@ export class UmbPropertyElement extends UmbLitElement { }, null, ); + this.observe( this.#propertyContext.appearance, (appearance) => { @@ -197,6 +218,14 @@ export class UmbPropertyElement extends UmbLitElement { }, null, ); + + this.observe( + this.#propertyContext.validation, + (validation) => { + this._mandatory = validation?.mandatory; + }, + null, + ); } private _onPropertyEditorChange = (e: CustomEvent): void => { @@ -297,10 +326,11 @@ export class UmbPropertyElement extends UmbLitElement { return html` ${this.#renderPropertyActionMenu()} ${this._variantDifference diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts index b86d2568f7..7b12366898 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts @@ -6,6 +6,7 @@ import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { UMB_NOTIFICATION_CONTEXT, type UmbNotificationOptions } from '@umbraco-cms/backoffice/notification'; import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; +import type { ProblemDetails } from '@umbraco-cms/backoffice/external/backend-api'; export class UmbResourceController extends UmbControllerBase { #promise: Promise; @@ -57,8 +58,8 @@ export class UmbResourceController extends UmbControllerBase { * If the executor function throws an error, then show the details in a notification. */ async tryExecuteAndNotify(options?: UmbNotificationOptions): Promise> { - const { data, error: _error } = await UmbResourceController.tryExecute(this.#promise); - const error: any = _error; + const { data, error } = await UmbResourceController.tryExecute(this.#promise); + if (error) { /** * Determine if we want to show a notification or just log the error to the console. @@ -71,18 +72,33 @@ export class UmbResourceController extends UmbControllerBase { } else { console.group('ApiError caught in UmbResourceController'); console.error('Request failed', error.request); - console.error('ProblemDetails', error.body); + console.error('Request body', error.body); console.error('Error', error); + let problemDetails: ProblemDetails | null = null; + // ApiError - body could hold a ProblemDetails from the server if (typeof error.body !== 'undefined' && !!error.body) { try { - (error as any).body = typeof error.body === 'string' ? JSON.parse(error.body) : error.body; + (error as any).body = problemDetails = typeof error.body === 'string' ? JSON.parse(error.body) : error.body; } catch (e) { console.error('Error parsing error body (expected JSON)', e); } } + /** + * Check if the operation status ends with `ByNotification` and if so, don't show a notification + * This is a special case where the operation was cancelled by the server and the client gets a notification header instead. + */ + let isCancelledByNotification = false; + if ( + problemDetails?.operationStatus && + typeof problemDetails.operationStatus === 'string' && + problemDetails.operationStatus.endsWith('ByNotification') + ) { + isCancelledByNotification = true; + } + // Go through the error status codes and act accordingly switch (error.status ?? 0) { case 401: { @@ -103,14 +119,14 @@ export class UmbResourceController extends UmbControllerBase { case 500: // Server Error - if (this.#notificationContext) { - let headline = error.body?.title ?? error.name ?? 'Server Error'; + if (!isCancelledByNotification && this.#notificationContext) { + let headline = problemDetails?.title ?? error.name ?? 'Server Error'; let message = 'A fatal server error occurred. If this continues, please reach out to your administrator.'; // Special handling for ObjectCacheAppCache corruption errors, which we are investigating if ( - error.body?.detail?.includes('ObjectCacheAppCache') || - error.body?.detail?.includes('Umbraco.Cms.Infrastructure.Scoping.Scope.DisposeLastScope()') + problemDetails?.detail?.includes('ObjectCacheAppCache') || + problemDetails?.detail?.includes('Umbraco.Cms.Infrastructure.Scoping.Scope.DisposeLastScope()') ) { headline = 'Please restart the server'; message = @@ -128,12 +144,14 @@ export class UmbResourceController extends UmbControllerBase { break; default: // Other errors - if (this.#notificationContext) { + if (!isCancelledByNotification && this.#notificationContext) { this.#notificationContext.peek('danger', { data: { - headline: error.body?.title ?? error.name ?? 'Server Error', - message: error.body?.detail ?? error.message ?? 'Something went wrong', - structuredList: error.body.errors, + headline: problemDetails?.title ?? error.name ?? 'Server Error', + message: problemDetails?.detail ?? error.message ?? 'Something went wrong', + structuredList: problemDetails?.errors + ? (problemDetails.errors as Record>) + : undefined, }, ...options, }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/store/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/store/index.ts index 96a1ac29cb..59ffb63a12 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/store/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/store/index.ts @@ -1,4 +1,5 @@ export * from './store-base.js'; +export * from './store-object-base.js'; export * from './store.interface.js'; export * from './events/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/store/store-object-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/store/store-object-base.ts new file mode 100644 index 0000000000..5d3004489b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/store/store-object-base.ts @@ -0,0 +1,60 @@ +import { UmbStoreUpdateEvent } from './events/index.js'; +import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; +import { type Observable, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; + +/** + * The base class for a store that holds an object. + */ +export class UmbStoreObjectBase extends UmbContextBase implements UmbApi { + protected _data; + + constructor(host: UmbControllerHost, storeAlias: string, initialData?: T) { + super(host, storeAlias); + this._data = new UmbObjectState(initialData ?? null); + } + + /** + * Updates the store with the given data + * @param data - The (partial) data to update the store with + * @memberof UmbStoreObjectBase + */ + update(data: Partial) { + this._data.update(data); + this.dispatchEvent(new UmbStoreUpdateEvent([])); + } + + /** + * Returns the current state of the store + * @memberof UmbStoreObjectBase + */ + getState() { + return this._data.getValue(); + } + + /** + * Returns an observable of the store + * @memberof UmbStoreObjectBase + */ + all() { + return this._data.asObservable(); + } + + /** + * Returns an observable of a part of the store + * @param key - The key of the part to return + * @memberof UmbStoreObjectBase + */ + part(key: Part): Observable { + return this._data.asObservablePart((data) => data![key]); + } + + /** + * Destroys the store + * @memberof UmbStoreObjectBase + */ + override destroy() { + this._data.destroy(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts index 2fe2d1004a..82505e62be 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts @@ -21,5 +21,6 @@ export * from './string/from-camel-case.function.js'; export * from './string/generate-umbraco-alias.function.js'; export * from './string/increment-string.function.js'; export * from './string/split-string-to-array.js'; +export * from './string/string-or-string-array-contains.function.js'; export * from './string/to-camel-case/to-camel-case.function.js'; export type * from './type/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/string/string-or-string-array-contains.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/string/string-or-string-array-contains.function.ts new file mode 100644 index 0000000000..54f8dc419f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/string/string-or-string-array-contains.function.ts @@ -0,0 +1,3 @@ +export function stringOrStringArrayContains(value: string | Array, search: string) { + return Array.isArray(value) ? value.indexOf(search) !== -1 : value === search; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts index bb3d9dbf68..a898f756b9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts @@ -268,14 +268,18 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< ${when( dataTypesEntries.length > 0, () => - html`
Available configurations
+ html`
+ Available configurations +
${this._renderDataTypes()}${this.#renderLoadMore()}`, )} ${when( editorUIEntries.length > 0, () => - html`
Create a new configuration
- ${this._renderUIs()}`, + html`
+ Create a new configuration +
+ ${this._renderUIs(true)}`, )} `; } @@ -298,7 +302,7 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< ); } - private _renderUIs() { + private _renderUIs(createAsNewOnPick?: boolean) { if (!this._groupedPropertyEditorUIs) return nothing; const entries = Object.entries(this._groupedPropertyEditorUIs); @@ -306,33 +310,46 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< return entries.map( ([key, value]) => html`
${key === 'undefined' ? 'Uncategorized' : key}
- ${this._renderGroupUIs(value)}`, + ${this._renderGroupUIs(value, createAsNewOnPick)}`, ); } - private _renderGroupUIs(uis: Array) { + private _renderGroupUIs(uis: Array, createAsNewOnPick?: boolean) { return html`
    ${this._dataTypePickerModalRouteBuilder ? repeat( uis, (propertyEditorUI) => propertyEditorUI.alias, - (propertyEditorUI) => - html`
  • - -
    - - ${propertyEditorUI.meta.label || propertyEditorUI.name} -
    -
    -
  • `, + (propertyEditorUI) => { + return html`
  • ${this._renderDataTypeButton(propertyEditorUI, createAsNewOnPick)}
  • `; + }, ) : ''}
`; } + private _renderDataTypeButton(propertyEditorUI: ManifestPropertyEditorUi, createAsNewOnPick?: boolean) { + if (createAsNewOnPick) { + return html` this._createDataType(propertyEditorUI.alias)}> + ${this._renderItemContent(propertyEditorUI)} + `; + } else { + return html` + ${this._renderItemContent(propertyEditorUI)} + `; + } + } + private _renderItemContent(propertyEditorUI: ManifestPropertyEditorUi) { + return html`
+ + ${propertyEditorUI.meta.label || propertyEditorUI.name} +
`; + } + private _renderGroupDataTypes(dataTypes: Array) { return html`
    ${repeat( diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts index cddf17f5e4..df5e20479a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts @@ -2,15 +2,17 @@ import { UmbDocumentPickerContext } from './input-document.context.js'; import { classMap, css, customElement, html, property, repeat, state } from '@umbraco-cms/backoffice/external/lit'; import { splitStringToArray } from '@umbraco-cms/backoffice/utils'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; import { UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/modal'; import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; import type { UmbDocumentItemModel } from '@umbraco-cms/backoffice/document'; import type { UmbTreeStartNode } from '@umbraco-cms/backoffice/tree'; -import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; -@customElement('umb-input-document') +const elementName = 'umb-input-document'; + +@customElement(elementName) export class UmbInputDocumentElement extends UmbFormControlMixin( UmbLitElement, ) { @@ -73,7 +75,7 @@ export class UmbInputDocumentElement extends UmbFormControlMixin) { @@ -124,28 +126,24 @@ export class UmbInputDocumentElement extends UmbFormControlMixin this.minMessage, - () => !!this.min && this.#pickerContext.getSelection().length < this.min, + () => !!this.min && this.selection.length < this.min, ); this.addValidator( 'rangeOverflow', () => this.maxMessage, - () => !!this.max && this.#pickerContext.getSelection().length > this.max, + () => !!this.max && this.selection.length > this.max, ); this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(',')), '_observeSelection'); this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems), '_observerItems'); } - protected override getFormElement() { - return undefined; - } - #isDraft(item: UmbDocumentItemModel) { return item.variants[0]?.state === 'Draft'; } - #pickableFilter: (item: UmbDocumentItemModel) => boolean = (item) => { + #pickableFilter = (item: UmbDocumentItemModel): boolean => { if (this.allowedContentTypeIds && this.allowedContentTypeIds.length > 0) { return this.allowedContentTypeIds.includes(item.documentType.unique); } @@ -160,7 +158,7 @@ export class UmbInputDocumentElement extends UmbFormControlMixin= this.max) return; + if (this.selection.length >= this.max) return; return html` ${this.#renderOpenButton(item)} - this.#removeItem(item)} label=${this.localize.term('general_remove')}> + this.#onRemove(item)} label=${this.localize.term('general_remove')}> `; @@ -229,7 +227,7 @@ export class UmbInputDocumentElement extends UmbFormControlMixin = [ alias: 'Umb.Condition.WorkspaceAlias', match: workspace.alias, }, + { + alias: UMB_CONTENT_HAS_PROPERTIES_WORKSPACE_CONDITION, + }, ], }, { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/components/imaging-thumbnail.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/components/imaging-thumbnail.element.ts new file mode 100644 index 0000000000..81ae2ff903 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/components/imaging-thumbnail.element.ts @@ -0,0 +1,173 @@ +import { UmbImagingCropMode } from '../types.js'; +import { UmbImagingRepository } from '../imaging.repository.js'; +import { css, customElement, html, nothing, property, state, when } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; + +const ELEMENT_NAME = 'umb-imaging-thumbnail'; + +@customElement(ELEMENT_NAME) +export class UmbImagingThumbnailElement extends UmbLitElement { + /** + * The unique identifier for the media item. + * @remark This is also known as the media key and is used to fetch the resource. + */ + @property() + unique = ''; + + /** + * The width of the thumbnail in pixels. + * @default 300 + */ + @property({ type: Number }) + width = 300; + + /** + * The height of the thumbnail in pixels. + * @default 300 + */ + @property({ type: Number }) + height = 300; + + /** + * The mode of the thumbnail. + * @remark The mode determines how the image is cropped. + * @enum {UmbImagingCropMode} + */ + @property() + mode: UmbImagingCropMode = UmbImagingCropMode.MIN; + + /** + * The alt text for the thumbnail. + */ + @property() + alt = ''; + + /** + * The fallback icon for the thumbnail. + */ + @property() + icon = 'icon-picture'; + + /** + * The `loading` state of the thumbnail. + * @enum {'lazy' | 'eager'} + * @default 'lazy' + */ + @property() + loading: 'lazy' | 'eager' = 'lazy'; + + @state() + private _isLoading = true; + + @state() + private _thumbnailUrl = ''; + + #imagingRepository = new UmbImagingRepository(this); + + #intersectionObserver?: IntersectionObserver; + + override render() { + return html` ${this.#renderThumbnail()} ${when(this._isLoading, () => this.#renderLoading())} `; + } + + override connectedCallback() { + super.connectedCallback(); + + if (this.loading === 'lazy') { + this.#intersectionObserver = new IntersectionObserver((entries) => { + if (entries[0].isIntersecting) { + this.#generateThumbnailUrl(); + this.#intersectionObserver?.disconnect(); + } + }); + this.#intersectionObserver.observe(this); + } else { + this.#generateThumbnailUrl(); + } + } + + override disconnectedCallback() { + super.disconnectedCallback(); + this.#intersectionObserver?.disconnect(); + } + + #renderLoading() { + return html`
    `; + } + + #renderThumbnail() { + if (this._isLoading) return nothing; + + return when( + this._thumbnailUrl, + () => + html`${this.alt}`, + () => html``, + ); + } + + async #generateThumbnailUrl() { + const { data } = await this.#imagingRepository.requestThumbnailUrls( + [this.unique], + this.height, + this.width, + this.mode, + ); + + this._thumbnailUrl = data?.[0]?.url ?? ''; + this._isLoading = false; + } + + static override styles = [ + UmbTextStyles, + css` + :host { + display: block; + position: relative; + overflow: hidden; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + } + + #loader { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + width: 100%; + } + + #figure { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + + background-image: url('data:image/svg+xml;charset=utf-8,'); + background-size: 10px 10px; + background-repeat: repeat; + } + + #icon { + width: 100%; + height: 100%; + font-size: var(--uui-size-8); + } + `, + ]; +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: UmbImagingThumbnailElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/components/index.ts b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/components/index.ts new file mode 100644 index 0000000000..60818ea906 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/components/index.ts @@ -0,0 +1 @@ +export * from './imaging-thumbnail.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/constants.ts index 2df4ed57ef..e808cedcd9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/constants.ts @@ -1 +1,2 @@ export const UMB_IMAGING_REPOSITORY_ALIAS = 'Umb.Repository.Imaging'; +export const UMB_IMAGING_STORE_ALIAS = 'Umb.Store.Imaging'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.repository.ts index acdfb5a2f5..c602078a52 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.repository.ts @@ -1,30 +1,62 @@ -import type { UmbImagingModel } from './types.js'; +import { UmbImagingCropMode, type UmbImagingModel } from './types.js'; import { UmbImagingServerDataSource } from './imaging.server.data.js'; -import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api'; +import { UMB_IMAGING_STORE_CONTEXT } from './imaging.store.token.js'; +import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; +import type { UmbMediaUrlModel } from '@umbraco-cms/backoffice/media'; -export class UmbImagingRepository extends UmbControllerBase implements UmbApi { +export class UmbImagingRepository extends UmbRepositoryBase implements UmbApi { + #dataStore?: typeof UMB_IMAGING_STORE_CONTEXT.TYPE; #itemSource: UmbImagingServerDataSource; constructor(host: UmbControllerHost) { super(host); this.#itemSource = new UmbImagingServerDataSource(host); + + this.consumeContext(UMB_IMAGING_STORE_CONTEXT, (instance) => { + this.#dataStore = instance; + }); } /** * Requests the items for the given uniques * @param {Array} uniques - * @return {*} * @memberof UmbImagingRepository */ - async requestResizedItems(uniques: Array, imagingModel?: UmbImagingModel) { + async requestResizedItems( + uniques: Array, + imagingModel?: UmbImagingModel, + ): Promise<{ data: UmbMediaUrlModel[] }> { if (!uniques.length) throw new Error('Uniques are missing'); + if (!this.#dataStore) throw new Error('Data store is missing'); - const { data, error: _error } = await this.#itemSource.getItems(uniques, imagingModel); - const error: any = _error; - return { data, error }; + const urls = new Map(); + + for (const unique of uniques) { + const existingCrop = this.#dataStore.getCrop(unique, imagingModel); + if (existingCrop !== undefined) { + urls.set(unique, existingCrop); + continue; + } + + const { data: urlModels, error } = await this.#itemSource.getItems([unique], imagingModel); + + if (error) { + console.error('[UmbImagingRepository] Error fetching items', error); + continue; + } + + const url = urlModels?.[0].url; + + this.#dataStore.addCrop(unique, url ?? '', imagingModel); + + if (url) { + urls.set(unique, url); + } + } + + return { data: Array.from(urls).map(([unique, url]) => ({ unique, url })) }; } /** @@ -32,12 +64,12 @@ export class UmbImagingRepository extends UmbControllerBase implements UmbApi { * @param {Array} uniques * @param {number} height * @param {number} width - * @returns {*} + * @param {ImageCropModeModel} mode - The crop mode * @memberof UmbImagingRepository */ - async requestThumbnailUrls(uniques: Array, height: number, width: number) { - const imagingModel = { height: height, width: width, mode: ImageCropModeModel.MIN }; - return await this.requestResizedItems(uniques, imagingModel); + async requestThumbnailUrls(uniques: Array, height: number, width: number, mode = UmbImagingCropMode.MIN) { + const imagingModel: UmbImagingModel = { height, width, mode }; + return this.requestResizedItems(uniques, imagingModel); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.store.token.ts b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.store.token.ts new file mode 100644 index 0000000000..cebe560072 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.store.token.ts @@ -0,0 +1,4 @@ +import type { UmbImagingStore } from './imaging.store.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_IMAGING_STORE_CONTEXT = new UmbContextToken('UmbImagingStore'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.store.ts b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.store.ts new file mode 100644 index 0000000000..c48a6a2d0d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.store.ts @@ -0,0 +1,47 @@ +import { UMB_IMAGING_STORE_CONTEXT } from './imaging.store.token.js'; +import type { UmbImagingModel } from './types.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; +import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; + +export class UmbImagingStore extends UmbContextBase implements UmbApi { + #data; + + constructor(host: UmbControllerHost) { + super(host, UMB_IMAGING_STORE_CONTEXT.toString()); + this.#data = new Map>(); + } + + /** + * Gets the data from the store. + */ + getData(unique: string) { + return this.#data.get(unique); + } + + /** + * Gets a specific crop if it exists. + */ + getCrop(unique: string, data?: UmbImagingModel) { + return this.#data.get(unique)?.get(this.#generateCropKey(data)); + } + + /** + * Adds a new crop to the store. + */ + addCrop(unique: string, urlInfo: string, data?: UmbImagingModel) { + if (!this.#data.has(unique)) { + this.#data.set(unique, new Map()); + } + this.#data.get(unique)?.set(this.#generateCropKey(data), urlInfo); + } + + /** + * Generates a unique key for the crop based on the width, height and mode. + */ + #generateCropKey(data?: UmbImagingModel) { + return data ? `${data.width}x${data.height};${data.mode}` : 'generic'; + } +} + +export default UmbImagingStore; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/index.ts b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/index.ts index afd4abe3b5..d05152d5dc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/index.ts @@ -1,2 +1,3 @@ +export * from './components/index.js'; export { UmbImagingRepository } from './imaging.repository.js'; export { UMB_IMAGING_REPOSITORY_ALIAS } from './constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/manifests.ts index 7f8ef13bb2..c27d00801c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/manifests.ts @@ -1,5 +1,5 @@ -import { UMB_IMAGING_REPOSITORY_ALIAS } from './constants.js'; -import type { ManifestRepository, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; +import { UMB_IMAGING_REPOSITORY_ALIAS, UMB_IMAGING_STORE_ALIAS } from './constants.js'; +import type { ManifestRepository, ManifestStore, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; const repository: ManifestRepository = { type: 'repository', @@ -8,4 +8,11 @@ const repository: ManifestRepository = { api: () => import('./imaging.repository.js'), }; -export const manifests: Array = [repository]; +const store: ManifestStore = { + type: 'store', + alias: UMB_IMAGING_STORE_ALIAS, + name: 'Imaging Store', + api: () => import('./imaging.store.js'), +}; + +export const manifests: Array = [repository, store]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/types.ts b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/types.ts index 41df2f5b95..0411433515 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/types.ts @@ -1,7 +1,9 @@ -import type { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api'; +import { ImageCropModeModel as UmbImagingCropMode } from '@umbraco-cms/backoffice/external/backend-api'; + +export { UmbImagingCropMode }; export interface UmbImagingModel { height?: number; width?: number; - mode?: ImageCropModeModel; + mode?: UmbImagingCropMode; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.context.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.context.ts index a2a5b053e1..4785fc9b8c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.context.ts @@ -1,39 +1,20 @@ import type { UmbMediaCollectionFilterModel, UmbMediaCollectionItemModel } from './types.js'; import { UMB_MEDIA_GRID_COLLECTION_VIEW_ALIAS } from './views/index.js'; -import { UmbImagingRepository } from '@umbraco-cms/backoffice/imaging'; -import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbDefaultCollectionContext } from '@umbraco-cms/backoffice/collection'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api'; export class UmbMediaCollectionContext extends UmbDefaultCollectionContext< UmbMediaCollectionItemModel, UmbMediaCollectionFilterModel > { - #imagingRepository: UmbImagingRepository; - - #thumbnailItems = new UmbArrayState([], (x) => x); - public readonly thumbnailItems = this.#thumbnailItems.asObservable(); + /** + * The thumbnail items that are currently displayed in the collection. + * @deprecated Use the `` element instead. + */ + public readonly thumbnailItems = this.items; constructor(host: UmbControllerHost) { super(host, UMB_MEDIA_GRID_COLLECTION_VIEW_ALIAS); - this.#imagingRepository = new UmbImagingRepository(host); - - this.observe(this.items, async (items) => { - if (!items?.length) return; - - const { data } = await this.#imagingRepository.requestResizedItems( - items.map((m) => m.unique), - { height: 400, width: 400, mode: ImageCropModeModel.MIN }, - ); - - this.#thumbnailItems.setValue( - items.map((item) => { - const thumbnail = data?.find((m) => m.unique === item.unique)?.url; - return { ...item, url: thumbnail }; - }), - ); - }); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/grid/media-grid-collection-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/grid/media-grid-collection-view.element.ts index 2345df4307..27042a9738 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/grid/media-grid-collection-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/grid/media-grid-collection-view.element.ts @@ -8,6 +8,8 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/modal'; import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; +import '@umbraco-cms/backoffice/imaging'; + @customElement('umb-media-grid-collection-view') export class UmbMediaGridCollectionViewElement extends UmbLitElement { @state() @@ -52,7 +54,7 @@ export class UmbMediaGridCollectionViewElement extends UmbLitElement { this.observe(this.#collectionContext.loading, (loading) => (this._loading = loading), '_observeLoading'); - this.observe(this.#collectionContext.thumbnailItems, (items) => (this._items = items), '_observeItems'); + this.observe(this.#collectionContext.items, (items) => (this._items = items), '_observeItems'); this.observe( this.#collectionContext.selection.selection, @@ -127,13 +129,7 @@ export class UmbMediaGridCollectionViewElement extends UmbLitElement { @selected=${() => this.#onSelect(item)} @deselected=${() => this.#onDeselect(item)} class="media-item"> - ${when( - item.url, - () => html`${item.name}`, - () => html``, - )} - - + `; } @@ -158,16 +154,6 @@ export class UmbMediaGridCollectionViewElement extends UmbLitElement { grid-auto-rows: 200px; gap: var(--uui-size-space-5); } - - img { - background-image: url('data:image/svg+xml;charset=utf-8,'); - background-size: 10px 10px; - background-repeat: repeat; - } - - umb-icon { - font-size: var(--uui-size-8); - } `, ]; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.element.ts index 4ebbdaa607..8377fa87d1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.element.ts @@ -1,7 +1,6 @@ import type { UmbMediaCardItemModel } from '../../modals/index.js'; import type { UmbMediaItemModel } from '../../repository/index.js'; import { UmbMediaPickerContext } from './input-media.context.js'; -import { UmbImagingRepository } from '@umbraco-cms/backoffice/imaging'; import { css, customElement, html, ifDefined, property, repeat, state } from '@umbraco-cms/backoffice/external/lit'; import { splitStringToArray } from '@umbraco-cms/backoffice/utils'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; @@ -11,6 +10,8 @@ import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; import { UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/modal'; import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; +import '@umbraco-cms/backoffice/imaging'; + const elementName = 'umb-input-media'; @customElement(elementName) @@ -87,7 +88,7 @@ export class UmbInputMediaElement extends UmbFormControlMixin) { @@ -123,8 +124,6 @@ export class UmbInputMediaElement extends UmbFormControlMixin !this._cards.find((card) => card.unique === item.unique)); if (selectedItems?.length && !missingCards.length) return; - if (!selectedItems?.length) { - this._cards = []; - return; - } - - const uniques = selectedItems.map((x) => x.unique); - - const { data: thumbnails } = await this.#imagingRepository.requestThumbnailUrls(uniques, 400, 400); - - this._cards = selectedItems.map((item) => { - const thumbnail = thumbnails?.find((x) => x.unique === item.unique); - return { - ...item, - src: thumbnail?.url, - }; - }); + this._cards = selectedItems ?? []; }); this.addValidator( 'rangeUnderflow', () => this.minMessage, - () => !!this.min && this.#pickerContext.getSelection().length < this.min, + () => !!this.min && this.selection.length < this.min, ); this.addValidator( 'rangeOverflow', () => this.maxMessage, - () => !!this.max && this.#pickerContext.getSelection().length > this.max, + () => !!this.max && this.selection.length > this.max, ); } - protected override getFormElement() { - return undefined; - } - - #pickableFilter: (item: UmbMediaItemModel) => boolean = (item) => { + #pickableFilter = (item: UmbMediaItemModel): boolean => { if (this.allowedContentTypeIds && this.allowedContentTypeIds.length > 0) { return this.allowedContentTypeIds.includes(item.mediaType.unique); } @@ -192,8 +172,9 @@ export class UmbInputMediaElement extends UmbFormControlMixin x.unique !== item.unique); } override render() { @@ -231,9 +212,10 @@ export class UmbInputMediaElement extends UmbFormControlMixin - ${item.src - ? html`${item.name}` - : html``} + ${this.#renderIsTrashed(item)} item.mediaKey); const { data: items } = await this.#itemRepository.requestItems(uniques); - const { data: thumbnails } = await this.#imagingRepository.requestThumbnailUrls(uniques, 400, 400); this._cards = this.items.map((item) => { const media = items?.find((x) => x.unique === item.mediaKey); - const thumbnail = thumbnails?.find((x) => x.unique === item.mediaKey); return { unique: item.key, media: item.mediaKey, name: media?.name ?? '', - src: thumbnail?.url, icon: media?.mediaType?.icon, isTrashed: media?.isTrashed ?? false, }; @@ -366,9 +362,10 @@ export class UmbInputRichMediaElement extends UUIFormControlMixin(UmbLitElement, const href = this._routeBuilder?.({ key: item.unique }); return html` - ${item.src - ? html`${item.name}` - : html``} + ${this.#renderIsTrashed(item)} { #mediaTreeRepository = new UmbMediaTreeRepository(this); // used to get file structure #mediaItemRepository = new UmbMediaItemRepository(this); // used to search - #imagingRepository = new UmbImagingRepository(this); // used to get image renditions #dataType?: { unique: string }; - @state() - private _filter: (item: UmbMediaCardItemModel) => boolean = () => true; - @state() private _selectableFilter: (item: UmbMediaCardItemModel) => boolean = () => true; @@ -63,7 +58,6 @@ export class UmbMediaPickerModalElement extends UmbModalBaseElement< override async connectedCallback(): Promise { super.connectedCallback(); - if (this.data?.filter) this._filter = this.data?.filter; if (this.data?.pickableFilter) this._selectableFilter = this.data?.pickableFilter; if (this.data?.startNode) { @@ -88,26 +82,10 @@ export class UmbMediaPickerModalElement extends UmbModalBaseElement< take: 100, }); - this.#mediaItemsCurrentFolder = await this.#mapMediaUrls(data?.items ?? []); + this.#mediaItemsCurrentFolder = data?.items ?? []; this.#filterMediaItems(); } - async #mapMediaUrls(items: Array): Promise> { - if (!items.length) return []; - - const { data } = await this.#imagingRepository.requestResizedItems( - items.map((item) => item.unique), - { height: 400, width: 400, mode: ImageCropModeModel.MIN }, - ); - - return items - .map((item): UmbMediaCardItemModel => { - const src = data?.find((media) => media.unique === item.unique)?.url; - return { ...item, src }; - }) - .filter((item) => this._filter(item)); - } - #onOpen(item: UmbMediaCardItemModel) { this._currentMediaEntity = { name: item.name, @@ -152,7 +130,7 @@ export class UmbMediaPickerModalElement extends UmbModalBaseElement< } // Map urls for search results as we are going to show for all folders (as long they aren't trashed). - this._mediaFilteredList = await this.#mapMediaUrls(data.filter((found) => found.isTrashed === false)); + this._mediaFilteredList = data.filter((found) => found.isTrashed === false); } #debouncedSearch = debounce(() => { @@ -240,9 +218,10 @@ export class UmbMediaPickerModalElement extends UmbModalBaseElement< @deselected=${() => this.#onDeselected(item)} ?selected=${this.value?.selection?.find((value) => value === item.unique)} ?selectable=${!disabled}> - ${item.src - ? html`${ifDefined(item.name)}` - : html``} + `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/manifests.ts index b26fbb35d9..7d62e0815c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/manifests.ts @@ -6,6 +6,7 @@ import type { ManifestTypes, } from '@umbraco-cms/backoffice/extension-registry'; import { UMB_ENTITY_IS_NOT_TRASHED_CONDITION_ALIAS } from '@umbraco-cms/backoffice/recycle-bin'; +import { UMB_CONTENT_HAS_PROPERTIES_WORKSPACE_CONDITION } from '@umbraco-cms/backoffice/content'; const workspace: ManifestWorkspaces = { type: 'workspace', @@ -55,6 +56,9 @@ const workspaceViews: Array = [ alias: 'Umb.Condition.WorkspaceAlias', match: workspace.alias, }, + { + alias: UMB_CONTENT_HAS_PROPERTIES_WORKSPACE_CONDITION, + }, ], }, { diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/input-member/input-member.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/input-member/input-member.element.ts index fdb67e7a79..20f5188702 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/input-member/input-member.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/input-member/input-member.element.ts @@ -9,7 +9,9 @@ import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/rou import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; -@customElement('umb-input-member') +const elementName = 'umb-input-member'; + +@customElement(elementName) export class UmbInputMemberElement extends UmbFormControlMixin( UmbLitElement, ) { @@ -72,7 +74,7 @@ export class UmbInputMemberElement extends UmbFormControlMixin) { @@ -123,24 +125,20 @@ export class UmbInputMemberElement extends UmbFormControlMixin this.minMessage, - () => !!this.min && this.#pickerContext.getSelection().length < this.min, + () => !!this.min && this.selection.length < this.min, ); this.addValidator( 'rangeOverflow', () => this.maxMessage, - () => !!this.max && this.#pickerContext.getSelection().length > this.max, + () => !!this.max && this.selection.length > this.max, ); this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(',')), '_observeSelection'); this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems), '_observeItems'); } - protected override getFormElement() { - return undefined; - } - - #pickableFilter: (item: UmbMemberItemModel) => boolean = (item) => { + #pickableFilter = (item: UmbMemberItemModel): boolean => { if (this.allowedContentTypeIds && this.allowedContentTypeIds.length > 0) { return this.allowedContentTypeIds.includes(item.memberType.unique); } @@ -154,7 +152,7 @@ export class UmbInputMemberElement extends UmbFormControlMixin= this.max) return nothing; - return html``; + if (this.selection.length >= this.max) return nothing; + return html` + + `; } #renderItem(item: UmbMemberItemModel) { @@ -190,7 +190,7 @@ export class UmbInputMemberElement extends UmbFormControlMixin ${this.#renderOpenButton(item)} - this.#removeItem(item)} label=${this.localize.term('general_remove')}> + this.#onRemove(item)} label=${this.localize.term('general_remove')}> `; @@ -210,7 +210,7 @@ export class UmbInputMemberElement extends UmbFormControlMixin `; + return html``; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/manifests.ts index 0c83475d5c..c47595c8fc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/manifests.ts @@ -6,6 +6,7 @@ import type { ManifestWorkspaceView, ManifestTypes, } from '@umbraco-cms/backoffice/extension-registry'; +import { UMB_CONTENT_HAS_PROPERTIES_WORKSPACE_CONDITION } from '@umbraco-cms/backoffice/content'; export const UMB_MEMBER_WORKSPACE_ALIAS = 'Umb.Workspace.Member'; @@ -58,6 +59,9 @@ export const workspaceViews: Array = [ alias: 'Umb.Condition.WorkspaceAlias', match: UMB_MEMBER_WORKSPACE_ALIAS, }, + { + alias: UMB_CONTENT_HAS_PROPERTIES_WORKSPACE_CONDITION, + }, ], }, { diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/components/index.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/components/index.ts new file mode 100644 index 0000000000..4ef883ab59 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/components/index.ts @@ -0,0 +1 @@ +export * from './input-multi-url/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/components/input-multi-url/index.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/components/input-multi-url/index.ts new file mode 100644 index 0000000000..d197dc0c3d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/components/input-multi-url/index.ts @@ -0,0 +1 @@ +export * from './input-multi-url.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/multi-url-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/components/input-multi-url/input-multi-url.element.ts similarity index 95% rename from src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/multi-url-picker.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/components/input-multi-url/input-multi-url.element.ts index b8a0b8b578..6c9e3eeba5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/multi-url-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/components/input-multi-url/input-multi-url.element.ts @@ -1,5 +1,5 @@ -import type { UmbLinkPickerLink } from '../link-picker-modal/types.js'; -import { UMB_LINK_PICKER_MODAL } from '../link-picker-modal/link-picker-modal.token.js'; +import type { UmbLinkPickerLink } from '../../link-picker-modal/types.js'; +import { UMB_LINK_PICKER_MODAL } from '../../link-picker-modal/link-picker-modal.token.js'; import { css, customElement, html, property, repeat, state } from '@umbraco-cms/backoffice/external/lit'; import { simpleHashCode } from '@umbraco-cms/backoffice/observable-api'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; @@ -18,9 +18,9 @@ import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui'; * @fires blur - when the input loses focus * @fires focus - when the input gains focus */ -const elementName = 'umb-multi-url-picker'; +const elementName = 'umb-input-multi-url'; @customElement(elementName) -export class UmbMultiUrlPickerElement extends UUIFormControlMixin(UmbLitElement, '') { +export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement, '') { #sorter = new UmbSorterController(this, { getUniqueOfElement: (element) => { return element.id; @@ -296,6 +296,6 @@ export class UmbMultiUrlPickerElement extends UUIFormControlMixin(UmbLitElement, declare global { interface HTMLElementTagNameMap { - [elementName]: UmbMultiUrlPickerElement; + [elementName]: UmbInputMultiUrlElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/components/input-multi-url/input-multi-url.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/components/input-multi-url/input-multi-url.stories.ts new file mode 100644 index 0000000000..1017d7d56d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/components/input-multi-url/input-multi-url.stories.ts @@ -0,0 +1,15 @@ +import type { Meta, StoryObj } from '@storybook/web-components'; +import './input-multi-url.element.js'; +import type { UmbInputMultiUrlElement } from './input-multi-url.element.js'; + +const meta: Meta = { + title: 'Components/Inputs/Multi URL', + component: 'umb-input-multi-url', +}; + +export default meta; +type Story = StoryObj; + +export const Overview: Story = { + args: {}, +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/index.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/index.ts new file mode 100644 index 0000000000..896c42a50a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/index.ts @@ -0,0 +1,2 @@ +export * from './components/index.js'; +export * from './link-picker-modal/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/link-picker-modal/index.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/link-picker-modal/index.ts new file mode 100644 index 0000000000..9accd1cfe1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/link-picker-modal/index.ts @@ -0,0 +1,3 @@ +export type * from './types.js'; +export * from './constants.js'; +export * from './link-picker-modal.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/index.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/index.ts deleted file mode 100644 index 2d2ffcb054..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './multi-url-picker.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/multi-url-picker.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/multi-url-picker.stories.ts deleted file mode 100644 index 2772b6ccd2..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/multi-url-picker.stories.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/web-components'; -import './multi-url-picker.element.js'; -import type { UmbMultiUrlPickerElement } from './multi-url-picker.element.js'; - -const meta: Meta = { - title: 'Components/Inputs/Multi URL', - component: 'umb-input-multi-url', -}; - -export default meta; -type Story = StoryObj; - -export const Overview: Story = { - args: {}, -}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/package.json b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/package.json new file mode 100644 index 0000000000..fa59153208 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/package.json @@ -0,0 +1,8 @@ +{ + "name": "@umbraco-backoffice/multi-url-picker", + "private": true, + "type": "module", + "scripts": { + "build": "vite build" + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/property-editor-ui-multi-url-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/property-editor-ui-multi-url-picker.element.ts index 70a7cf3dff..bf8dd06020 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/property-editor-ui-multi-url-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/property-editor-ui-multi-url-picker.element.ts @@ -1,5 +1,5 @@ import type { UmbLinkPickerLink } from '../link-picker-modal/types.js'; -import type { UmbMultiUrlPickerElement } from '../multi-url-picker/multi-url-picker.element.js'; +import type { UmbInputMultiUrlElement } from '../components/input-multi-url/index.js'; import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; @@ -8,8 +8,7 @@ import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/ import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui'; -// import of local component -import '../multi-url-picker/multi-url-picker.element.js'; +import '../components/input-multi-url/index.js'; /** * @element umb-property-editor-ui-multi-url-picker @@ -60,14 +59,14 @@ export class UmbPropertyEditorUIMultiUrlPickerElement extends UmbLitElement impl }); } - #onChange(event: CustomEvent & { target: UmbMultiUrlPickerElement }) { + #onChange(event: CustomEvent & { target: UmbInputMultiUrlElement }) { this.value = event.target.urls; this.dispatchEvent(new UmbPropertyValueChangeEvent()); } override render() { return html` - - + `; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/vite.config.ts new file mode 100644 index 0000000000..b943c871c7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'vite'; +import { rmSync } from 'fs'; +import { getDefaultConfig } from '../../vite-config-base'; + +const dist = '../../../dist-cms/packages/multi-url-picker'; + +// delete the unbundled dist folder +rmSync(dist, { recursive: true, force: true }); + +export default defineConfig({ + ...getDefaultConfig({ dist }), +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/content-picker/components/input-content/input-content.element.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/content-picker/components/input-content/input-content.element.ts index 5396d89c1d..b404d9a51e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/content-picker/components/input-content/input-content.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/content-picker/components/input-content/input-content.element.ts @@ -2,66 +2,73 @@ import type { UmbContentPickerSource } from '../../types.js'; import { css, html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UmbInputDocumentElement } from '@umbraco-cms/backoffice/document'; -import type { UmbInputMediaElement } from '@umbraco-cms/backoffice/media'; -import type { UmbInputMemberElement } from '@umbraco-cms/backoffice/member'; import type { UmbReferenceByUniqueAndType } from '@umbraco-cms/backoffice/models'; import type { UmbTreeStartNode } from '@umbraco-cms/backoffice/tree'; import { splitStringToArray } from '@umbraco-cms/backoffice/utils'; import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; const elementName = 'umb-input-content'; + @customElement(elementName) export class UmbInputContentElement extends UmbFormControlMixin( UmbLitElement, ) { - protected override getFormElement() { - return undefined; - } - - private _type: UmbContentPickerSource['type'] = 'content'; - @property({ type: Object, attribute: false }) + @property() public set type(newType: UmbContentPickerSource['type']) { - const oldType = this._type; - if (newType?.toLowerCase() !== this._type) { - this._type = newType?.toLowerCase() as UmbContentPickerSource['type']; + const oldType = this.#type; + if (newType?.toLowerCase() !== this.#type) { + this.#type = newType?.toLowerCase() as UmbContentPickerSource['type']; this.requestUpdate('type', oldType); } } public get type(): UmbContentPickerSource['type'] { - return this._type; + return this.#type; } + #type: UmbContentPickerSource['type'] = 'content'; @property({ type: Number }) min = 0; + @property({ type: String, attribute: 'min-message' }) + minMessage = 'This field need more items'; + @property({ type: Number }) max = 0; + @property({ type: String, attribute: 'max-message' }) + maxMessage = 'This field exceeds the allowed amount of items'; + @property({ type: Object, attribute: false }) startNode?: UmbTreeStartNode; - private _allowedContentTypeIds: Array = []; @property() public set allowedContentTypeIds(value: string) { - this._allowedContentTypeIds = value ? value.split(',') : []; + this.#allowedContentTypeIds = value ? value.split(',') : []; } public get allowedContentTypeIds(): string { - return this._allowedContentTypeIds.join(','); + return this.#allowedContentTypeIds.join(','); } + #allowedContentTypeIds: Array = []; @property({ type: Boolean }) showOpenButton?: boolean; - #entityTypeLookup = { content: 'document', media: 'media', member: 'member' }; + @property({ type: Array }) + public set selection(values: Array) { + this.#selection = values?.map((item) => item.unique) ?? []; + } + public get selection(): Array { + return this.#selection.map((id) => ({ type: this.#entityTypeLookup[this.#type], unique: id })); + } - // TODO: to be consistent with other pickers, this should be named `selection` [NL] + /** @deprecated Please use `selection` instead. This property will be removed in Umbraco 15. */ @property({ type: Array }) public set items(items: Array) { - this.#selection = items?.map((item) => item.unique) ?? []; + this.selection = items; } + /** @deprecated Please use `selection` instead. This property will be removed in Umbraco 15. */ public get items(): Array { - return this.#selection.map((id) => ({ type: this.#entityTypeLookup[this._type], unique: id })); + return this.selection; } @property({ type: String }) @@ -72,38 +79,22 @@ export class UmbInputContentElement extends UmbFormControlMixin 0 ? this.#selection.join(',') : undefined; } + #entityTypeLookup = { content: 'document', media: 'media', member: 'member' }; + #selection: Array = []; - #onChange(event: CustomEvent) { - switch (this._type) { - case 'content': - { - const input = event.target as UmbInputDocumentElement; - this.#selection = input.selection; - this.value = input.selection.join(','); - } - break; - case 'media': { - const input = event.target as UmbInputMediaElement; - this.#selection = input.selection; - this.value = input.selection.join(','); - break; - } - case 'member': { - const input = event.target as UmbInputMemberElement; - this.#selection = input.selection; - this.value = input.selection.join(','); - break; - } - default: - break; - } + override firstUpdated() { + this.addFormControlElement(this.shadowRoot!.querySelector(`umb-input-${this.#entityTypeLookup[this.#type]}`)!); + } + #onChange(event: CustomEvent & { target: { selection: string[] | undefined } }) { + this.#selection = event.target.selection ?? []; + this.value = this.#selection.join(','); this.dispatchEvent(new UmbChangeEvent()); } override render() { - switch (this._type) { + switch (this.#type) { case 'content': return this.#renderDocumentPicker(); case 'media': @@ -116,34 +107,46 @@ export class UmbInputContentElement extends UmbFormControlMixin`; + return html` + + `; } #renderMediaPicker() { - return html``; + return html` + + `; } #renderMemberPicker() { - return html``; + return html` + + `; } static override styles = [ @@ -156,7 +159,7 @@ export class UmbInputContentElement extends UmbFormControlMixin { let element: UmbInputContentElement; beforeEach(async () => { - element = await fixture(html` `); + element = await fixture(html``); }); it('is defined with its own instance', () => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/content-picker/property-editor-ui-content-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/content-picker/property-editor-ui-content-picker.element.ts index 6c250bb1fa..c40a756769 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/content-picker/property-editor-ui-content-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/content-picker/property-editor-ui-content-picker.element.ts @@ -3,25 +3,39 @@ import type { UmbInputContentElement } from './components/input-content/index.js import type { UmbContentPickerSource, UmbContentPickerSourceType } from './types.js'; import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; -import { UMB_ENTITY_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; -import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; -import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { UMB_DOCUMENT_ENTITY_TYPE } from '@umbraco-cms/backoffice/document'; +import { UMB_ENTITY_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UMB_MEDIA_ENTITY_TYPE } from '@umbraco-cms/backoffice/media'; import { UMB_MEMBER_ENTITY_TYPE } from '@umbraco-cms/backoffice/member'; +import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; +import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbTreeStartNode } from '@umbraco-cms/backoffice/tree'; // import of local component import './components/input-content/index.js'; +type UmbContentPickerValueType = UmbInputContentElement['selection']; + +const elementName = 'umb-property-editor-ui-content-picker'; + /** * @element umb-property-editor-ui-content-picker */ -@customElement('umb-property-editor-ui-content-picker') -export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement { +@customElement(elementName) +export class UmbPropertyEditorUIContentPickerElement + extends UmbFormControlMixin(UmbLitElement, undefined) + implements UmbPropertyEditorUiElement +{ @property({ type: Array }) - value: UmbInputContentElement['items'] = []; + public override set value(value: UmbContentPickerValueType | undefined) { + this.#value = value; + } + public override get value(): UmbContentPickerValueType | undefined { + return this.#value; + } + #value?: UmbContentPickerValueType = []; @state() _type: UmbContentPickerSource['type'] = 'content'; @@ -29,9 +43,15 @@ export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement imple @state() _min = 0; + @state() + _minMessage = ''; + @state() _max = Infinity; + @state() + _maxMessage = ''; + @state() _allowedContentTypeUniques?: string | null; @@ -64,15 +84,28 @@ export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement imple this.#dynamicRoot = startNode.dynamicRoot; } - this._min = Number(config.getValueByAlias('minNumber')) || 0; - this._max = Number(config.getValueByAlias('maxNumber')) || Infinity; + this._min = this.#parseInt(config.getValueByAlias('minNumber'), 0); + this._max = this.#parseInt(config.getValueByAlias('maxNumber'), Infinity); this._allowedContentTypeUniques = config.getValueByAlias('filter'); this._showOpenButton = config.getValueByAlias('showOpenButton'); + + this._minMessage = `${this.localize.term('validation_minCount')} ${this._min} ${this.localize.term('validation_items')}`; + this._maxMessage = `${this.localize.term('validation_maxCount')} ${this._max} ${this.localize.term('validation_itemsSelected')}`; + + // NOTE: Run validation immediately, to notify if the value is outside of min/max range. [LK] + if (this._min > 0 || this._max < Infinity) { + this.checkValidity(); + } } - override connectedCallback() { - super.connectedCallback(); + #parseInt(value: unknown, fallback: number): number { + const num = Number(value); + return !isNaN(num) && num > 0 ? num : fallback; + } + + override firstUpdated() { + this.addFormControlElement(this.shadowRoot!.querySelector('umb-input-content')!); this.#setPickerRootUnique(); } @@ -96,7 +129,7 @@ export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement imple } #onChange(event: CustomEvent & { target: UmbInputContentElement }) { - this.value = event.target.items; + this.value = event.target.selection; this.dispatchEvent(new UmbPropertyValueChangeEvent()); } @@ -106,22 +139,26 @@ export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement imple ? { unique: this._rootUnique, entityType: this._rootEntityType } : undefined; - return html``; + return html` + + `; } } -export default UmbPropertyEditorUIContentPickerElement; +export { UmbPropertyEditorUIContentPickerElement as element }; declare global { interface HTMLElementTagNameMap { - 'umb-property-editor-ui-content-picker': UmbPropertyEditorUIContentPickerElement; + [elementName]: UmbPropertyEditorUIContentPickerElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/block-configuration/property-editor-ui-block-rte-type-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/block-configuration/property-editor-ui-block-rte-type-configuration.element.ts index 0547db8671..2b59427770 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/block-configuration/property-editor-ui-block-rte-type-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/block-configuration/property-editor-ui-block-rte-type-configuration.element.ts @@ -1,7 +1,6 @@ -import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type'; import { UmbInputBlockTypeElement } from '@umbraco-cms/backoffice/block-type'; import { UMB_BLOCK_RTE_TYPE } from '@umbraco-cms/backoffice/block-rte'; -import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; +import type { UmbBlockTypeBaseModel, UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, property, state, nothing } from '@umbraco-cms/backoffice/external/lit'; import { UmbPropertyValueChangeEvent, diff --git a/src/Umbraco.Web.UI.Client/src/packages/ufm/components/ufm-render/ufm-render.element.ts b/src/Umbraco.Web.UI.Client/src/packages/ufm/components/ufm-render/ufm-render.element.ts index 570669c6bc..65f33c51b4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/ufm/components/ufm-render/ufm-render.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/ufm/components/ufm-render/ufm-render.element.ts @@ -97,6 +97,7 @@ export class UmbUfmRenderElement extends UmbLitElement { css` * { max-width: 100%; + word-wrap: break-word; } pre { diff --git a/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts b/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts index 14793f279a..3015aaa2c8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts @@ -37,7 +37,7 @@ export class UmbUmbracoNewsDashboardElement extends UmbLitElement {
diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/constants.ts new file mode 100644 index 0000000000..751657e83d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/constants.ts @@ -0,0 +1,2 @@ +export const UMB_USER_CONFIG_REPOSITORY_ALIAS = 'Umb.Repository.User.Config'; +export const UMB_USER_CONFIG_STORE_ALIAS = 'Umb.Store.User.Config'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/index.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/index.ts new file mode 100644 index 0000000000..c848c34591 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/index.ts @@ -0,0 +1,2 @@ +export * from './constants.js'; +export * from './user-config.repository.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/manifests.ts new file mode 100644 index 0000000000..0477378456 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/manifests.ts @@ -0,0 +1,18 @@ +import { UMB_USER_CONFIG_REPOSITORY_ALIAS, UMB_USER_CONFIG_STORE_ALIAS } from './constants.js'; +import type { ManifestRepository, ManifestStore, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; + +const store: ManifestStore = { + type: 'store', + alias: UMB_USER_CONFIG_STORE_ALIAS, + name: 'User Config Store', + api: () => import('./user-config.store.js'), +}; + +const repository: ManifestRepository = { + type: 'repository', + alias: UMB_USER_CONFIG_REPOSITORY_ALIAS, + name: 'User Config Repository', + api: () => import('./user-config.repository.js'), +}; + +export const manifests: Array = [repository, store]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.repository.ts new file mode 100644 index 0000000000..1dc05c25f3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.repository.ts @@ -0,0 +1,57 @@ +import type { UmbUserConfigurationModel } from '../../types.js'; +import { UmbUserConfigServerDataSource } from './user-config.server.data-source.js'; +import { UMB_USER_CONFIG_STORE_CONTEXT } from './user-config.store.token.js'; +import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; +import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { Observable } from '@umbraco-cms/backoffice/observable-api'; + +export class UmbUserConfigRepository extends UmbRepositoryBase implements UmbApi { + #dataStore?: typeof UMB_USER_CONFIG_STORE_CONTEXT.TYPE; + #dataSource = new UmbUserConfigServerDataSource(this); + + constructor(host: UmbControllerHost) { + super(host); + this.consumeContext(UMB_USER_CONFIG_STORE_CONTEXT, (store) => { + this.#dataStore = store; + this.#init(); + }); + } + + async #init() { + // Check if the store already has data + if (this.#dataStore?.getState()) { + return; + } + + const { data } = await this.#dataSource.getUserConfig(); + + if (data) { + this.#dataStore?.update(data); + } + } + + /** + * Subscribe to the entire user configuration. + */ + all() { + if (!this.#dataStore) { + throw new Error('Data store not initialized'); + } + + return this.#dataStore.all(); + } + + /** + * Subscribe to a part of the user configuration. + */ + part(part: Part): Observable { + if (!this.#dataStore) { + throw new Error('Data store not initialized'); + } + + return this.#dataStore.part(part); + } +} + +export default UmbUserConfigRepository; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.server.data-source.ts new file mode 100644 index 0000000000..a85a0f3311 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.server.data-source.ts @@ -0,0 +1,19 @@ +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UserService } from '@umbraco-cms/backoffice/external/backend-api'; +import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; + +export class UmbUserConfigServerDataSource { + #host; + + constructor(host: UmbControllerHost) { + this.#host = host; + } + + /** + * Get the user configuration. + * @memberof UmbUserConfigServerDataSource + */ + getUserConfig() { + return tryExecuteAndNotify(this.#host, UserService.getUserConfiguration()); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.store.token.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.store.token.ts new file mode 100644 index 0000000000..44a4f281fb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.store.token.ts @@ -0,0 +1,4 @@ +import type { UmbUserConfigStore } from './user-config.store.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_USER_CONFIG_STORE_CONTEXT = new UmbContextToken('UmbUserConfigStore'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.store.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.store.ts new file mode 100644 index 0000000000..0a7a65c45c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/config/user-config.store.ts @@ -0,0 +1,12 @@ +import type { UmbUserConfigurationModel } from '../../types.js'; +import { UMB_USER_CONFIG_STORE_CONTEXT } from './user-config.store.token.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbStoreObjectBase } from '@umbraco-cms/backoffice/store'; + +export class UmbUserConfigStore extends UmbStoreObjectBase { + constructor(host: UmbControllerHost) { + super(host, UMB_USER_CONFIG_STORE_CONTEXT.toString()); + } +} + +export default UmbUserConfigStore; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/manifests.ts index d85871ebf3..d4a39f20e3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/manifests.ts @@ -1,5 +1,6 @@ import { manifests as avatarManifests } from './avatar/manifests.js'; import { manifests as changePasswordManifests } from './change-password/manifests.js'; +import { manifests as configManifests } from './config/manifests.js'; import { manifests as detailManifests } from './detail/manifests.js'; import { manifests as disableManifests } from './disable/manifests.js'; import { manifests as enableManifests } from './enable/manifests.js'; @@ -12,6 +13,7 @@ export const manifests: Array = [ ...itemManifests, ...avatarManifests, ...changePasswordManifests, + ...configManifests, ...disableManifests, ...enableManifests, ...unlockManifests, diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/types.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/types.ts index 35be2ba1ec..b092e1b3e8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/types.ts @@ -1,6 +1,10 @@ import type { UmbUserEntityType } from './entity.js'; import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models'; -import { UserStateModel, type UserTwoFactorProviderModel } from '@umbraco-cms/backoffice/external/backend-api'; +import { + type UserConfigurationResponseModel, + UserStateModel, + type UserTwoFactorProviderModel, +} from '@umbraco-cms/backoffice/external/backend-api'; export type UmbUserStateEnum = UserStateModel; export const UmbUserStateEnum = UserStateModel; @@ -32,3 +36,5 @@ export interface UmbUserStartNodesModel { } export type UmbUserMfaProviderModel = UserTwoFactorProviderModel; + +export type UmbUserConfigurationModel = UserConfigurationResponseModel; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-profile-settings/user-workspace-profile-settings.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-profile-settings/user-workspace-profile-settings.element.ts index 82200e78fc..b213e50752 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-profile-settings/user-workspace-profile-settings.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-profile-settings/user-workspace-profile-settings.element.ts @@ -1,6 +1,6 @@ import { UMB_USER_WORKSPACE_CONTEXT } from '../../user-workspace.context-token.js'; import type { UmbUserDetailModel } from '../../../types.js'; -import { html, customElement, state, ifDefined, css } from '@umbraco-cms/backoffice/external/lit'; +import { html, customElement, state, ifDefined, css, nothing } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; @@ -11,6 +11,9 @@ export class UmbUserWorkspaceProfileSettingsElement extends UmbLitElement { @state() private _user?: UmbUserDetailModel; + @state() + private _usernameIsEmail = true; + #userWorkspaceContext?: typeof UMB_USER_WORKSPACE_CONTEXT.TYPE; constructor() { @@ -19,9 +22,34 @@ export class UmbUserWorkspaceProfileSettingsElement extends UmbLitElement { this.consumeContext(UMB_USER_WORKSPACE_CONTEXT, (instance) => { this.#userWorkspaceContext = instance; this.observe(this.#userWorkspaceContext.data, (user) => (this._user = user), 'umbUserObserver'); + this.observe( + this.#userWorkspaceContext.configRepository.part('usernameIsEmail'), + (usernameIsEmail) => (this._usernameIsEmail = usernameIsEmail), + 'umbUsernameIsEmailObserver', + ); }); } + #onEmailChange(event: UmbChangeEvent) { + const target = event.target as HTMLInputElement; + + if (typeof target?.value === 'string') { + this.#userWorkspaceContext?.updateProperty('email', target.value); + + if (this._usernameIsEmail) { + this.#userWorkspaceContext?.updateProperty('userName', target.value); + } + } + } + + #onUsernameChange(event: UmbChangeEvent) { + const target = event.target as HTMLInputElement; + + if (typeof target?.value === 'string') { + this.#userWorkspaceContext?.updateProperty('userName', target.value); + } + } + #onLanguageChange(event: UmbChangeEvent) { const target = event.target as UmbUiCultureInputElement; @@ -33,23 +61,50 @@ export class UmbUserWorkspaceProfileSettingsElement extends UmbLitElement { override render() { return html`
Profile
- ${this.#renderEmailProperty()} ${this.#renderUILanguageProperty()} + ${this.#renderEmailProperty()} ${this.#renderUsernameProperty()} ${this.#renderUILanguageProperty()}
`; } #renderEmailProperty() { return html` - + `; } + #renderUsernameProperty() { + if (this._usernameIsEmail) { + return nothing; + } + + return html` + + + + `; + } + #renderUILanguageProperty() { return html` (undefined); #currentData = new UmbObjectState(undefined); diff --git a/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index 43b3583c02..53b2db2515 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/tsconfig.json @@ -82,6 +82,7 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js "@umbraco-cms/backoffice/menu": ["./src/packages/core/menu/index.ts"], "@umbraco-cms/backoffice/modal": ["./src/packages/core/modal/index.ts"], "@umbraco-cms/backoffice/models": ["./src/packages/core/models/index.ts"], + "@umbraco-cms/backoffice/multi-url-picker": ["./src/packages/multi-url-picker/index.ts"], "@umbraco-cms/backoffice/notification": ["./src/packages/core/notification/index.ts"], "@umbraco-cms/backoffice/object-type": ["./src/packages/object-type/index.ts"], "@umbraco-cms/backoffice/package": ["./src/packages/packages/package/index.ts"],