diff --git a/package-lock.json b/package-lock.json index b7e325721b0cfe0e22a140792806184c38122c24..f7620ff19c88bf6c050ca2c1e73dbe67f8533483 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,28 +20,31 @@ "@angular/router": "~13.3.0", "@ngx-translate/core": "^14.0.0", "@ngx-translate/http-loader": "^6.0.0", - "@tiptap/core": "^2.0.0-beta.175", - "@tiptap/extension-blockquote": "^2.0.0-beta.26", - "@tiptap/extension-bubble-menu": "^2.0.0-beta.56", - "@tiptap/extension-bullet-list": "^2.0.0-beta.26", - "@tiptap/extension-color": "^2.0.0-beta.9", - "@tiptap/extension-floating-menu": "^2.0.0-beta.51", - "@tiptap/extension-font-family": "^2.0.0-beta.21", - "@tiptap/extension-heading": "^2.0.0-beta.26", - "@tiptap/extension-highlight": "^2.0.0-beta.33", - "@tiptap/extension-image": "^2.0.0-beta.27", - "@tiptap/extension-list-item": "^2.0.0-beta.20", - "@tiptap/extension-ordered-list": "^2.0.0-beta.27", - "@tiptap/extension-paragraph": "^2.0.0-beta.23", - "@tiptap/extension-strike": "~2.0.0-beta.27", - "@tiptap/extension-subscript": "^2.0.0-beta.10", - "@tiptap/extension-superscript": "^2.0.0-beta.10", - "@tiptap/extension-text-align": "^2.0.0-beta.29", - "@tiptap/extension-text-style": "^2.0.0-beta.23", - "@tiptap/extension-underline": "^2.0.0-beta.23", - "@tiptap/starter-kit": "2.0.0-beta.184", - "ngx-tiptap": "^3.0.4", - "prosemirror-state": "^1.3.4", + "@tiptap/core": "^2.0.0-beta.182", + "@tiptap/extension-blockquote": "^2.0.0-beta.29", + "@tiptap/extension-bold": "^2.0.0-beta.28", + "@tiptap/extension-bubble-menu": "^2.0.0-beta.61", + "@tiptap/extension-bullet-list": "^2.0.0-beta.29", + "@tiptap/extension-color": "^2.0.0-beta.12", + "@tiptap/extension-document": "^2.0.0-beta.17", + "@tiptap/extension-floating-menu": "^2.0.0-beta.56", + "@tiptap/extension-font-family": "^2.0.0-beta.24", + "@tiptap/extension-heading": "^2.0.0-beta.29", + "@tiptap/extension-highlight": "^2.0.0-beta.35", + "@tiptap/extension-image": "^2.0.0-beta.30", + "@tiptap/extension-italic": "^2.0.0-beta.28", + "@tiptap/extension-list-item": "^2.0.0-beta.23", + "@tiptap/extension-ordered-list": "^2.0.0-beta.30", + "@tiptap/extension-paragraph": "^2.0.0-beta.26", + "@tiptap/extension-strike": "~2.0.0-beta.29", + "@tiptap/extension-subscript": "^2.0.0-beta.13", + "@tiptap/extension-superscript": "^2.0.0-beta.13", + "@tiptap/extension-text": "^2.0.0-beta.17", + "@tiptap/extension-text-align": "^2.0.0-beta.31", + "@tiptap/extension-text-style": "^2.0.0-beta.26", + "@tiptap/extension-underline": "^2.0.0-beta.25", + "ngx-tiptap": "^5.0.0", + "prosemirror-state": "^1.4.1", "rxjs": "^7.4.0", "testcafe": "^1.18.6", "tslib": "^2.1.0", @@ -51,7 +54,7 @@ "@angular-devkit/build-angular": "~13.3.0", "@angular/cli": "~13.3.0", "@angular/compiler-cli": "~13.3.0", - "@iqb/eslint-config": "^1.1.1", + "@iqb/eslint-config": "^2.1.1", "@types/jasmine": "~3.6.0", "@types/node": "^12.11.7", "iqb-dev-components": "^1.4.0", @@ -2464,15 +2467,16 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.3.tgz", - "integrity": "sha512-uGo44hIwoLGNyduRpjdEpovcbMdd+Nv7amtmJxnKmI8xj6yd5LncmSwDa5NgX/41lIFJtkjD6YdVfgEzPfJ5UA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", "dev": true, + "peer": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.3.2", - "globals": "^13.9.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -2488,6 +2492,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2503,13 +2508,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, + "peer": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -2525,6 +2532,7 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -2536,13 +2544,15 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2555,6 +2565,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "peer": true, "engines": { "node": ">=10" }, @@ -2573,6 +2584,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", "dev": true, + "peer": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", @@ -2586,19 +2598,19 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@iqb/eslint-config": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@iqb/eslint-config/-/eslint-config-1.1.1.tgz", - "integrity": "sha512-KaAdhBSnCXXfxeQRI51tZxgW58FXkAvHSFvZE38b+gvKPZ5YVYXuX0dlEEu7rYezeW7jCOk6zRtdkUbB5DJVyg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@iqb/eslint-config/-/eslint-config-2.1.1.tgz", + "integrity": "sha512-F94TVoZ0xT4mvxKdr9efDiRG74M0TQERCH2Eh4AL1u1JseDqYwbNhcnItadvdjho9USt9sMLDdxwOnADfHkE1g==", "dev": true, - "dependencies": { - "@typescript-eslint/eslint-plugin": "^5.19.0", - "@typescript-eslint/parser": "^5.19.0", - "eslint": "^8.13.0", - "eslint-config-airbnb-typescript": "^17.0.0", - "eslint-plugin-import": "^2.26.0" + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.30.0", + "@typescript-eslint/parser": "^5.30.0", + "eslint": "^8.18.0", + "eslint-config-airbnb-typescript": "^17.0.0" } }, "node_modules/@istanbuljs/load-nyc-config": { @@ -2859,24 +2871,17 @@ } }, "node_modules/@tiptap/core": { - "version": "2.0.0-beta.175", - "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.0.0-beta.175.tgz", - "integrity": "sha512-dDf+2GtifskNLysn49kaCIz0o5hf6VDZ8J7jSQAfoPDEkEkfw9OKhWrR7NzWW6J34CSJreFDRiWkGt8Qz283Vg==", - "dependencies": { - "@types/prosemirror-commands": "^1.0.4", - "@types/prosemirror-keymap": "^1.0.4", - "@types/prosemirror-model": "^1.16.0", - "@types/prosemirror-schema-list": "^1.0.3", - "@types/prosemirror-state": "^1.2.8", - "@types/prosemirror-transform": "^1.1.5", - "@types/prosemirror-view": "^1.23.1", - "prosemirror-commands": "^1.2.1", - "prosemirror-keymap": "^1.1.5", - "prosemirror-model": "^1.16.1", - "prosemirror-schema-list": "^1.1.6", - "prosemirror-state": "^1.3.4", - "prosemirror-transform": "^1.3.3", - "prosemirror-view": "^1.23.6" + "version": "2.0.0-beta.182", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.0.0-beta.182.tgz", + "integrity": "sha512-MZGkMGnVnWhBzjvpBNwQ9zBz38ndi3Irbf90uCTSArR0kaCVkW4vmyuPuOXd+0SO8Yv/l5oyDdOCpaG3rnQYfw==", + "dependencies": { + "prosemirror-commands": "1.3.0", + "prosemirror-keymap": "1.2.0", + "prosemirror-model": "1.18.1", + "prosemirror-schema-list": "1.2.0", + "prosemirror-state": "1.4.1", + "prosemirror-transform": "1.6.0", + "prosemirror-view": "1.26.2" }, "funding": { "type": "github", @@ -2884,9 +2889,9 @@ } }, "node_modules/@tiptap/extension-blockquote": { - "version": "2.0.0-beta.26", - "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.26.tgz", - "integrity": "sha512-A6yjcYovONJfOjQFk6vDYXswaCdCtCwjL7w9VTB0R2DLTuJvvRt9DWN0IDcMrj5G+aMgDq4GUUTitv+2Y8krDg==", + "version": "2.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.29.tgz", + "integrity": "sha512-zMYT5TtpKWav9VhTn4JLyMvXmhEdbD6on0MdhcTjRm0I5ugyR4ZbJwh2aelM7G9DZVYzB8jZU18OSDJmo7Af7w==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -2896,9 +2901,9 @@ } }, "node_modules/@tiptap/extension-bold": { - "version": "2.0.0-beta.26", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.26.tgz", - "integrity": "sha512-pnO0I5sEQM3pmowjMGQ74adLzvc6HqGyLyqMizaGMicPu9uTYlSdId+qckYEEgPwPMaEShtv2Vg+ZHs7KVqfcg==", + "version": "2.0.0-beta.28", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.28.tgz", + "integrity": "sha512-DY8GOzw9xjmTFrnvTbgHUNxTnDfKrkDgrhe0SUvdkT2udntWp8umPdhPiD3vczLgHOJw6tX68qMRjbsR1ZPcHQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -2908,12 +2913,12 @@ } }, "node_modules/@tiptap/extension-bubble-menu": { - "version": "2.0.0-beta.56", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.56.tgz", - "integrity": "sha512-nZozwauICdaNPmDPrSn1JFd/9/2rLtK8i2vBOcqxWHObVROvu8ZlJspnrJv23vS6P7/ZO3e/QLVHpnn+1yVq3g==", + "version": "2.0.0-beta.61", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.61.tgz", + "integrity": "sha512-T3Yx+y1sUnXAJjK1CUfsQewSxOpDca9KzKqN2H9c9RZ9UlorR9XmZg6YYW7m9a7adeihj+o3cCO9jRd8dV+nnA==", "dependencies": { - "prosemirror-state": "^1.3.4", - "prosemirror-view": "^1.23.6", + "prosemirror-state": "1.4.1", + "prosemirror-view": "1.26.2", "tippy.js": "^6.3.7" }, "funding": { @@ -2925,36 +2930,9 @@ } }, "node_modules/@tiptap/extension-bullet-list": { - "version": "2.0.0-beta.26", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.0.0-beta.26.tgz", - "integrity": "sha512-1n5HV8gY1tLjPk4x48nva6SZlFHoPlRfF6pqSu9JcJxPO7FUSPxUokuz4swYNe0LRrtykfyNz44dUcxKVhoFow==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" - } - }, - "node_modules/@tiptap/extension-code": { - "version": "2.0.0-beta.26", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.0.0-beta.26.tgz", - "integrity": "sha512-QcFWdEFfbJ1n5UFFBD17QPPAJ3J5p/b7XV484u0shCzywO7aNPV32QeHy1z0eMoyZtCbOWf6hg/a7Ugv8IwpHw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" - } - }, - "node_modules/@tiptap/extension-code-block": { - "version": "2.0.0-beta.37", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.0.0-beta.37.tgz", - "integrity": "sha512-mJAM+PHaNoKRYwM3D36lZ51/aoPxxvZNQn3UBnZ6G7l0ZJSgB3JvBEzqK6S8nNFeYIIxGwv4QF6vXe4MG9ie2g==", - "dependencies": { - "prosemirror-state": "^1.3.4" - }, + "version": "2.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.0.0-beta.29.tgz", + "integrity": "sha512-R8VB2l1ZB6VeGWx/t/04nBS5Wg3qjIDEZCpPihj2fccJOw99Lu0Ub2UJg/SfdGmeNNpBh4ZYYFv1g/XjyzlXKg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -2964,9 +2942,9 @@ } }, "node_modules/@tiptap/extension-color": { - "version": "2.0.0-beta.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-color/-/extension-color-2.0.0-beta.9.tgz", - "integrity": "sha512-c8zcaNCdwUwbgrutfsG7LD9KH7ZvDVwKOZHbOL4gMSwdH9s+6r1ThRFLEbKgHIJlTa2jd96qoo+lVfj1Qwp7ww==", + "version": "2.0.0-beta.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-color/-/extension-color-2.0.0-beta.12.tgz", + "integrity": "sha512-ddFIUdb7e0gLlYlcQiyZ+zRC4nXtpX2bhOHZ7IdCasoHYEWs/8dsncngLtymAgsRBeruMZ5K6ZhAYtgQVUugqQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -2977,25 +2955,9 @@ } }, "node_modules/@tiptap/extension-document": { - "version": "2.0.0-beta.15", - "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.0.0-beta.15.tgz", - "integrity": "sha512-ypENC+xUYD5m2t+KOKNYqyXnanXd5fxyIyhR1qeEEwwQwMXGNrO3kCH6O4mIDCpy+/WqHvVay2tV5dVsXnvY8w==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" - } - }, - "node_modules/@tiptap/extension-dropcursor": { - "version": "2.0.0-beta.25", - "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.0.0-beta.25.tgz", - "integrity": "sha512-GYf5s6dkZtsDy+TEkrQK6kLbfbitG4qnk02D+FlhlJMI/Nnx8rYCRJbwEHDdqrfX7XwZzULMqqqHvzxZYrEeNg==", - "dependencies": { - "@types/prosemirror-dropcursor": "^1.0.3", - "prosemirror-dropcursor": "^1.4.0" - }, + "version": "2.0.0-beta.17", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.0.0-beta.17.tgz", + "integrity": "sha512-L6sg0FNchbtIpQkCSjMmItVGs3/vep8Fq56WRtDc1wBSGUSmtHaxQG7F2FZLnNIUMuvzVMRD81m2vYG73WkY6A==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3005,12 +2967,12 @@ } }, "node_modules/@tiptap/extension-floating-menu": { - "version": "2.0.0-beta.51", - "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.51.tgz", - "integrity": "sha512-rEe7jADK9xr2n2LJsrGEN3Dz7sEGC1JT/7AdTdaZBxQRQvwxTjomqYGrt+LnX+v0MYggh6swMzj7upJosnKbBg==", + "version": "2.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.56.tgz", + "integrity": "sha512-j/evHE/6UPGkIgXny9IGcAh0IrcnQmg0b2NBYebs2mqx9xYKYoe+0jVgNdLp/0M3MRgQCzyWTyatBDBFOUR2mw==", "dependencies": { - "prosemirror-state": "^1.3.4", - "prosemirror-view": "^1.23.6", + "prosemirror-state": "1.4.1", + "prosemirror-view": "1.26.2", "tippy.js": "^6.3.7" }, "funding": { @@ -3022,9 +2984,9 @@ } }, "node_modules/@tiptap/extension-font-family": { - "version": "2.0.0-beta.21", - "resolved": "https://registry.npmjs.org/@tiptap/extension-font-family/-/extension-font-family-2.0.0-beta.21.tgz", - "integrity": "sha512-5KVCtuEBf1QyZxs/IOL0CPDtB5X3rk8QdDB8fB+UlASa6c/Dq59Uo2aObGOgAWNDdY0Vd9MmuDTvnJKP2LI2Ng==", + "version": "2.0.0-beta.24", + "resolved": "https://registry.npmjs.org/@tiptap/extension-font-family/-/extension-font-family-2.0.0-beta.24.tgz", + "integrity": "sha512-R7fgyJb4X6KS2Xcr73dYDxM1NSBNIQrtHLeSa3Cfc3Y9UDqeMFUIEgYUh4sBLBi3Z8Y9D3RRMvxN1x39j5eLLA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3034,38 +2996,10 @@ "@tiptap/extension-text-style": "^2.0.0-beta.1" } }, - "node_modules/@tiptap/extension-gapcursor": { - "version": "2.0.0-beta.34", - "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.0.0-beta.34.tgz", - "integrity": "sha512-Vm8vMWWQ2kJcUOLfB5CEo5pYgyudI7JeeiZvX9ScPmUmgKVYhEpt3EAICY9pUYJ41aAVH35gZLXkUtsz2f9GHw==", - "dependencies": { - "@types/prosemirror-gapcursor": "^1.0.4", - "prosemirror-gapcursor": "^1.2.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" - } - }, - "node_modules/@tiptap/extension-hard-break": { - "version": "2.0.0-beta.30", - "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.30.tgz", - "integrity": "sha512-X9xj/S+CikrbIE7ccUFVwit5QHEbflnKVxod+4zPwr1cxogFbE9AyLZE2MpYdx3z9LcnTYYi9leBqFrP4T/Olw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" - } - }, "node_modules/@tiptap/extension-heading": { - "version": "2.0.0-beta.26", - "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.0.0-beta.26.tgz", - "integrity": "sha512-nR6W/3rjnZH1Swo7tGBoYsmO6xMvu9MGq6jlm3WVHCB7B3CsrRvCkTwGjVIbKTaZC4bQfx5gvAUpQFvwuU+M5w==", + "version": "2.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.0.0-beta.29.tgz", + "integrity": "sha512-q92jYcsT5bPhvuQaB0h44Z9r+Ii22tDYo082KMVnR4+tknHT/3xx+p4JC8KHjh+/5W8Quyafqy6mS8L8VX0zsQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3075,40 +3009,9 @@ } }, "node_modules/@tiptap/extension-highlight": { - "version": "2.0.0-beta.33", - "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.0.0-beta.33.tgz", - "integrity": "sha512-TXyMiCcY5a0w5UFax350xU+T79GKJw2XwJ6Punc6sY2RRRgKaEbN1ZF0JCdQhQvD1ooKImHzCRYR8Pldb0xgfg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" - } - }, - "node_modules/@tiptap/extension-history": { - "version": "2.0.0-beta.21", - "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.0.0-beta.21.tgz", - "integrity": "sha512-0v8Cl30V4dsabdpspLdk+f+lMoIvLFlJN5WRxtc7RRZ5gfJVxPHwooIKdvC51brfh/oJtWFCNMRjhoz0fRaF9A==", - "dependencies": { - "@types/prosemirror-history": "^1.0.3", - "prosemirror-history": "^1.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.0.0-beta.1" - } - }, - "node_modules/@tiptap/extension-horizontal-rule": { - "version": "2.0.0-beta.31", - "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.0.0-beta.31.tgz", - "integrity": "sha512-MNc4retfjRgkv3qxqGya0+/BEd1Kmn+oMsCRvE+8x3sXyKIse+vdqMuG5qUcA6np0ZD/9hh1riiQ1GQdgc23Ng==", - "dependencies": { - "prosemirror-state": "^1.3.4" - }, + "version": "2.0.0-beta.35", + "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.0.0-beta.35.tgz", + "integrity": "sha512-xvEKOyuTj4mhQ8GIOItaSymJhGkWt2gGuCvmFWnTVZAaJJQOlgUTdkmayLCtwoDDP7biiuDhRJokTukGGmhUZw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3118,9 +3021,9 @@ } }, "node_modules/@tiptap/extension-image": { - "version": "2.0.0-beta.27", - "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.0.0-beta.27.tgz", - "integrity": "sha512-kdJ7V39yNdVWUco/RBe7WgvFevd81l+pU6+Je9HpelqBBP953wDttzLMuAWQB4AeLv9WhKSlORHiFv2SKsV5NA==", + "version": "2.0.0-beta.30", + "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.0.0-beta.30.tgz", + "integrity": "sha512-VhEmgiKkZMiKR7hbpJgIlIUS/QNjSGI5ER7mKDAbuV1IB5yb6nGjZ6o3Exrr2/CaTaW5hQarBC1z2Xgdu05EGg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3130,9 +3033,9 @@ } }, "node_modules/@tiptap/extension-italic": { - "version": "2.0.0-beta.26", - "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.26.tgz", - "integrity": "sha512-vejGe2ra4K5ipFOn1U9viqF9X9nPTX8WSJpSOux+9UbKjHpANy7bz69tp66OIi/Wh5L/MMDc+luH/04qfVnpZw==", + "version": "2.0.0-beta.28", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.28.tgz", + "integrity": "sha512-/pKRiCfewh7nqiXRD3N4hQHfGrGNOiWPFYZfY35bSpvTms7PDb/MF7xT1CWW23hSpY31BBS+R/a66vlR/gqu7Q==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3142,9 +3045,9 @@ } }, "node_modules/@tiptap/extension-list-item": { - "version": "2.0.0-beta.20", - "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.0.0-beta.20.tgz", - "integrity": "sha512-5IPEspJt38t9ROj4xLUesOVEYlTT/R9Skd9meHRxJQZX1qrzBICs5PC/WRIsnexrvTBhdxpYgCYjpvpsJBlKuQ==", + "version": "2.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.0.0-beta.23.tgz", + "integrity": "sha512-AkzvdELz3ZnrlZM0r9+ritBDOnAjXHR/8zCZhW0ZlWx4zyKPMsNG5ygivY+xr4QT65NEGRT8P8b2zOhXrMjjMQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3154,9 +3057,9 @@ } }, "node_modules/@tiptap/extension-ordered-list": { - "version": "2.0.0-beta.27", - "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.27.tgz", - "integrity": "sha512-apFDeignxdZb3cA3p1HJu0zw1JgJdBYUBz1r7f99qdNybYuk3I/1MPUvlOuOgvIrBB/wydoyVDP+v9F7QN3tfQ==", + "version": "2.0.0-beta.30", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.30.tgz", + "integrity": "sha512-GRxGQdq1u0Rp5N8TjthCqoZ//460m343A0HCN7UwfQOnX7Ipv0UJemwNkSHWrl7Pexym9vy3yPWgrn7oRRmgEw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3166,9 +3069,9 @@ } }, "node_modules/@tiptap/extension-paragraph": { - "version": "2.0.0-beta.23", - "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.0.0-beta.23.tgz", - "integrity": "sha512-VWAxyzecErYWk97Kv/Gkghh97zAQTcaVOisEnYYArZAlyYDaYM48qVssAC/vnRRynP2eQxb1EkppbAxE+bMHAA==", + "version": "2.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.0.0-beta.26.tgz", + "integrity": "sha512-WcYsuUa7LLfk0vi7I1dVjdMRu53B52FMMqd+UL1qPdDKVkU3DBsZVwPj+yyfQyqN8Mc/xyg9VacGaiKFLmWNDg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3178,9 +3081,9 @@ } }, "node_modules/@tiptap/extension-strike": { - "version": "2.0.0-beta.27", - "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.0.0-beta.27.tgz", - "integrity": "sha512-2dmCgtesuDdivM/54Q+Y6Tc3JbGz1SkHP6c62piuqBiYLWg3xa16zChZOhfN8szbbQlBgLT6XRTDt3c2Ux+Dug==", + "version": "2.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.0.0-beta.29.tgz", + "integrity": "sha512-zqFuY7GfNmZ/KClt6kxQ+msGo3syqucP/Xnlihxi+/h/G+oTvEwyOIXCtDOltvxcsWH/TUsdr5vzLp0j+Mdc6Q==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3190,9 +3093,9 @@ } }, "node_modules/@tiptap/extension-subscript": { - "version": "2.0.0-beta.10", - "resolved": "https://registry.npmjs.org/@tiptap/extension-subscript/-/extension-subscript-2.0.0-beta.10.tgz", - "integrity": "sha512-er8/1lp0Rb+SKwEioW0w4oVf3EkdQZ0WS/5kPBG4W0DncfUMT+bw5de76S3kRL9PLZ9UShAL7wuXtuiSi5QsMw==", + "version": "2.0.0-beta.13", + "resolved": "https://registry.npmjs.org/@tiptap/extension-subscript/-/extension-subscript-2.0.0-beta.13.tgz", + "integrity": "sha512-L9f2zKzNI5y4YvMdNxHDT4Y+8gS1UwtbTJ1vUJdCZGfF8DrMuTZIRp3LjOxYXydr7NGEXyYbucdm97Tzrsp8WA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3202,9 +3105,9 @@ } }, "node_modules/@tiptap/extension-superscript": { - "version": "2.0.0-beta.10", - "resolved": "https://registry.npmjs.org/@tiptap/extension-superscript/-/extension-superscript-2.0.0-beta.10.tgz", - "integrity": "sha512-TUUBS8XsD2MorGORYVlhGDH7wcc9diSbHscD4Dnz8pKWVR0JPUd/od4h5qSffDzAOKxtphTiX9LOFWk6zVooKg==", + "version": "2.0.0-beta.13", + "resolved": "https://registry.npmjs.org/@tiptap/extension-superscript/-/extension-superscript-2.0.0-beta.13.tgz", + "integrity": "sha512-Vr9KIG2c4jzymcMMQCjhx2gppmRvnbw6Xvrd8YCpK4szyYI1ClMQ5KQMYl2zV3Y4ZIsivRSy9cE0ipGsXGE3Gw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3214,9 +3117,9 @@ } }, "node_modules/@tiptap/extension-text": { - "version": "2.0.0-beta.15", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.0.0-beta.15.tgz", - "integrity": "sha512-S3j2+HyV2gsXZP8Wg/HA+YVXQsZ3nrXgBM9HmGAxB0ESOO50l7LWfip0f3qcw1oRlh5H3iLPkA6/f7clD2/TFA==", + "version": "2.0.0-beta.17", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.0.0-beta.17.tgz", + "integrity": "sha512-OyKL+pqWJEtjyd9/mrsuY1kZh2b3LWpOQDWKtd4aWR4EA0efmQG+7FPwcIeAVEh7ZoqM+/ABCnPjN6IjzIrSfg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3226,9 +3129,9 @@ } }, "node_modules/@tiptap/extension-text-align": { - "version": "2.0.0-beta.29", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.0.0-beta.29.tgz", - "integrity": "sha512-FNGpl0tVtgG7AK9kVWF/+CGYHta05NpoME4/j6+vhNlZLBNXRA+AKg7W5T8UxmtaC9yGoJsBs2X8M9eCxWVaEQ==", + "version": "2.0.0-beta.31", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.0.0-beta.31.tgz", + "integrity": "sha512-gSJqi57piiMPc2r6WEkXv7ZgQIogigsRUhmlnZC/7s3zzOvjXrexWnV0Ctt/9A7BKcM7OHMykpZyoewvk6QRTw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3238,9 +3141,9 @@ } }, "node_modules/@tiptap/extension-text-style": { - "version": "2.0.0-beta.23", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.0.0-beta.23.tgz", - "integrity": "sha512-h/7o//RB4WRrLKWV7E5eFk7tZnfjH0Wt9klixOmvTmus6dm00a7r6wTuaT1GNjfPOgClP3K185lTA5rrdgrxRA==", + "version": "2.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.0.0-beta.26.tgz", + "integrity": "sha512-sHUlj5j86W53jvj9ijhXGyqxDdT2c9B7lVwdmDtksvSIrRYuCFqn7hFBzlypNBb56zePqBlIFymUmhK283L6fQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3250,9 +3153,9 @@ } }, "node_modules/@tiptap/extension-underline": { - "version": "2.0.0-beta.23", - "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.0.0-beta.23.tgz", - "integrity": "sha512-pMjFH/NpFWLd2XQQa5rG9rGVQ9mu3ygdtu6VGfJ3aAjzBiyLXDKhE4biIFWyFsr8zLpp7DjwbrmLV0UGvbG1WQ==", + "version": "2.0.0-beta.25", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.0.0-beta.25.tgz", + "integrity": "sha512-kRDdb/mF6QWzFGV3cQuLh6xyXULXaKPL/TghefoOZhwkdIWV/M3zFar5tsZO54+tbIrzxoVP6t7mO2Y5G/SLDQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -3261,36 +3164,6 @@ "@tiptap/core": "^2.0.0-beta.1" } }, - "node_modules/@tiptap/starter-kit": { - "version": "2.0.0-beta.184", - "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.0.0-beta.184.tgz", - "integrity": "sha512-FgF94i5RQzXiGAIkaubnXEaYwJfiZRbMPZcmarwNo8IyqPnLT34Q1yjw/qZ3nv7rDehWV5l/zenbrrNtPYVCkA==", - "dependencies": { - "@tiptap/core": "^2.0.0-beta.175", - "@tiptap/extension-blockquote": "^2.0.0-beta.26", - "@tiptap/extension-bold": "^2.0.0-beta.26", - "@tiptap/extension-bullet-list": "^2.0.0-beta.26", - "@tiptap/extension-code": "^2.0.0-beta.26", - "@tiptap/extension-code-block": "^2.0.0-beta.37", - "@tiptap/extension-document": "^2.0.0-beta.15", - "@tiptap/extension-dropcursor": "^2.0.0-beta.25", - "@tiptap/extension-gapcursor": "^2.0.0-beta.34", - "@tiptap/extension-hard-break": "^2.0.0-beta.30", - "@tiptap/extension-heading": "^2.0.0-beta.26", - "@tiptap/extension-history": "^2.0.0-beta.21", - "@tiptap/extension-horizontal-rule": "^2.0.0-beta.31", - "@tiptap/extension-italic": "^2.0.0-beta.26", - "@tiptap/extension-list-item": "^2.0.0-beta.20", - "@tiptap/extension-ordered-list": "^2.0.0-beta.27", - "@tiptap/extension-paragraph": "^2.0.0-beta.23", - "@tiptap/extension-strike": "^2.0.0-beta.27", - "@tiptap/extension-text": "^2.0.0-beta.15" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - } - }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -3447,7 +3320,8 @@ "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/lodash": { "version": "4.14.182", @@ -3473,7 +3347,8 @@ "node_modules/@types/orderedmap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/orderedmap/-/orderedmap-1.0.0.tgz", - "integrity": "sha512-dxKo80TqYx3YtBipHwA/SdFmMMyLCnP+5mkEqN0eMjcTBzHkiiX0ES118DsjDBjvD+zeSsSU9jULTZ+frog+Gw==" + "integrity": "sha512-dxKo80TqYx3YtBipHwA/SdFmMMyLCnP+5mkEqN0eMjcTBzHkiiX0ES118DsjDBjvD+zeSsSU9jULTZ+frog+Gw==", + "peer": true }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -3481,75 +3356,20 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, - "node_modules/@types/prosemirror-commands": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@types/prosemirror-commands/-/prosemirror-commands-1.0.4.tgz", - "integrity": "sha512-utDNYB3EXLjAfYIcRWJe6pn3kcQ5kG4RijbT/0Y/TFOm6yhvYS/D9eJVnijdg9LDjykapcezchxGRqFD5LcyaQ==", - "dependencies": { - "@types/prosemirror-model": "*", - "@types/prosemirror-state": "*", - "@types/prosemirror-view": "*" - } - }, - "node_modules/@types/prosemirror-dropcursor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/prosemirror-dropcursor/-/prosemirror-dropcursor-1.0.3.tgz", - "integrity": "sha512-b0/8njnJ4lwyHKcGuCMf3x7r1KjxyugB1R/c2iMCjplsJHSC7UY9+OysqgJR5uUXRekUSGniiLgBtac/lvH6wg==", - "dependencies": { - "@types/prosemirror-state": "*" - } - }, - "node_modules/@types/prosemirror-gapcursor": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@types/prosemirror-gapcursor/-/prosemirror-gapcursor-1.0.4.tgz", - "integrity": "sha512-9xKjFIG5947dzerFvkLWp6F53JwrUYoYwh3SgcTFEp8SbSfNNrez/PFYVZKPnoqPoaK5WtTdQTaMwpCV9rXQIg==", - "dependencies": { - "@types/prosemirror-model": "*", - "@types/prosemirror-state": "*" - } - }, - "node_modules/@types/prosemirror-history": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/prosemirror-history/-/prosemirror-history-1.0.3.tgz", - "integrity": "sha512-5TloMDRavgLjOAKXp1Li8u0xcsspzbT1Cm9F2pwHOkgvQOz1jWQb2VIXO7RVNsFjLBZdIXlyfSLivro3DuMWXg==", - "dependencies": { - "@types/prosemirror-model": "*", - "@types/prosemirror-state": "*" - } - }, - "node_modules/@types/prosemirror-keymap": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@types/prosemirror-keymap/-/prosemirror-keymap-1.0.4.tgz", - "integrity": "sha512-ycevwkqUh+jEQtPwqO7sWGcm+Sybmhu8MpBsM8DlO3+YTKnXbKA6SDz/+q14q1wK3UA8lHJyfR+v+GPxfUSemg==", - "dependencies": { - "@types/prosemirror-commands": "*", - "@types/prosemirror-model": "*", - "@types/prosemirror-state": "*", - "@types/prosemirror-view": "*" - } - }, "node_modules/@types/prosemirror-model": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/@types/prosemirror-model/-/prosemirror-model-1.16.2.tgz", "integrity": "sha512-1XPJopkKP3oHSBP61uuSuW13DIDZPWvAzP6Pv2/6mixk8EBPUeRGIW548DjJTicMo23gEg1zvCZy9asblQdWag==", + "peer": true, "dependencies": { "@types/orderedmap": "*" } }, - "node_modules/@types/prosemirror-schema-list": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/prosemirror-schema-list/-/prosemirror-schema-list-1.0.3.tgz", - "integrity": "sha512-uWybOf+M2Ea7rlbs0yLsS4YJYNGXYtn4N+w8HCw3Vvfl6wBAROzlMt0gV/D/VW/7J/LlAjwMezuGe8xi24HzXA==", - "dependencies": { - "@types/orderedmap": "*", - "@types/prosemirror-model": "*", - "@types/prosemirror-state": "*" - } - }, "node_modules/@types/prosemirror-state": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@types/prosemirror-state/-/prosemirror-state-1.3.0.tgz", "integrity": "sha512-nMdUF6w8B++NH4V54X+4GvDty7M02UfuHQW0s1AS25Z4ZrOW4RSY2+s57doXBbeMSjzYV/QoMxCY2sT3KQ2VdQ==", + "peer": true, "dependencies": { "@types/prosemirror-model": "*", "@types/prosemirror-transform": "*", @@ -3560,6 +3380,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@types/prosemirror-transform/-/prosemirror-transform-1.4.0.tgz", "integrity": "sha512-ntfuTl9nJWHvFykCmqJj4YQMws6G5H9nBaxHW0xRqfTxDxUvX2bCloqRN7bQTWg9h3VSP2lx45UuET1fn/oQ9Q==", + "peer": true, "dependencies": { "@types/prosemirror-model": "*" } @@ -3568,6 +3389,7 @@ "version": "1.23.3", "resolved": "https://registry.npmjs.org/@types/prosemirror-view/-/prosemirror-view-1.23.3.tgz", "integrity": "sha512-T5dPDmZiXAazJVSvnx55D6h4mcpiH2q2wTyO9zIeOdox5zx964+zcDl9dFNaXG3qCGlERwMPckhBZL1HCxyygw==", + "peer": true, "dependencies": { "@types/prosemirror-model": "*", "@types/prosemirror-state": "*", @@ -3630,19 +3452,20 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.23.0.tgz", - "integrity": "sha512-hEcSmG4XodSLiAp1uxv/OQSGsDY6QN3TcRU32gANp+19wGE1QQZLRS8/GV58VRUoXhnkuJ3ZxNQ3T6Z6zM59DA==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.31.0.tgz", + "integrity": "sha512-VKW4JPHzG5yhYQrQ1AzXgVgX8ZAJEvCz0QI6mLRX4tf7rnFfh5D8SKm0Pq6w5PyNfAWJk6sv313+nEt3ohWMBQ==", "dev": true, + "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.23.0", - "@typescript-eslint/type-utils": "5.23.0", - "@typescript-eslint/utils": "5.23.0", - "debug": "^4.3.2", + "@typescript-eslint/scope-manager": "5.31.0", + "@typescript-eslint/type-utils": "5.31.0", + "@typescript-eslint/utils": "5.31.0", + "debug": "^4.3.4", "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", + "ignore": "^5.2.0", "regexpp": "^3.2.0", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" }, "engines": { @@ -3662,16 +3485,51 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/parser": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.23.0.tgz", - "integrity": "sha512-V06cYUkqcGqpFjb8ttVgzNF53tgbB/KoQT/iB++DOIExKmzI9vBJKjZKt/6FuV9c+zrDsvJKbJ2DOCYwX91cbw==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.31.0.tgz", + "integrity": "sha512-UStjQiZ9OFTFReTrN+iGrC6O/ko9LVDhreEK5S3edmXgR396JGq7CoX2TWIptqt/ESzU2iRKXAHfSF2WJFcWHw==", "dev": true, + "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.23.0", - "@typescript-eslint/types": "5.23.0", - "@typescript-eslint/typescript-estree": "5.23.0", - "debug": "^4.3.2" + "@typescript-eslint/scope-manager": "5.31.0", + "@typescript-eslint/types": "5.31.0", + "@typescript-eslint/typescript-estree": "5.31.0", + "debug": "^4.3.4" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3689,14 +3547,33 @@ } } }, + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.23.0.tgz", - "integrity": "sha512-EhjaFELQHCRb5wTwlGsNMvzK9b8Oco4aYNleeDlNuL6qXWDF47ch4EhVNPh8Rdhf9tmqbN4sWDk/8g+Z/J8JVw==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.31.0.tgz", + "integrity": "sha512-8jfEzBYDBG88rcXFxajdVavGxb5/XKXyvWgvD8Qix3EEJLCFIdVloJw+r9ww0wbyNLOTYyBsR+4ALNGdlalLLg==", "dev": true, + "peer": true, "dependencies": { - "@typescript-eslint/types": "5.23.0", - "@typescript-eslint/visitor-keys": "5.23.0" + "@typescript-eslint/types": "5.31.0", + "@typescript-eslint/visitor-keys": "5.31.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3707,13 +3584,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.23.0.tgz", - "integrity": "sha512-iuI05JsJl/SUnOTXA9f4oI+/4qS/Zcgk+s2ir+lRmXI+80D8GaGwoUqs4p+X+4AxDolPpEpVUdlEH4ADxFy4gw==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.31.0.tgz", + "integrity": "sha512-7ZYqFbvEvYXFn9ax02GsPcEOmuWNg+14HIf4q+oUuLnMbpJ6eHAivCg7tZMVwzrIuzX3QCeAOqKoyMZCv5xe+w==", "dev": true, + "peer": true, "dependencies": { - "@typescript-eslint/utils": "5.23.0", - "debug": "^4.3.2", + "@typescript-eslint/utils": "5.31.0", + "debug": "^4.3.4", "tsutils": "^3.21.0" }, "engines": { @@ -3732,11 +3610,30 @@ } } }, + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/@typescript-eslint/types": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.23.0.tgz", - "integrity": "sha512-NfBsV/h4dir/8mJwdZz7JFibaKC3E/QdeMEDJhiAE3/eMkoniZ7MjbEMCGXw6MZnZDMN3G9S0mH/6WUIj91dmw==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.31.0.tgz", + "integrity": "sha512-/f/rMaEseux+I4wmR6mfpM2wvtNZb1p9hAV77hWfuKc3pmaANp5dLAZSiE3/8oXTYTt3uV9KW5yZKJsMievp6g==", "dev": true, + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -3746,17 +3643,18 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.23.0.tgz", - "integrity": "sha512-xE9e0lrHhI647SlGMl+m+3E3CKPF1wzvvOEWnuE3CCjjT7UiRnDGJxmAcVKJIlFgK6DY9RB98eLr1OPigPEOGg==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.31.0.tgz", + "integrity": "sha512-3S625TMcARX71wBc2qubHaoUwMEn+l9TCsaIzYI/ET31Xm2c9YQ+zhGgpydjorwQO9pLfR/6peTzS/0G3J/hDw==", "dev": true, + "peer": true, "dependencies": { - "@typescript-eslint/types": "5.23.0", - "@typescript-eslint/visitor-keys": "5.23.0", - "debug": "^4.3.2", - "globby": "^11.0.4", + "@typescript-eslint/types": "5.31.0", + "@typescript-eslint/visitor-keys": "5.31.0", + "debug": "^4.3.4", + "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" }, "engines": { @@ -3772,16 +3670,51 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/utils": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.23.0.tgz", - "integrity": "sha512-dbgaKN21drqpkbbedGMNPCtRPZo1IOUr5EI9Jrrh99r5UW5Q0dz46RKXeSBoPV+56R6dFKpbrdhgUNSJsDDRZA==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.31.0.tgz", + "integrity": "sha512-kcVPdQS6VIpVTQ7QnGNKMFtdJdvnStkqS5LeALr4rcwx11G6OWb2HB17NMPnlRHvaZP38hL9iK8DdE9Fne7NYg==", "dev": true, + "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.23.0", - "@typescript-eslint/types": "5.23.0", - "@typescript-eslint/typescript-estree": "5.23.0", + "@typescript-eslint/scope-manager": "5.31.0", + "@typescript-eslint/types": "5.31.0", + "@typescript-eslint/typescript-estree": "5.31.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -3797,13 +3730,14 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.23.0.tgz", - "integrity": "sha512-Vd4mFNchU62sJB8pX19ZSPog05B0Y0CE2UxAZPT5k4iqhRYjPnqyY3woMxCd0++t9OTqkgjST+1ydLBi7e2Fvg==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.31.0.tgz", + "integrity": "sha512-ZK0jVxSjS4gnPirpVjXHz7mgdOsZUHzNYSfTw2yPa3agfbt9YfqaBiBZFSSxeBWnpWkzCxTfUpnzA3Vily/CSg==", "dev": true, + "peer": true, "dependencies": { - "@typescript-eslint/types": "5.23.0", - "eslint-visitor-keys": "^3.0.0" + "@typescript-eslint/types": "5.31.0", + "eslint-visitor-keys": "^3.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4036,6 +3970,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "peer": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -4280,6 +4215,7 @@ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -4315,6 +4251,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -5320,7 +5257,8 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/connect": { "version": "3.7.0", @@ -5944,7 +5882,8 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/default-gateway": { "version": "6.0.3", @@ -6212,6 +6151,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, + "peer": true, "dependencies": { "esutils": "^2.0.2" }, @@ -6515,6 +6455,7 @@ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.0.tgz", "integrity": "sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", @@ -6558,6 +6499,7 @@ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", "dev": true, + "peer": true, "dependencies": { "has": "^1.0.3" } @@ -6567,6 +6509,7 @@ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, + "peer": true, "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -6958,12 +6901,13 @@ } }, "node_modules/eslint": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.15.0.tgz", - "integrity": "sha512-GG5USZ1jhCu8HJkzGgeK8/+RGnHaNYZGrGDzUtigK3BsGESW/rs2az23XqE0WVwDxy1VRvvjSSGu5nB0Bu+6SA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.20.0.tgz", + "integrity": "sha512-d4ixhz5SKCa1D6SCPrivP7yYVi7nyD6A4vs6HIAul9ujBzcEmZVM3/0NN/yu5nKhmO1wjp5xQ46iRfmDGlOviA==", "dev": true, + "peer": true, "dependencies": { - "@eslint/eslintrc": "^1.2.3", + "@eslint/eslintrc": "^1.3.0", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -6981,7 +6925,7 @@ "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", - "globals": "^13.6.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -7014,6 +6958,7 @@ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", "dev": true, + "peer": true, "dependencies": { "confusing-browser-globals": "^1.0.10", "object.assign": "^4.1.2", @@ -7033,6 +6978,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true, + "peer": true, "bin": { "semver": "bin/semver.js" } @@ -7042,6 +6988,7 @@ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.0.0.tgz", "integrity": "sha512-elNiuzD0kPAPTXjFWg+lE24nMdHMtuxgYoD30OyMD6yrW1AhFZPAg27VX7d3tzOErw+dgJTNWfRSDqEcXb4V0g==", "dev": true, + "peer": true, "dependencies": { "eslint-config-airbnb-base": "^15.0.0" }, @@ -7057,6 +7004,7 @@ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", "dev": true, + "peer": true, "dependencies": { "debug": "^3.2.7", "resolve": "^1.20.0" @@ -7067,6 +7015,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "peer": true, "dependencies": { "ms": "^2.1.1" } @@ -7076,6 +7025,7 @@ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", "dev": true, + "peer": true, "dependencies": { "debug": "^3.2.7", "find-up": "^2.1.0" @@ -7089,6 +7039,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "peer": true, "dependencies": { "ms": "^2.1.1" } @@ -7098,6 +7049,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, + "peer": true, "dependencies": { "locate-path": "^2.0.0" }, @@ -7110,6 +7062,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, + "peer": true, "dependencies": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" @@ -7123,6 +7076,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, + "peer": true, "dependencies": { "p-try": "^1.0.0" }, @@ -7135,6 +7089,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, + "peer": true, "dependencies": { "p-limit": "^1.1.0" }, @@ -7147,6 +7102,7 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true, + "peer": true, "engines": { "node": ">=4" } @@ -7156,6 +7112,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true, + "peer": true, "engines": { "node": ">=4" } @@ -7165,6 +7122,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", "dev": true, + "peer": true, "dependencies": { "array-includes": "^3.1.4", "array.prototype.flat": "^1.2.5", @@ -7192,6 +7150,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "peer": true, "dependencies": { "ms": "2.0.0" } @@ -7201,6 +7160,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, + "peer": true, "dependencies": { "esutils": "^2.0.2" }, @@ -7213,6 +7173,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7224,7 +7185,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "dev": true, + "peer": true }, "node_modules/eslint-scope": { "version": "5.1.1", @@ -7244,6 +7206,7 @@ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, + "peer": true, "dependencies": { "eslint-visitor-keys": "^2.0.0" }, @@ -7262,6 +7225,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true, + "peer": true, "engines": { "node": ">=10" } @@ -7271,6 +7235,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true, + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -7280,6 +7245,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -7296,6 +7262,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -7310,13 +7277,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "peer": true }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -7333,6 +7302,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -7344,13 +7314,15 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "peer": true, "engines": { "node": ">=10" }, @@ -7363,6 +7335,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", "dev": true, + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -7376,6 +7349,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "peer": true, "engines": { "node": ">=4.0" } @@ -7385,6 +7359,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "peer": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -7397,6 +7372,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", "dev": true, + "peer": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -7412,6 +7388,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "peer": true, "engines": { "node": ">=8" } @@ -7421,6 +7398,7 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -7432,13 +7410,15 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7451,6 +7431,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -7463,6 +7444,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "peer": true, "engines": { "node": ">=10" }, @@ -7483,6 +7465,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", "dev": true, + "peer": true, "dependencies": { "acorn": "^8.7.1", "acorn-jsx": "^5.3.2", @@ -7510,6 +7493,7 @@ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "dev": true, + "peer": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -7522,6 +7506,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "peer": true, "engines": { "node": ">=4.0" } @@ -7786,7 +7771,8 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true + "dev": true, + "peer": true }, "node_modules/fastq": { "version": "1.13.0", @@ -7828,6 +7814,7 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, + "peer": true, "dependencies": { "flat-cache": "^3.0.4" }, @@ -7954,6 +7941,7 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, + "peer": true, "dependencies": { "flatted": "^3.1.0", "rimraf": "^3.0.2" @@ -8084,6 +8072,7 @@ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -8101,7 +8090,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true + "dev": true, + "peer": true }, "node_modules/functions-have-names": { "version": "1.2.3", @@ -8205,6 +8195,7 @@ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" @@ -8314,6 +8305,7 @@ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8852,6 +8844,7 @@ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", "dev": true, + "peer": true, "dependencies": { "get-intrinsic": "^1.1.0", "has": "^1.0.3", @@ -8927,6 +8920,7 @@ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, + "peer": true, "dependencies": { "has-bigints": "^1.0.1" }, @@ -8951,6 +8945,7 @@ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -8967,6 +8962,7 @@ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", "dev": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -9094,6 +9090,7 @@ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -9114,6 +9111,7 @@ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, + "peer": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -9199,6 +9197,7 @@ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2" }, @@ -9222,6 +9221,7 @@ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, + "peer": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -9237,6 +9237,7 @@ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, + "peer": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -9269,6 +9270,7 @@ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2" }, @@ -9534,7 +9536,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true + "dev": true, + "peer": true }, "node_modules/json5": { "version": "2.2.1", @@ -9954,6 +9957,7 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "peer": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -10067,7 +10071,8 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/lodash.once": { "version": "4.1.1", @@ -10659,7 +10664,8 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true + "dev": true, + "peer": true }, "node_modules/needle": { "version": "2.9.1", @@ -10714,22 +10720,22 @@ } }, "node_modules/ngx-tiptap": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/ngx-tiptap/-/ngx-tiptap-3.0.4.tgz", - "integrity": "sha512-iU69pGqYoan5v9N8l92tpFSncB7x5mbXcJVwJwUDtEI922N+59cd4pw5y+NrN3er9E/HVAgOLfRB40TDHliwqQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ngx-tiptap/-/ngx-tiptap-5.0.0.tgz", + "integrity": "sha512-SlySnakZ7OroJSG5owPqVQ1ncqS9tgWYQ7551aI2VzZmcdCKKss00cdy+TqNyeRUg0b1+9Ep+yorUmg2CHPKLg==", "dependencies": { - "tslib": "^2.2.0" + "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/common": ">=12.0.0", - "@angular/core": ">=12.0.0", - "@angular/forms": ">=12.0.0", - "@tiptap/core": "^2.0.0-beta.113", - "@tiptap/extension-bubble-menu": "^2.0.0-beta.36", - "@tiptap/extension-floating-menu": "^2.0.0-beta.30", - "@types/prosemirror-model": "^1.13.2", - "@types/prosemirror-state": "^1.2.7", - "@types/prosemirror-view": "^1.19.1" + "@angular/common": ">=13.0.0", + "@angular/core": ">=13.0.0", + "@angular/forms": ">=13.0.0", + "@tiptap/core": "^2.0.0-beta.162", + "@tiptap/extension-bubble-menu": "^2.0.0-beta.54", + "@tiptap/extension-floating-menu": "^2.0.0-beta.49", + "@types/prosemirror-model": "^1.16.0", + "@types/prosemirror-state": "^1.2.8", + "@types/prosemirror-view": "^1.23.0" } }, "node_modules/ngx-translate-testing": { @@ -11251,6 +11257,7 @@ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -11265,6 +11272,7 @@ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -11348,6 +11356,7 @@ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "dev": true, + "peer": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -11454,9 +11463,9 @@ } }, "node_modules/orderedmap": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.1.7.tgz", - "integrity": "sha512-B1SuadDDwIRXXutaJQ1xjreGL3hxujpexBG4PquoXbgJD8bjp2k8b8qI/mk7q0LUdIx7T8IALWB8mPbfsjbGCw==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.0.0.tgz", + "integrity": "sha512-buf4PoAMlh45b8a8gsGy/X6w279TSqkyAS0C0wdTSJwFSU+ljQFJON5I8NfjLHoCXwpSROIo2wr0g33T+kQshQ==" }, "node_modules/os-family": { "version": "1.1.0", @@ -12471,6 +12480,7 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "peer": true, "engines": { "node": ">= 0.8.0" } @@ -12528,93 +12538,63 @@ } }, "node_modules/prosemirror-commands": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.2.2.tgz", - "integrity": "sha512-TX+KpWudMon06frryfpO/u7hsQv2hu8L4VSVbCpi3/7wXHBgl+35mV85qfa3RpT8xD2f3MdeoTqH0vy5JdbXPg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.3.0.tgz", + "integrity": "sha512-BwBbZ5OAScPcm0x7H8SPbqjuEJnCU2RJT9LDyOiiIl/3NbL1nJZI4SFNHwU2e/tRr2Xe7JsptpzseqvZvToLBQ==", "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.0.0" } }, - "node_modules/prosemirror-dropcursor": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.4.0.tgz", - "integrity": "sha512-6+YwTjmqDwlA/Dm+5wK67ezgqgjA/MhSDgaNxKUzH97SmeuWFXyLeDRxxOPZeSo7yTxcDGUCWTEjmQZsVBuMrQ==", - "dependencies": { - "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.1.0", - "prosemirror-view": "^1.1.0" - } - }, - "node_modules/prosemirror-gapcursor": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.2.2.tgz", - "integrity": "sha512-7YzuRBbu9W7HGQde84kCHfIjaRLNcAdeijbgqrm/R9dsdTWkV+rrdcmic/sCc+bptiNpvjCEE+R6hrbT8zFQeQ==", - "dependencies": { - "prosemirror-keymap": "^1.0.0", - "prosemirror-model": "^1.0.0", - "prosemirror-state": "^1.0.0", - "prosemirror-view": "^1.0.0" - } - }, - "node_modules/prosemirror-history": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.2.0.tgz", - "integrity": "sha512-B9v9xtf4fYbKxQwIr+3wtTDNLDZcmMMmGiI3TAPShnUzvo+Rmv1GiUrsQChY1meetHl7rhML2cppF3FTs7f7UQ==", - "dependencies": { - "prosemirror-state": "^1.2.2", - "prosemirror-transform": "^1.0.0", - "rope-sequence": "^1.3.0" - } - }, "node_modules/prosemirror-keymap": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.1.5.tgz", - "integrity": "sha512-8SZgPH3K+GLsHL2wKuwBD9rxhsbnVBTwpHCO4VUO5GmqUQlxd/2GtBVWTsyLq4Dp3N9nGgPd3+lZFKUDuVp+Vw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.0.tgz", + "integrity": "sha512-TdSfu+YyLDd54ufN/ZeD1VtBRYpgZnTPnnbY+4R08DDgs84KrIPEPbJL8t1Lm2dkljFx6xeBE26YWH3aIzkPKg==", "dependencies": { "prosemirror-state": "^1.0.0", "w3c-keyname": "^2.2.0" } }, "node_modules/prosemirror-model": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.16.1.tgz", - "integrity": "sha512-r1/w0HDU40TtkXp0DyKBnFPYwd8FSlUSJmGCGFv4DeynfeSlyQF2FD0RQbVEMOe6P3PpUSXM6LZBV7W/YNZ4mA==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.1.tgz", + "integrity": "sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==", "dependencies": { - "orderedmap": "^1.1.0" + "orderedmap": "^2.0.0" } }, "node_modules/prosemirror-schema-list": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.1.6.tgz", - "integrity": "sha512-aFGEdaCWmJzouZ8DwedmvSsL50JpRkqhQ6tcpThwJONVVmCgI36LJHtoQ4VGZbusMavaBhXXr33zyD2IVsTlkw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.2.0.tgz", + "integrity": "sha512-8PT/9xOx1HHdC7fDNNfhQ50Z8Mzu7nKyA1KCDltSpcZVZIbB0k7KtsHrnXyuIhbLlScoymBiLZ00c5MH6wdFsA==", "dependencies": { "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.0.0" } }, "node_modules/prosemirror-state": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.3.4.tgz", - "integrity": "sha512-Xkkrpd1y/TQ6HKzN3agsQIGRcLckUMA9u3j207L04mt8ToRgpGeyhbVv0HI7omDORIBHjR29b7AwlATFFf2GLA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.1.tgz", + "integrity": "sha512-U/LBDW2gNmVa07sz/D229XigSdDQ5CLFwVB1Vb32MJbAHHhWe/6pOc721faI17tqw4pZ49i1xfY/jEZ9tbIhPg==", "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-transform": "^1.0.0" } }, "node_modules/prosemirror-transform": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.4.2.tgz", - "integrity": "sha512-bcIsf3uRZhfab0xRfyyxOEh6eqSszq/hJbDbmUumFnbHBoWhB/uXbpz6vvUxfk0XiEvrZDJ+5pXRrNDc1Hu3vQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.6.0.tgz", + "integrity": "sha512-MAp7AjsjEGEqQY0sSMufNIUuEyB1ZR9Fqlm8dTwwWwpEJRv/plsKjWXBbx52q3Ml8MtaMcd7ic14zAHVB3WaMw==", "dependencies": { "prosemirror-model": "^1.0.0" } }, "node_modules/prosemirror-view": { - "version": "1.23.13", - "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.23.13.tgz", - "integrity": "sha512-X/NcwZv8pgcEWfs3n++Wz4nDgqDIeDvJ9kfCk6DCoC9XUlDekqJLFt9wCcCUBXedb8hs/dmd+JmcaLgbr67XZw==", + "version": "1.26.2", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.26.2.tgz", + "integrity": "sha512-CGKw+GadkfSBEwRAJTHCEKJ4DlV6/3IhAdjpwGyZHUHtbP7jX4Ol4zmi7xa2c6GOabDlIJLYXJydoNYLX7lNeQ==", "dependencies": { "prosemirror-model": "^1.16.0", "prosemirror-state": "^1.0.0", @@ -12875,6 +12855,7 @@ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true, + "peer": true, "engines": { "node": ">=8" }, @@ -13110,11 +13091,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rope-sequence": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.3.tgz", - "integrity": "sha512-85aZYCxweiD5J8yTEbw+E6A27zSnLPNDL0WfPdw3YYodq7WjnTKo0q4dtyQ2gz23iPT8Q9CUyJtAaUNcTxRf5Q==" - }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -13858,6 +13834,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -13872,6 +13849,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -13917,6 +13895,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "peer": true, "engines": { "node": ">=8" }, @@ -14864,6 +14843,7 @@ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, + "peer": true, "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.1", @@ -14876,6 +14856,7 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, + "peer": true, "dependencies": { "minimist": "^1.2.0" }, @@ -14888,6 +14869,7 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true, + "peer": true, "engines": { "node": ">=4" } @@ -14902,6 +14884,7 @@ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, + "peer": true, "dependencies": { "tslib": "^1.8.1" }, @@ -14916,7 +14899,8 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/tunnel-agent": { "version": "0.6.0", @@ -14934,6 +14918,7 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "peer": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -15017,6 +15002,7 @@ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", @@ -15150,7 +15136,8 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/validate-npm-package-name": { "version": "3.0.0", @@ -15180,9 +15167,9 @@ } }, "node_modules/w3c-keyname": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.4.tgz", - "integrity": "sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw==" + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.5.tgz", + "integrity": "sha512-WJrK7i6w+ULuZsGscCezbCH4Aev5U3xY87vnSimzzEgPQhb0Sa0a1rE3c2jtEwrFtSfi61Jefw3jI5/DD/3jbQ==" }, "node_modules/watchpack": { "version": "2.3.1", @@ -15608,6 +15595,7 @@ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, + "peer": true, "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -15679,6 +15667,7 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -17444,15 +17433,16 @@ "dev": true }, "@eslint/eslintrc": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.3.tgz", - "integrity": "sha512-uGo44hIwoLGNyduRpjdEpovcbMdd+Nv7amtmJxnKmI8xj6yd5LncmSwDa5NgX/41lIFJtkjD6YdVfgEzPfJ5UA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", "dev": true, + "peer": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.3.2", - "globals": "^13.9.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -17465,6 +17455,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -17476,13 +17467,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "peer": true }, "globals": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", - "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, + "peer": true, "requires": { "type-fest": "^0.20.2" } @@ -17492,6 +17485,7 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "peer": true, "requires": { "argparse": "^2.0.1" } @@ -17500,13 +17494,15 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "peer": true }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "peer": true, "requires": { "brace-expansion": "^1.1.7" } @@ -17515,7 +17511,8 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true + "dev": true, + "peer": true } } }, @@ -17530,6 +17527,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", "dev": true, + "peer": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", @@ -17540,20 +17538,15 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "dev": true, + "peer": true }, "@iqb/eslint-config": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@iqb/eslint-config/-/eslint-config-1.1.1.tgz", - "integrity": "sha512-KaAdhBSnCXXfxeQRI51tZxgW58FXkAvHSFvZE38b+gvKPZ5YVYXuX0dlEEu7rYezeW7jCOk6zRtdkUbB5DJVyg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@iqb/eslint-config/-/eslint-config-2.1.1.tgz", + "integrity": "sha512-F94TVoZ0xT4mvxKdr9efDiRG74M0TQERCH2Eh4AL1u1JseDqYwbNhcnItadvdjho9USt9sMLDdxwOnADfHkE1g==", "dev": true, - "requires": { - "@typescript-eslint/eslint-plugin": "^5.19.0", - "@typescript-eslint/parser": "^5.19.0", - "eslint": "^8.13.0", - "eslint-config-airbnb-typescript": "^17.0.0", - "eslint-plugin-import": "^2.26.0" - } + "requires": {} }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -17750,247 +17743,159 @@ } }, "@tiptap/core": { - "version": "2.0.0-beta.175", - "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.0.0-beta.175.tgz", - "integrity": "sha512-dDf+2GtifskNLysn49kaCIz0o5hf6VDZ8J7jSQAfoPDEkEkfw9OKhWrR7NzWW6J34CSJreFDRiWkGt8Qz283Vg==", + "version": "2.0.0-beta.182", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.0.0-beta.182.tgz", + "integrity": "sha512-MZGkMGnVnWhBzjvpBNwQ9zBz38ndi3Irbf90uCTSArR0kaCVkW4vmyuPuOXd+0SO8Yv/l5oyDdOCpaG3rnQYfw==", "requires": { - "@types/prosemirror-commands": "^1.0.4", - "@types/prosemirror-keymap": "^1.0.4", - "@types/prosemirror-model": "^1.16.0", - "@types/prosemirror-schema-list": "^1.0.3", - "@types/prosemirror-state": "^1.2.8", - "@types/prosemirror-transform": "^1.1.5", - "@types/prosemirror-view": "^1.23.1", - "prosemirror-commands": "^1.2.1", - "prosemirror-keymap": "^1.1.5", - "prosemirror-model": "^1.16.1", - "prosemirror-schema-list": "^1.1.6", - "prosemirror-state": "^1.3.4", - "prosemirror-transform": "^1.3.3", - "prosemirror-view": "^1.23.6" + "prosemirror-commands": "1.3.0", + "prosemirror-keymap": "1.2.0", + "prosemirror-model": "1.18.1", + "prosemirror-schema-list": "1.2.0", + "prosemirror-state": "1.4.1", + "prosemirror-transform": "1.6.0", + "prosemirror-view": "1.26.2" } }, "@tiptap/extension-blockquote": { - "version": "2.0.0-beta.26", - "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.26.tgz", - "integrity": "sha512-A6yjcYovONJfOjQFk6vDYXswaCdCtCwjL7w9VTB0R2DLTuJvvRt9DWN0IDcMrj5G+aMgDq4GUUTitv+2Y8krDg==", + "version": "2.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.29.tgz", + "integrity": "sha512-zMYT5TtpKWav9VhTn4JLyMvXmhEdbD6on0MdhcTjRm0I5ugyR4ZbJwh2aelM7G9DZVYzB8jZU18OSDJmo7Af7w==", "requires": {} }, "@tiptap/extension-bold": { - "version": "2.0.0-beta.26", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.26.tgz", - "integrity": "sha512-pnO0I5sEQM3pmowjMGQ74adLzvc6HqGyLyqMizaGMicPu9uTYlSdId+qckYEEgPwPMaEShtv2Vg+ZHs7KVqfcg==", + "version": "2.0.0-beta.28", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.28.tgz", + "integrity": "sha512-DY8GOzw9xjmTFrnvTbgHUNxTnDfKrkDgrhe0SUvdkT2udntWp8umPdhPiD3vczLgHOJw6tX68qMRjbsR1ZPcHQ==", "requires": {} }, "@tiptap/extension-bubble-menu": { - "version": "2.0.0-beta.56", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.56.tgz", - "integrity": "sha512-nZozwauICdaNPmDPrSn1JFd/9/2rLtK8i2vBOcqxWHObVROvu8ZlJspnrJv23vS6P7/ZO3e/QLVHpnn+1yVq3g==", + "version": "2.0.0-beta.61", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.61.tgz", + "integrity": "sha512-T3Yx+y1sUnXAJjK1CUfsQewSxOpDca9KzKqN2H9c9RZ9UlorR9XmZg6YYW7m9a7adeihj+o3cCO9jRd8dV+nnA==", "requires": { - "prosemirror-state": "^1.3.4", - "prosemirror-view": "^1.23.6", + "prosemirror-state": "1.4.1", + "prosemirror-view": "1.26.2", "tippy.js": "^6.3.7" } }, "@tiptap/extension-bullet-list": { - "version": "2.0.0-beta.26", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.0.0-beta.26.tgz", - "integrity": "sha512-1n5HV8gY1tLjPk4x48nva6SZlFHoPlRfF6pqSu9JcJxPO7FUSPxUokuz4swYNe0LRrtykfyNz44dUcxKVhoFow==", - "requires": {} - }, - "@tiptap/extension-code": { - "version": "2.0.0-beta.26", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.0.0-beta.26.tgz", - "integrity": "sha512-QcFWdEFfbJ1n5UFFBD17QPPAJ3J5p/b7XV484u0shCzywO7aNPV32QeHy1z0eMoyZtCbOWf6hg/a7Ugv8IwpHw==", + "version": "2.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.0.0-beta.29.tgz", + "integrity": "sha512-R8VB2l1ZB6VeGWx/t/04nBS5Wg3qjIDEZCpPihj2fccJOw99Lu0Ub2UJg/SfdGmeNNpBh4ZYYFv1g/XjyzlXKg==", "requires": {} }, - "@tiptap/extension-code-block": { - "version": "2.0.0-beta.37", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.0.0-beta.37.tgz", - "integrity": "sha512-mJAM+PHaNoKRYwM3D36lZ51/aoPxxvZNQn3UBnZ6G7l0ZJSgB3JvBEzqK6S8nNFeYIIxGwv4QF6vXe4MG9ie2g==", - "requires": { - "prosemirror-state": "^1.3.4" - } - }, "@tiptap/extension-color": { - "version": "2.0.0-beta.9", - "resolved": "https://registry.npmjs.org/@tiptap/extension-color/-/extension-color-2.0.0-beta.9.tgz", - "integrity": "sha512-c8zcaNCdwUwbgrutfsG7LD9KH7ZvDVwKOZHbOL4gMSwdH9s+6r1ThRFLEbKgHIJlTa2jd96qoo+lVfj1Qwp7ww==", + "version": "2.0.0-beta.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-color/-/extension-color-2.0.0-beta.12.tgz", + "integrity": "sha512-ddFIUdb7e0gLlYlcQiyZ+zRC4nXtpX2bhOHZ7IdCasoHYEWs/8dsncngLtymAgsRBeruMZ5K6ZhAYtgQVUugqQ==", "requires": {} }, "@tiptap/extension-document": { - "version": "2.0.0-beta.15", - "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.0.0-beta.15.tgz", - "integrity": "sha512-ypENC+xUYD5m2t+KOKNYqyXnanXd5fxyIyhR1qeEEwwQwMXGNrO3kCH6O4mIDCpy+/WqHvVay2tV5dVsXnvY8w==", + "version": "2.0.0-beta.17", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.0.0-beta.17.tgz", + "integrity": "sha512-L6sg0FNchbtIpQkCSjMmItVGs3/vep8Fq56WRtDc1wBSGUSmtHaxQG7F2FZLnNIUMuvzVMRD81m2vYG73WkY6A==", "requires": {} }, - "@tiptap/extension-dropcursor": { - "version": "2.0.0-beta.25", - "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.0.0-beta.25.tgz", - "integrity": "sha512-GYf5s6dkZtsDy+TEkrQK6kLbfbitG4qnk02D+FlhlJMI/Nnx8rYCRJbwEHDdqrfX7XwZzULMqqqHvzxZYrEeNg==", - "requires": { - "@types/prosemirror-dropcursor": "^1.0.3", - "prosemirror-dropcursor": "^1.4.0" - } - }, "@tiptap/extension-floating-menu": { - "version": "2.0.0-beta.51", - "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.51.tgz", - "integrity": "sha512-rEe7jADK9xr2n2LJsrGEN3Dz7sEGC1JT/7AdTdaZBxQRQvwxTjomqYGrt+LnX+v0MYggh6swMzj7upJosnKbBg==", + "version": "2.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.56.tgz", + "integrity": "sha512-j/evHE/6UPGkIgXny9IGcAh0IrcnQmg0b2NBYebs2mqx9xYKYoe+0jVgNdLp/0M3MRgQCzyWTyatBDBFOUR2mw==", "requires": { - "prosemirror-state": "^1.3.4", - "prosemirror-view": "^1.23.6", + "prosemirror-state": "1.4.1", + "prosemirror-view": "1.26.2", "tippy.js": "^6.3.7" } }, "@tiptap/extension-font-family": { - "version": "2.0.0-beta.21", - "resolved": "https://registry.npmjs.org/@tiptap/extension-font-family/-/extension-font-family-2.0.0-beta.21.tgz", - "integrity": "sha512-5KVCtuEBf1QyZxs/IOL0CPDtB5X3rk8QdDB8fB+UlASa6c/Dq59Uo2aObGOgAWNDdY0Vd9MmuDTvnJKP2LI2Ng==", - "requires": {} - }, - "@tiptap/extension-gapcursor": { - "version": "2.0.0-beta.34", - "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.0.0-beta.34.tgz", - "integrity": "sha512-Vm8vMWWQ2kJcUOLfB5CEo5pYgyudI7JeeiZvX9ScPmUmgKVYhEpt3EAICY9pUYJ41aAVH35gZLXkUtsz2f9GHw==", - "requires": { - "@types/prosemirror-gapcursor": "^1.0.4", - "prosemirror-gapcursor": "^1.2.1" - } - }, - "@tiptap/extension-hard-break": { - "version": "2.0.0-beta.30", - "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.30.tgz", - "integrity": "sha512-X9xj/S+CikrbIE7ccUFVwit5QHEbflnKVxod+4zPwr1cxogFbE9AyLZE2MpYdx3z9LcnTYYi9leBqFrP4T/Olw==", + "version": "2.0.0-beta.24", + "resolved": "https://registry.npmjs.org/@tiptap/extension-font-family/-/extension-font-family-2.0.0-beta.24.tgz", + "integrity": "sha512-R7fgyJb4X6KS2Xcr73dYDxM1NSBNIQrtHLeSa3Cfc3Y9UDqeMFUIEgYUh4sBLBi3Z8Y9D3RRMvxN1x39j5eLLA==", "requires": {} }, "@tiptap/extension-heading": { - "version": "2.0.0-beta.26", - "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.0.0-beta.26.tgz", - "integrity": "sha512-nR6W/3rjnZH1Swo7tGBoYsmO6xMvu9MGq6jlm3WVHCB7B3CsrRvCkTwGjVIbKTaZC4bQfx5gvAUpQFvwuU+M5w==", + "version": "2.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.0.0-beta.29.tgz", + "integrity": "sha512-q92jYcsT5bPhvuQaB0h44Z9r+Ii22tDYo082KMVnR4+tknHT/3xx+p4JC8KHjh+/5W8Quyafqy6mS8L8VX0zsQ==", "requires": {} }, "@tiptap/extension-highlight": { - "version": "2.0.0-beta.33", - "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.0.0-beta.33.tgz", - "integrity": "sha512-TXyMiCcY5a0w5UFax350xU+T79GKJw2XwJ6Punc6sY2RRRgKaEbN1ZF0JCdQhQvD1ooKImHzCRYR8Pldb0xgfg==", + "version": "2.0.0-beta.35", + "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.0.0-beta.35.tgz", + "integrity": "sha512-xvEKOyuTj4mhQ8GIOItaSymJhGkWt2gGuCvmFWnTVZAaJJQOlgUTdkmayLCtwoDDP7biiuDhRJokTukGGmhUZw==", "requires": {} }, - "@tiptap/extension-history": { - "version": "2.0.0-beta.21", - "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.0.0-beta.21.tgz", - "integrity": "sha512-0v8Cl30V4dsabdpspLdk+f+lMoIvLFlJN5WRxtc7RRZ5gfJVxPHwooIKdvC51brfh/oJtWFCNMRjhoz0fRaF9A==", - "requires": { - "@types/prosemirror-history": "^1.0.3", - "prosemirror-history": "^1.2.0" - } - }, - "@tiptap/extension-horizontal-rule": { - "version": "2.0.0-beta.31", - "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.0.0-beta.31.tgz", - "integrity": "sha512-MNc4retfjRgkv3qxqGya0+/BEd1Kmn+oMsCRvE+8x3sXyKIse+vdqMuG5qUcA6np0ZD/9hh1riiQ1GQdgc23Ng==", - "requires": { - "prosemirror-state": "^1.3.4" - } - }, "@tiptap/extension-image": { - "version": "2.0.0-beta.27", - "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.0.0-beta.27.tgz", - "integrity": "sha512-kdJ7V39yNdVWUco/RBe7WgvFevd81l+pU6+Je9HpelqBBP953wDttzLMuAWQB4AeLv9WhKSlORHiFv2SKsV5NA==", + "version": "2.0.0-beta.30", + "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.0.0-beta.30.tgz", + "integrity": "sha512-VhEmgiKkZMiKR7hbpJgIlIUS/QNjSGI5ER7mKDAbuV1IB5yb6nGjZ6o3Exrr2/CaTaW5hQarBC1z2Xgdu05EGg==", "requires": {} }, "@tiptap/extension-italic": { - "version": "2.0.0-beta.26", - "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.26.tgz", - "integrity": "sha512-vejGe2ra4K5ipFOn1U9viqF9X9nPTX8WSJpSOux+9UbKjHpANy7bz69tp66OIi/Wh5L/MMDc+luH/04qfVnpZw==", + "version": "2.0.0-beta.28", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.28.tgz", + "integrity": "sha512-/pKRiCfewh7nqiXRD3N4hQHfGrGNOiWPFYZfY35bSpvTms7PDb/MF7xT1CWW23hSpY31BBS+R/a66vlR/gqu7Q==", "requires": {} }, "@tiptap/extension-list-item": { - "version": "2.0.0-beta.20", - "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.0.0-beta.20.tgz", - "integrity": "sha512-5IPEspJt38t9ROj4xLUesOVEYlTT/R9Skd9meHRxJQZX1qrzBICs5PC/WRIsnexrvTBhdxpYgCYjpvpsJBlKuQ==", + "version": "2.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.0.0-beta.23.tgz", + "integrity": "sha512-AkzvdELz3ZnrlZM0r9+ritBDOnAjXHR/8zCZhW0ZlWx4zyKPMsNG5ygivY+xr4QT65NEGRT8P8b2zOhXrMjjMQ==", "requires": {} }, "@tiptap/extension-ordered-list": { - "version": "2.0.0-beta.27", - "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.27.tgz", - "integrity": "sha512-apFDeignxdZb3cA3p1HJu0zw1JgJdBYUBz1r7f99qdNybYuk3I/1MPUvlOuOgvIrBB/wydoyVDP+v9F7QN3tfQ==", + "version": "2.0.0-beta.30", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.30.tgz", + "integrity": "sha512-GRxGQdq1u0Rp5N8TjthCqoZ//460m343A0HCN7UwfQOnX7Ipv0UJemwNkSHWrl7Pexym9vy3yPWgrn7oRRmgEw==", "requires": {} }, "@tiptap/extension-paragraph": { - "version": "2.0.0-beta.23", - "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.0.0-beta.23.tgz", - "integrity": "sha512-VWAxyzecErYWk97Kv/Gkghh97zAQTcaVOisEnYYArZAlyYDaYM48qVssAC/vnRRynP2eQxb1EkppbAxE+bMHAA==", + "version": "2.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.0.0-beta.26.tgz", + "integrity": "sha512-WcYsuUa7LLfk0vi7I1dVjdMRu53B52FMMqd+UL1qPdDKVkU3DBsZVwPj+yyfQyqN8Mc/xyg9VacGaiKFLmWNDg==", "requires": {} }, "@tiptap/extension-strike": { - "version": "2.0.0-beta.27", - "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.0.0-beta.27.tgz", - "integrity": "sha512-2dmCgtesuDdivM/54Q+Y6Tc3JbGz1SkHP6c62piuqBiYLWg3xa16zChZOhfN8szbbQlBgLT6XRTDt3c2Ux+Dug==", + "version": "2.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.0.0-beta.29.tgz", + "integrity": "sha512-zqFuY7GfNmZ/KClt6kxQ+msGo3syqucP/Xnlihxi+/h/G+oTvEwyOIXCtDOltvxcsWH/TUsdr5vzLp0j+Mdc6Q==", "requires": {} }, "@tiptap/extension-subscript": { - "version": "2.0.0-beta.10", - "resolved": "https://registry.npmjs.org/@tiptap/extension-subscript/-/extension-subscript-2.0.0-beta.10.tgz", - "integrity": "sha512-er8/1lp0Rb+SKwEioW0w4oVf3EkdQZ0WS/5kPBG4W0DncfUMT+bw5de76S3kRL9PLZ9UShAL7wuXtuiSi5QsMw==", + "version": "2.0.0-beta.13", + "resolved": "https://registry.npmjs.org/@tiptap/extension-subscript/-/extension-subscript-2.0.0-beta.13.tgz", + "integrity": "sha512-L9f2zKzNI5y4YvMdNxHDT4Y+8gS1UwtbTJ1vUJdCZGfF8DrMuTZIRp3LjOxYXydr7NGEXyYbucdm97Tzrsp8WA==", "requires": {} }, "@tiptap/extension-superscript": { - "version": "2.0.0-beta.10", - "resolved": "https://registry.npmjs.org/@tiptap/extension-superscript/-/extension-superscript-2.0.0-beta.10.tgz", - "integrity": "sha512-TUUBS8XsD2MorGORYVlhGDH7wcc9diSbHscD4Dnz8pKWVR0JPUd/od4h5qSffDzAOKxtphTiX9LOFWk6zVooKg==", + "version": "2.0.0-beta.13", + "resolved": "https://registry.npmjs.org/@tiptap/extension-superscript/-/extension-superscript-2.0.0-beta.13.tgz", + "integrity": "sha512-Vr9KIG2c4jzymcMMQCjhx2gppmRvnbw6Xvrd8YCpK4szyYI1ClMQ5KQMYl2zV3Y4ZIsivRSy9cE0ipGsXGE3Gw==", "requires": {} }, "@tiptap/extension-text": { - "version": "2.0.0-beta.15", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.0.0-beta.15.tgz", - "integrity": "sha512-S3j2+HyV2gsXZP8Wg/HA+YVXQsZ3nrXgBM9HmGAxB0ESOO50l7LWfip0f3qcw1oRlh5H3iLPkA6/f7clD2/TFA==", + "version": "2.0.0-beta.17", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.0.0-beta.17.tgz", + "integrity": "sha512-OyKL+pqWJEtjyd9/mrsuY1kZh2b3LWpOQDWKtd4aWR4EA0efmQG+7FPwcIeAVEh7ZoqM+/ABCnPjN6IjzIrSfg==", "requires": {} }, "@tiptap/extension-text-align": { - "version": "2.0.0-beta.29", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.0.0-beta.29.tgz", - "integrity": "sha512-FNGpl0tVtgG7AK9kVWF/+CGYHta05NpoME4/j6+vhNlZLBNXRA+AKg7W5T8UxmtaC9yGoJsBs2X8M9eCxWVaEQ==", + "version": "2.0.0-beta.31", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.0.0-beta.31.tgz", + "integrity": "sha512-gSJqi57piiMPc2r6WEkXv7ZgQIogigsRUhmlnZC/7s3zzOvjXrexWnV0Ctt/9A7BKcM7OHMykpZyoewvk6QRTw==", "requires": {} }, "@tiptap/extension-text-style": { - "version": "2.0.0-beta.23", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.0.0-beta.23.tgz", - "integrity": "sha512-h/7o//RB4WRrLKWV7E5eFk7tZnfjH0Wt9klixOmvTmus6dm00a7r6wTuaT1GNjfPOgClP3K185lTA5rrdgrxRA==", + "version": "2.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.0.0-beta.26.tgz", + "integrity": "sha512-sHUlj5j86W53jvj9ijhXGyqxDdT2c9B7lVwdmDtksvSIrRYuCFqn7hFBzlypNBb56zePqBlIFymUmhK283L6fQ==", "requires": {} }, "@tiptap/extension-underline": { - "version": "2.0.0-beta.23", - "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.0.0-beta.23.tgz", - "integrity": "sha512-pMjFH/NpFWLd2XQQa5rG9rGVQ9mu3ygdtu6VGfJ3aAjzBiyLXDKhE4biIFWyFsr8zLpp7DjwbrmLV0UGvbG1WQ==", + "version": "2.0.0-beta.25", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.0.0-beta.25.tgz", + "integrity": "sha512-kRDdb/mF6QWzFGV3cQuLh6xyXULXaKPL/TghefoOZhwkdIWV/M3zFar5tsZO54+tbIrzxoVP6t7mO2Y5G/SLDQ==", "requires": {} }, - "@tiptap/starter-kit": { - "version": "2.0.0-beta.184", - "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.0.0-beta.184.tgz", - "integrity": "sha512-FgF94i5RQzXiGAIkaubnXEaYwJfiZRbMPZcmarwNo8IyqPnLT34Q1yjw/qZ3nv7rDehWV5l/zenbrrNtPYVCkA==", - "requires": { - "@tiptap/core": "^2.0.0-beta.175", - "@tiptap/extension-blockquote": "^2.0.0-beta.26", - "@tiptap/extension-bold": "^2.0.0-beta.26", - "@tiptap/extension-bullet-list": "^2.0.0-beta.26", - "@tiptap/extension-code": "^2.0.0-beta.26", - "@tiptap/extension-code-block": "^2.0.0-beta.37", - "@tiptap/extension-document": "^2.0.0-beta.15", - "@tiptap/extension-dropcursor": "^2.0.0-beta.25", - "@tiptap/extension-gapcursor": "^2.0.0-beta.34", - "@tiptap/extension-hard-break": "^2.0.0-beta.30", - "@tiptap/extension-heading": "^2.0.0-beta.26", - "@tiptap/extension-history": "^2.0.0-beta.21", - "@tiptap/extension-horizontal-rule": "^2.0.0-beta.31", - "@tiptap/extension-italic": "^2.0.0-beta.26", - "@tiptap/extension-list-item": "^2.0.0-beta.20", - "@tiptap/extension-ordered-list": "^2.0.0-beta.27", - "@tiptap/extension-paragraph": "^2.0.0-beta.23", - "@tiptap/extension-strike": "^2.0.0-beta.27", - "@tiptap/extension-text": "^2.0.0-beta.15" - } - }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -18143,7 +18048,8 @@ "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true + "dev": true, + "peer": true }, "@types/lodash": { "version": "4.14.182", @@ -18169,7 +18075,8 @@ "@types/orderedmap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/orderedmap/-/orderedmap-1.0.0.tgz", - "integrity": "sha512-dxKo80TqYx3YtBipHwA/SdFmMMyLCnP+5mkEqN0eMjcTBzHkiiX0ES118DsjDBjvD+zeSsSU9jULTZ+frog+Gw==" + "integrity": "sha512-dxKo80TqYx3YtBipHwA/SdFmMMyLCnP+5mkEqN0eMjcTBzHkiiX0ES118DsjDBjvD+zeSsSU9jULTZ+frog+Gw==", + "peer": true }, "@types/parse-json": { "version": "4.0.0", @@ -18177,75 +18084,20 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, - "@types/prosemirror-commands": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@types/prosemirror-commands/-/prosemirror-commands-1.0.4.tgz", - "integrity": "sha512-utDNYB3EXLjAfYIcRWJe6pn3kcQ5kG4RijbT/0Y/TFOm6yhvYS/D9eJVnijdg9LDjykapcezchxGRqFD5LcyaQ==", - "requires": { - "@types/prosemirror-model": "*", - "@types/prosemirror-state": "*", - "@types/prosemirror-view": "*" - } - }, - "@types/prosemirror-dropcursor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/prosemirror-dropcursor/-/prosemirror-dropcursor-1.0.3.tgz", - "integrity": "sha512-b0/8njnJ4lwyHKcGuCMf3x7r1KjxyugB1R/c2iMCjplsJHSC7UY9+OysqgJR5uUXRekUSGniiLgBtac/lvH6wg==", - "requires": { - "@types/prosemirror-state": "*" - } - }, - "@types/prosemirror-gapcursor": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@types/prosemirror-gapcursor/-/prosemirror-gapcursor-1.0.4.tgz", - "integrity": "sha512-9xKjFIG5947dzerFvkLWp6F53JwrUYoYwh3SgcTFEp8SbSfNNrez/PFYVZKPnoqPoaK5WtTdQTaMwpCV9rXQIg==", - "requires": { - "@types/prosemirror-model": "*", - "@types/prosemirror-state": "*" - } - }, - "@types/prosemirror-history": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/prosemirror-history/-/prosemirror-history-1.0.3.tgz", - "integrity": "sha512-5TloMDRavgLjOAKXp1Li8u0xcsspzbT1Cm9F2pwHOkgvQOz1jWQb2VIXO7RVNsFjLBZdIXlyfSLivro3DuMWXg==", - "requires": { - "@types/prosemirror-model": "*", - "@types/prosemirror-state": "*" - } - }, - "@types/prosemirror-keymap": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@types/prosemirror-keymap/-/prosemirror-keymap-1.0.4.tgz", - "integrity": "sha512-ycevwkqUh+jEQtPwqO7sWGcm+Sybmhu8MpBsM8DlO3+YTKnXbKA6SDz/+q14q1wK3UA8lHJyfR+v+GPxfUSemg==", - "requires": { - "@types/prosemirror-commands": "*", - "@types/prosemirror-model": "*", - "@types/prosemirror-state": "*", - "@types/prosemirror-view": "*" - } - }, "@types/prosemirror-model": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/@types/prosemirror-model/-/prosemirror-model-1.16.2.tgz", "integrity": "sha512-1XPJopkKP3oHSBP61uuSuW13DIDZPWvAzP6Pv2/6mixk8EBPUeRGIW548DjJTicMo23gEg1zvCZy9asblQdWag==", + "peer": true, "requires": { "@types/orderedmap": "*" } }, - "@types/prosemirror-schema-list": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/prosemirror-schema-list/-/prosemirror-schema-list-1.0.3.tgz", - "integrity": "sha512-uWybOf+M2Ea7rlbs0yLsS4YJYNGXYtn4N+w8HCw3Vvfl6wBAROzlMt0gV/D/VW/7J/LlAjwMezuGe8xi24HzXA==", - "requires": { - "@types/orderedmap": "*", - "@types/prosemirror-model": "*", - "@types/prosemirror-state": "*" - } - }, "@types/prosemirror-state": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@types/prosemirror-state/-/prosemirror-state-1.3.0.tgz", "integrity": "sha512-nMdUF6w8B++NH4V54X+4GvDty7M02UfuHQW0s1AS25Z4ZrOW4RSY2+s57doXBbeMSjzYV/QoMxCY2sT3KQ2VdQ==", + "peer": true, "requires": { "@types/prosemirror-model": "*", "@types/prosemirror-transform": "*", @@ -18256,6 +18108,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@types/prosemirror-transform/-/prosemirror-transform-1.4.0.tgz", "integrity": "sha512-ntfuTl9nJWHvFykCmqJj4YQMws6G5H9nBaxHW0xRqfTxDxUvX2bCloqRN7bQTWg9h3VSP2lx45UuET1fn/oQ9Q==", + "peer": true, "requires": { "@types/prosemirror-model": "*" } @@ -18264,6 +18117,7 @@ "version": "1.23.3", "resolved": "https://registry.npmjs.org/@types/prosemirror-view/-/prosemirror-view-1.23.3.tgz", "integrity": "sha512-T5dPDmZiXAazJVSvnx55D6h4mcpiH2q2wTyO9zIeOdox5zx964+zcDl9dFNaXG3qCGlERwMPckhBZL1HCxyygw==", + "peer": true, "requires": { "@types/prosemirror-model": "*", "@types/prosemirror-state": "*", @@ -18326,98 +18180,174 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.23.0.tgz", - "integrity": "sha512-hEcSmG4XodSLiAp1uxv/OQSGsDY6QN3TcRU32gANp+19wGE1QQZLRS8/GV58VRUoXhnkuJ3ZxNQ3T6Z6zM59DA==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.31.0.tgz", + "integrity": "sha512-VKW4JPHzG5yhYQrQ1AzXgVgX8ZAJEvCz0QI6mLRX4tf7rnFfh5D8SKm0Pq6w5PyNfAWJk6sv313+nEt3ohWMBQ==", "dev": true, + "peer": true, "requires": { - "@typescript-eslint/scope-manager": "5.23.0", - "@typescript-eslint/type-utils": "5.23.0", - "@typescript-eslint/utils": "5.23.0", - "debug": "^4.3.2", + "@typescript-eslint/scope-manager": "5.31.0", + "@typescript-eslint/type-utils": "5.31.0", + "@typescript-eslint/utils": "5.31.0", + "debug": "^4.3.4", "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", + "ignore": "^5.2.0", "regexpp": "^3.2.0", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "peer": true, + "requires": { + "ms": "2.1.2" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "@typescript-eslint/parser": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.23.0.tgz", - "integrity": "sha512-V06cYUkqcGqpFjb8ttVgzNF53tgbB/KoQT/iB++DOIExKmzI9vBJKjZKt/6FuV9c+zrDsvJKbJ2DOCYwX91cbw==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.31.0.tgz", + "integrity": "sha512-UStjQiZ9OFTFReTrN+iGrC6O/ko9LVDhreEK5S3edmXgR396JGq7CoX2TWIptqt/ESzU2iRKXAHfSF2WJFcWHw==", "dev": true, + "peer": true, "requires": { - "@typescript-eslint/scope-manager": "5.23.0", - "@typescript-eslint/types": "5.23.0", - "@typescript-eslint/typescript-estree": "5.23.0", - "debug": "^4.3.2" + "@typescript-eslint/scope-manager": "5.31.0", + "@typescript-eslint/types": "5.31.0", + "@typescript-eslint/typescript-estree": "5.31.0", + "debug": "^4.3.4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "peer": true, + "requires": { + "ms": "2.1.2" + } + } } }, "@typescript-eslint/scope-manager": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.23.0.tgz", - "integrity": "sha512-EhjaFELQHCRb5wTwlGsNMvzK9b8Oco4aYNleeDlNuL6qXWDF47ch4EhVNPh8Rdhf9tmqbN4sWDk/8g+Z/J8JVw==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.31.0.tgz", + "integrity": "sha512-8jfEzBYDBG88rcXFxajdVavGxb5/XKXyvWgvD8Qix3EEJLCFIdVloJw+r9ww0wbyNLOTYyBsR+4ALNGdlalLLg==", "dev": true, + "peer": true, "requires": { - "@typescript-eslint/types": "5.23.0", - "@typescript-eslint/visitor-keys": "5.23.0" + "@typescript-eslint/types": "5.31.0", + "@typescript-eslint/visitor-keys": "5.31.0" } }, "@typescript-eslint/type-utils": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.23.0.tgz", - "integrity": "sha512-iuI05JsJl/SUnOTXA9f4oI+/4qS/Zcgk+s2ir+lRmXI+80D8GaGwoUqs4p+X+4AxDolPpEpVUdlEH4ADxFy4gw==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.31.0.tgz", + "integrity": "sha512-7ZYqFbvEvYXFn9ax02GsPcEOmuWNg+14HIf4q+oUuLnMbpJ6eHAivCg7tZMVwzrIuzX3QCeAOqKoyMZCv5xe+w==", "dev": true, + "peer": true, "requires": { - "@typescript-eslint/utils": "5.23.0", - "debug": "^4.3.2", + "@typescript-eslint/utils": "5.31.0", + "debug": "^4.3.4", "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "peer": true, + "requires": { + "ms": "2.1.2" + } + } } }, "@typescript-eslint/types": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.23.0.tgz", - "integrity": "sha512-NfBsV/h4dir/8mJwdZz7JFibaKC3E/QdeMEDJhiAE3/eMkoniZ7MjbEMCGXw6MZnZDMN3G9S0mH/6WUIj91dmw==", - "dev": true + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.31.0.tgz", + "integrity": "sha512-/f/rMaEseux+I4wmR6mfpM2wvtNZb1p9hAV77hWfuKc3pmaANp5dLAZSiE3/8oXTYTt3uV9KW5yZKJsMievp6g==", + "dev": true, + "peer": true }, "@typescript-eslint/typescript-estree": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.23.0.tgz", - "integrity": "sha512-xE9e0lrHhI647SlGMl+m+3E3CKPF1wzvvOEWnuE3CCjjT7UiRnDGJxmAcVKJIlFgK6DY9RB98eLr1OPigPEOGg==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.31.0.tgz", + "integrity": "sha512-3S625TMcARX71wBc2qubHaoUwMEn+l9TCsaIzYI/ET31Xm2c9YQ+zhGgpydjorwQO9pLfR/6peTzS/0G3J/hDw==", "dev": true, + "peer": true, "requires": { - "@typescript-eslint/types": "5.23.0", - "@typescript-eslint/visitor-keys": "5.23.0", - "debug": "^4.3.2", - "globby": "^11.0.4", + "@typescript-eslint/types": "5.31.0", + "@typescript-eslint/visitor-keys": "5.31.0", + "debug": "^4.3.4", + "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "peer": true, + "requires": { + "ms": "2.1.2" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "@typescript-eslint/utils": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.23.0.tgz", - "integrity": "sha512-dbgaKN21drqpkbbedGMNPCtRPZo1IOUr5EI9Jrrh99r5UW5Q0dz46RKXeSBoPV+56R6dFKpbrdhgUNSJsDDRZA==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.31.0.tgz", + "integrity": "sha512-kcVPdQS6VIpVTQ7QnGNKMFtdJdvnStkqS5LeALr4rcwx11G6OWb2HB17NMPnlRHvaZP38hL9iK8DdE9Fne7NYg==", "dev": true, + "peer": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.23.0", - "@typescript-eslint/types": "5.23.0", - "@typescript-eslint/typescript-estree": "5.23.0", + "@typescript-eslint/scope-manager": "5.31.0", + "@typescript-eslint/types": "5.31.0", + "@typescript-eslint/typescript-estree": "5.31.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.23.0.tgz", - "integrity": "sha512-Vd4mFNchU62sJB8pX19ZSPog05B0Y0CE2UxAZPT5k4iqhRYjPnqyY3woMxCd0++t9OTqkgjST+1ydLBi7e2Fvg==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.31.0.tgz", + "integrity": "sha512-ZK0jVxSjS4gnPirpVjXHz7mgdOsZUHzNYSfTw2yPa3agfbt9YfqaBiBZFSSxeBWnpWkzCxTfUpnzA3Vily/CSg==", "dev": true, + "peer": true, "requires": { - "@typescript-eslint/types": "5.23.0", - "eslint-visitor-keys": "^3.0.0" + "@typescript-eslint/types": "5.31.0", + "eslint-visitor-keys": "^3.3.0" } }, "@webassemblyjs/ast": { @@ -18632,6 +18562,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "peer": true, "requires": {} }, "adjust-sourcemap-loader": { @@ -18815,6 +18746,7 @@ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", "dev": true, + "peer": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -18838,6 +18770,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", "dev": true, + "peer": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -19578,7 +19511,8 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true + "dev": true, + "peer": true }, "connect": { "version": "3.7.0", @@ -20024,7 +19958,8 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "peer": true }, "default-gateway": { "version": "6.0.3", @@ -20234,6 +20169,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, + "peer": true, "requires": { "esutils": "^2.0.2" } @@ -20483,6 +20419,7 @@ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.0.tgz", "integrity": "sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA==", "dev": true, + "peer": true, "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", @@ -20520,6 +20457,7 @@ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", "dev": true, + "peer": true, "requires": { "has": "^1.0.3" } @@ -20529,6 +20467,7 @@ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, + "peer": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -20724,12 +20663,13 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.15.0.tgz", - "integrity": "sha512-GG5USZ1jhCu8HJkzGgeK8/+RGnHaNYZGrGDzUtigK3BsGESW/rs2az23XqE0WVwDxy1VRvvjSSGu5nB0Bu+6SA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.20.0.tgz", + "integrity": "sha512-d4ixhz5SKCa1D6SCPrivP7yYVi7nyD6A4vs6HIAul9ujBzcEmZVM3/0NN/yu5nKhmO1wjp5xQ46iRfmDGlOviA==", "dev": true, + "peer": true, "requires": { - "@eslint/eslintrc": "^1.2.3", + "@eslint/eslintrc": "^1.3.0", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -20747,7 +20687,7 @@ "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", - "globals": "^13.6.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -20771,6 +20711,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -20783,6 +20724,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "peer": true, "requires": { "color-convert": "^2.0.1" } @@ -20791,13 +20733,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "peer": true }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "peer": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -20808,6 +20752,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "peer": true, "requires": { "color-name": "~1.1.4" } @@ -20816,19 +20761,22 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "peer": true }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true + "dev": true, + "peer": true }, "eslint-scope": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", "dev": true, + "peer": true, "requires": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -20838,13 +20786,15 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true + "dev": true, + "peer": true }, "glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "peer": true, "requires": { "is-glob": "^4.0.3" } @@ -20854,6 +20804,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", "dev": true, + "peer": true, "requires": { "type-fest": "^0.20.2" } @@ -20862,13 +20813,15 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "dev": true, + "peer": true }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "peer": true, "requires": { "argparse": "^2.0.1" } @@ -20877,13 +20830,15 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "peer": true }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "peer": true, "requires": { "brace-expansion": "^1.1.7" } @@ -20893,6 +20848,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -20901,7 +20857,8 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true + "dev": true, + "peer": true } } }, @@ -20910,6 +20867,7 @@ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", "dev": true, + "peer": true, "requires": { "confusing-browser-globals": "^1.0.10", "object.assign": "^4.1.2", @@ -20921,7 +20879,8 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "dev": true, + "peer": true } } }, @@ -20930,6 +20889,7 @@ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.0.0.tgz", "integrity": "sha512-elNiuzD0kPAPTXjFWg+lE24nMdHMtuxgYoD30OyMD6yrW1AhFZPAg27VX7d3tzOErw+dgJTNWfRSDqEcXb4V0g==", "dev": true, + "peer": true, "requires": { "eslint-config-airbnb-base": "^15.0.0" } @@ -20939,6 +20899,7 @@ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", "dev": true, + "peer": true, "requires": { "debug": "^3.2.7", "resolve": "^1.20.0" @@ -20949,6 +20910,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "peer": true, "requires": { "ms": "^2.1.1" } @@ -20960,6 +20922,7 @@ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", "dev": true, + "peer": true, "requires": { "debug": "^3.2.7", "find-up": "^2.1.0" @@ -20970,6 +20933,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "peer": true, "requires": { "ms": "^2.1.1" } @@ -20979,6 +20943,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, + "peer": true, "requires": { "locate-path": "^2.0.0" } @@ -20988,6 +20953,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, + "peer": true, "requires": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" @@ -20998,6 +20964,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, + "peer": true, "requires": { "p-try": "^1.0.0" } @@ -21007,6 +20974,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, + "peer": true, "requires": { "p-limit": "^1.1.0" } @@ -21015,13 +20983,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true + "dev": true, + "peer": true }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true + "dev": true, + "peer": true } } }, @@ -21030,6 +21000,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", "dev": true, + "peer": true, "requires": { "array-includes": "^3.1.4", "array.prototype.flat": "^1.2.5", @@ -21051,6 +21022,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "peer": true, "requires": { "ms": "2.0.0" } @@ -21060,6 +21032,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, + "peer": true, "requires": { "esutils": "^2.0.2" } @@ -21069,6 +21042,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "peer": true, "requires": { "brace-expansion": "^1.1.7" } @@ -21077,7 +21051,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "dev": true, + "peer": true } } }, @@ -21096,6 +21071,7 @@ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, + "peer": true, "requires": { "eslint-visitor-keys": "^2.0.0" }, @@ -21104,7 +21080,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true + "dev": true, + "peer": true } } }, @@ -21112,7 +21089,8 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true + "dev": true, + "peer": true }, "esotope-hammerhead": { "version": "0.6.1", @@ -21127,6 +21105,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", "dev": true, + "peer": true, "requires": { "acorn": "^8.7.1", "acorn-jsx": "^5.3.2", @@ -21144,6 +21123,7 @@ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "dev": true, + "peer": true, "requires": { "estraverse": "^5.1.0" }, @@ -21152,7 +21132,8 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true + "dev": true, + "peer": true } } }, @@ -21364,7 +21345,8 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true + "dev": true, + "peer": true }, "fastq": { "version": "1.13.0", @@ -21397,6 +21379,7 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, + "peer": true, "requires": { "flat-cache": "^3.0.4" } @@ -21497,6 +21480,7 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, + "peer": true, "requires": { "flatted": "^3.1.0", "rimraf": "^3.0.2" @@ -21584,6 +21568,7 @@ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", "dev": true, + "peer": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -21595,7 +21580,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true + "dev": true, + "peer": true }, "functions-have-names": { "version": "1.2.3", @@ -21669,6 +21655,7 @@ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dev": true, + "peer": true, "requires": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" @@ -21750,7 +21737,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true + "dev": true, + "peer": true }, "has-flag": { "version": "3.0.0", @@ -22167,6 +22155,7 @@ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", "dev": true, + "peer": true, "requires": { "get-intrinsic": "^1.1.0", "has": "^1.0.3", @@ -22223,6 +22212,7 @@ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, + "peer": true, "requires": { "has-bigints": "^1.0.1" } @@ -22241,6 +22231,7 @@ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, + "peer": true, "requires": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -22250,7 +22241,8 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true + "dev": true, + "peer": true }, "is-ci": { "version": "1.2.1", @@ -22332,7 +22324,8 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true + "dev": true, + "peer": true }, "is-number": { "version": "7.0.0", @@ -22344,6 +22337,7 @@ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, + "peer": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -22399,6 +22393,7 @@ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dev": true, + "peer": true, "requires": { "call-bind": "^1.0.2" } @@ -22413,6 +22408,7 @@ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, + "peer": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -22422,6 +22418,7 @@ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, + "peer": true, "requires": { "has-symbols": "^1.0.2" } @@ -22442,6 +22439,7 @@ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, + "peer": true, "requires": { "call-bind": "^1.0.2" } @@ -22652,7 +22650,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true + "dev": true, + "peer": true }, "json5": { "version": "2.2.1", @@ -22975,6 +22974,7 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "peer": true, "requires": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -23068,7 +23068,8 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "peer": true }, "lodash.once": { "version": "4.1.1", @@ -23509,7 +23510,8 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true + "dev": true, + "peer": true }, "needle": { "version": "2.9.1", @@ -23554,11 +23556,11 @@ "requires": {} }, "ngx-tiptap": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/ngx-tiptap/-/ngx-tiptap-3.0.4.tgz", - "integrity": "sha512-iU69pGqYoan5v9N8l92tpFSncB7x5mbXcJVwJwUDtEI922N+59cd4pw5y+NrN3er9E/HVAgOLfRB40TDHliwqQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ngx-tiptap/-/ngx-tiptap-5.0.0.tgz", + "integrity": "sha512-SlySnakZ7OroJSG5owPqVQ1ncqS9tgWYQ7551aI2VzZmcdCKKss00cdy+TqNyeRUg0b1+9Ep+yorUmg2CHPKLg==", "requires": { - "tslib": "^2.2.0" + "tslib": "^2.3.0" } }, "ngx-translate-testing": { @@ -23951,6 +23953,7 @@ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", "dev": true, + "peer": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -23962,6 +23965,7 @@ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", "dev": true, + "peer": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -24021,6 +24025,7 @@ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "dev": true, + "peer": true, "requires": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -24099,9 +24104,9 @@ } }, "orderedmap": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.1.7.tgz", - "integrity": "sha512-B1SuadDDwIRXXutaJQ1xjreGL3hxujpexBG4PquoXbgJD8bjp2k8b8qI/mk7q0LUdIx7T8IALWB8mPbfsjbGCw==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.0.0.tgz", + "integrity": "sha512-buf4PoAMlh45b8a8gsGy/X6w279TSqkyAS0C0wdTSJwFSU+ljQFJON5I8NfjLHoCXwpSROIo2wr0g33T+kQshQ==" }, "os-family": { "version": "1.1.0", @@ -24816,7 +24821,8 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true + "dev": true, + "peer": true }, "pretty-bytes": { "version": "5.6.0", @@ -24859,93 +24865,63 @@ } }, "prosemirror-commands": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.2.2.tgz", - "integrity": "sha512-TX+KpWudMon06frryfpO/u7hsQv2hu8L4VSVbCpi3/7wXHBgl+35mV85qfa3RpT8xD2f3MdeoTqH0vy5JdbXPg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.3.0.tgz", + "integrity": "sha512-BwBbZ5OAScPcm0x7H8SPbqjuEJnCU2RJT9LDyOiiIl/3NbL1nJZI4SFNHwU2e/tRr2Xe7JsptpzseqvZvToLBQ==", "requires": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.0.0" } }, - "prosemirror-dropcursor": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.4.0.tgz", - "integrity": "sha512-6+YwTjmqDwlA/Dm+5wK67ezgqgjA/MhSDgaNxKUzH97SmeuWFXyLeDRxxOPZeSo7yTxcDGUCWTEjmQZsVBuMrQ==", - "requires": { - "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.1.0", - "prosemirror-view": "^1.1.0" - } - }, - "prosemirror-gapcursor": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.2.2.tgz", - "integrity": "sha512-7YzuRBbu9W7HGQde84kCHfIjaRLNcAdeijbgqrm/R9dsdTWkV+rrdcmic/sCc+bptiNpvjCEE+R6hrbT8zFQeQ==", - "requires": { - "prosemirror-keymap": "^1.0.0", - "prosemirror-model": "^1.0.0", - "prosemirror-state": "^1.0.0", - "prosemirror-view": "^1.0.0" - } - }, - "prosemirror-history": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.2.0.tgz", - "integrity": "sha512-B9v9xtf4fYbKxQwIr+3wtTDNLDZcmMMmGiI3TAPShnUzvo+Rmv1GiUrsQChY1meetHl7rhML2cppF3FTs7f7UQ==", - "requires": { - "prosemirror-state": "^1.2.2", - "prosemirror-transform": "^1.0.0", - "rope-sequence": "^1.3.0" - } - }, "prosemirror-keymap": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.1.5.tgz", - "integrity": "sha512-8SZgPH3K+GLsHL2wKuwBD9rxhsbnVBTwpHCO4VUO5GmqUQlxd/2GtBVWTsyLq4Dp3N9nGgPd3+lZFKUDuVp+Vw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.0.tgz", + "integrity": "sha512-TdSfu+YyLDd54ufN/ZeD1VtBRYpgZnTPnnbY+4R08DDgs84KrIPEPbJL8t1Lm2dkljFx6xeBE26YWH3aIzkPKg==", "requires": { "prosemirror-state": "^1.0.0", "w3c-keyname": "^2.2.0" } }, "prosemirror-model": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.16.1.tgz", - "integrity": "sha512-r1/w0HDU40TtkXp0DyKBnFPYwd8FSlUSJmGCGFv4DeynfeSlyQF2FD0RQbVEMOe6P3PpUSXM6LZBV7W/YNZ4mA==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.1.tgz", + "integrity": "sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==", "requires": { - "orderedmap": "^1.1.0" + "orderedmap": "^2.0.0" } }, "prosemirror-schema-list": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.1.6.tgz", - "integrity": "sha512-aFGEdaCWmJzouZ8DwedmvSsL50JpRkqhQ6tcpThwJONVVmCgI36LJHtoQ4VGZbusMavaBhXXr33zyD2IVsTlkw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.2.0.tgz", + "integrity": "sha512-8PT/9xOx1HHdC7fDNNfhQ50Z8Mzu7nKyA1KCDltSpcZVZIbB0k7KtsHrnXyuIhbLlScoymBiLZ00c5MH6wdFsA==", "requires": { "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.0.0" } }, "prosemirror-state": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.3.4.tgz", - "integrity": "sha512-Xkkrpd1y/TQ6HKzN3agsQIGRcLckUMA9u3j207L04mt8ToRgpGeyhbVv0HI7omDORIBHjR29b7AwlATFFf2GLA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.1.tgz", + "integrity": "sha512-U/LBDW2gNmVa07sz/D229XigSdDQ5CLFwVB1Vb32MJbAHHhWe/6pOc721faI17tqw4pZ49i1xfY/jEZ9tbIhPg==", "requires": { "prosemirror-model": "^1.0.0", "prosemirror-transform": "^1.0.0" } }, "prosemirror-transform": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.4.2.tgz", - "integrity": "sha512-bcIsf3uRZhfab0xRfyyxOEh6eqSszq/hJbDbmUumFnbHBoWhB/uXbpz6vvUxfk0XiEvrZDJ+5pXRrNDc1Hu3vQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.6.0.tgz", + "integrity": "sha512-MAp7AjsjEGEqQY0sSMufNIUuEyB1ZR9Fqlm8dTwwWwpEJRv/plsKjWXBbx52q3Ml8MtaMcd7ic14zAHVB3WaMw==", "requires": { "prosemirror-model": "^1.0.0" } }, "prosemirror-view": { - "version": "1.23.13", - "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.23.13.tgz", - "integrity": "sha512-X/NcwZv8pgcEWfs3n++Wz4nDgqDIeDvJ9kfCk6DCoC9XUlDekqJLFt9wCcCUBXedb8hs/dmd+JmcaLgbr67XZw==", + "version": "1.26.2", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.26.2.tgz", + "integrity": "sha512-CGKw+GadkfSBEwRAJTHCEKJ4DlV6/3IhAdjpwGyZHUHtbP7jX4Ol4zmi7xa2c6GOabDlIJLYXJydoNYLX7lNeQ==", "requires": { "prosemirror-model": "^1.16.0", "prosemirror-state": "^1.0.0", @@ -25148,7 +25124,8 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true + "dev": true, + "peer": true }, "regexpu-core": { "version": "5.0.1", @@ -25322,11 +25299,6 @@ "glob": "^7.1.3" } }, - "rope-sequence": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.3.tgz", - "integrity": "sha512-85aZYCxweiD5J8yTEbw+E6A27zSnLPNDL0WfPdw3YYodq7WjnTKo0q4dtyQ2gz23iPT8Q9CUyJtAaUNcTxRf5Q==" - }, "run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -25900,6 +25872,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", "dev": true, + "peer": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -25911,6 +25884,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", "dev": true, + "peer": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -25943,7 +25917,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true + "dev": true, + "peer": true }, "stylus": { "version": "0.56.0", @@ -26695,6 +26670,7 @@ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, + "peer": true, "requires": { "@types/json5": "^0.0.29", "json5": "^1.0.1", @@ -26707,6 +26683,7 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, + "peer": true, "requires": { "minimist": "^1.2.0" } @@ -26715,7 +26692,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true + "dev": true, + "peer": true } } }, @@ -26729,6 +26707,7 @@ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, + "peer": true, "requires": { "tslib": "^1.8.1" }, @@ -26737,7 +26716,8 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "dev": true, + "peer": true } } }, @@ -26754,6 +26734,7 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "peer": true, "requires": { "prelude-ls": "^1.2.1" } @@ -26802,6 +26783,7 @@ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, + "peer": true, "requires": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", @@ -26907,7 +26889,8 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true + "dev": true, + "peer": true }, "validate-npm-package-name": { "version": "3.0.0", @@ -26931,9 +26914,9 @@ "dev": true }, "w3c-keyname": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.4.tgz", - "integrity": "sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw==" + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.5.tgz", + "integrity": "sha512-WJrK7i6w+ULuZsGscCezbCH4Aev5U3xY87vnSimzzEgPQhb0Sa0a1rE3c2jtEwrFtSfi61Jefw3jI5/DD/3jbQ==" }, "watchpack": { "version": "2.3.1", @@ -27239,6 +27222,7 @@ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, + "peer": true, "requires": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -27299,7 +27283,8 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true + "dev": true, + "peer": true }, "wrap-ansi": { "version": "2.1.0", diff --git a/package.json b/package.json index 70a51689f1d5b82a122144801c1983bad85e9169..96962030e556a468dd383938d442e638693616cd 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,8 @@ "start-player-local-network": "ng serve player --port 4202 --host 0.0.0.0", "build-editor": "ng build --project editor --output-hashing=none && scripts/build.sh editor $npm_package_config_editor_version", "build-player": "ng build --project player --output-hashing=none && scripts/build.sh player $npm_package_config_player_version", + "node-build-editor": "ng build --project editor --output-hashing=none && node scripts/wrap_and_pack.js editor $npm_package_config_editor_version projects/editor/src/html_wrapper/index.html", + "node-build-player": "ng build --project player --output-hashing=none && node scripts/wrap_and_pack.js player $npm_package_config_player_version projects/player/src/html_wrapper/index.html", "test": "ng test", "e2e": "node node_modules/testcafe/bin/testcafe.js 'chrome' e2e-tests/editor/test.ts" }, @@ -22,6 +24,9 @@ "parserOptions": { "project": "./tsconfig.json" }, + "rules": { + "class-methods-use-this": ["error", { "exceptMethods": ["getElementComponent", "getNewOptionLabel", "transform"] }] + }, "overrides": [ { "files": [ @@ -56,28 +61,31 @@ "@angular/router": "~13.3.0", "@ngx-translate/core": "^14.0.0", "@ngx-translate/http-loader": "^6.0.0", - "@tiptap/core": "^2.0.0-beta.175", - "@tiptap/extension-blockquote": "^2.0.0-beta.26", - "@tiptap/extension-bubble-menu": "^2.0.0-beta.56", - "@tiptap/extension-bullet-list": "^2.0.0-beta.26", - "@tiptap/extension-color": "^2.0.0-beta.9", - "@tiptap/extension-floating-menu": "^2.0.0-beta.51", - "@tiptap/extension-font-family": "^2.0.0-beta.21", - "@tiptap/extension-heading": "^2.0.0-beta.26", - "@tiptap/extension-highlight": "^2.0.0-beta.33", - "@tiptap/extension-image": "^2.0.0-beta.27", - "@tiptap/extension-list-item": "^2.0.0-beta.20", - "@tiptap/extension-ordered-list": "^2.0.0-beta.27", - "@tiptap/extension-paragraph": "^2.0.0-beta.23", - "@tiptap/extension-strike": "~2.0.0-beta.27", - "@tiptap/extension-subscript": "^2.0.0-beta.10", - "@tiptap/extension-superscript": "^2.0.0-beta.10", - "@tiptap/extension-text-align": "^2.0.0-beta.29", - "@tiptap/extension-text-style": "^2.0.0-beta.23", - "@tiptap/extension-underline": "^2.0.0-beta.23", - "@tiptap/starter-kit": "2.0.0-beta.184", - "ngx-tiptap": "^3.0.4", - "prosemirror-state": "^1.3.4", + "@tiptap/core": "^2.0.0-beta.182", + "@tiptap/extension-blockquote": "^2.0.0-beta.29", + "@tiptap/extension-bold": "^2.0.0-beta.28", + "@tiptap/extension-bubble-menu": "^2.0.0-beta.61", + "@tiptap/extension-bullet-list": "^2.0.0-beta.29", + "@tiptap/extension-color": "^2.0.0-beta.12", + "@tiptap/extension-document": "^2.0.0-beta.17", + "@tiptap/extension-floating-menu": "^2.0.0-beta.56", + "@tiptap/extension-font-family": "^2.0.0-beta.24", + "@tiptap/extension-heading": "^2.0.0-beta.29", + "@tiptap/extension-highlight": "^2.0.0-beta.35", + "@tiptap/extension-image": "^2.0.0-beta.30", + "@tiptap/extension-italic": "^2.0.0-beta.28", + "@tiptap/extension-list-item": "^2.0.0-beta.23", + "@tiptap/extension-ordered-list": "^2.0.0-beta.30", + "@tiptap/extension-paragraph": "^2.0.0-beta.26", + "@tiptap/extension-strike": "~2.0.0-beta.29", + "@tiptap/extension-subscript": "^2.0.0-beta.13", + "@tiptap/extension-superscript": "^2.0.0-beta.13", + "@tiptap/extension-text": "^2.0.0-beta.17", + "@tiptap/extension-text-align": "^2.0.0-beta.31", + "@tiptap/extension-text-style": "^2.0.0-beta.26", + "@tiptap/extension-underline": "^2.0.0-beta.25", + "ngx-tiptap": "^5.0.0", + "prosemirror-state": "^1.4.1", "rxjs": "^7.4.0", "testcafe": "^1.18.6", "tslib": "^2.1.0", @@ -87,7 +95,7 @@ "@angular-devkit/build-angular": "~13.3.0", "@angular/cli": "~13.3.0", "@angular/compiler-cli": "~13.3.0", - "@iqb/eslint-config": "^1.1.1", + "@iqb/eslint-config": "^2.1.1", "@types/jasmine": "~3.6.0", "@types/node": "^12.11.7", "iqb-dev-components": "^1.4.0", diff --git a/projects/common/assets/common-styles.css b/projects/common/assets/common-styles.css index f700034a0e7c6448fcc8b498249456b9acb0f5d0..9aac3c04ba2a00f1e5ee8dc2d82f3a4c41b63e04 100644 --- a/projects/common/assets/common-styles.css +++ b/projects/common/assets/common-styles.css @@ -54,3 +54,14 @@ blockquote p { .fixed-size-content { overflow: auto; } + +.snackbar-warning {border: 3px double #ff4d4d} +.snackbar-error {background-color: #ff4d4d} +.snackbar-success {background-color: green} + +.cdk-drag { + cursor: grab; +} +.cdk-drag-dragging { + cursor: grabbing; +} diff --git a/projects/common/components/button/button.component.ts b/projects/common/components/button/button.component.ts index 9e46f7717bacd3a1ef7a7b3d65218bbc8489002d..efb0e597e8cde230dd8a841d56a7ffed530f5d9d 100644 --- a/projects/common/components/button/button.component.ts +++ b/projects/common/components/button/button.component.ts @@ -24,6 +24,7 @@ import { ButtonElement } from 'common/models/elements/button/button'; action: elementModel.action, param: elementModel.actionParam }) : false"> +<!--TODO why prevent default?--> {{elementModel.label}} </a> <button *ngIf="!elementModel.imageSrc && !elementModel.asLink" mat-button diff --git a/projects/common/components/compound-elements/cloze/cloze-child-elements/drop-list-simple.component.ts b/projects/common/components/compound-elements/cloze/cloze-child-elements/drop-list-simple.component.ts index 71773d295bea7dedbf66142e2dbd9dae51733589..12351f167305ef302a9fdf8cf0d9c29eb034fc4f 100644 --- a/projects/common/components/compound-elements/cloze/cloze-child-elements/drop-list-simple.component.ts +++ b/projects/common/components/compound-elements/cloze/cloze-child-elements/drop-list-simple.component.ts @@ -73,6 +73,7 @@ import { DragNDropValueObject } from 'common/models/elements/element'; '.list {width: 100%; height: 100%; border-radius: 5px}', '.item {border-radius: 5px; padding: 0 5px; height: 100%; text-align: center;}', '.item:not(:last-child) {margin-bottom: 5px;}', + '.item:active {cursor: grabbing}', '.error-message {font-size: 75%; margin-top: 10px;}', '.cdk-drag-preview {padding: 8px 20px; border-radius: 10px}', '.drag-placeholder {background-color: lightgrey; border: dotted 3px #999;}', diff --git a/projects/common/components/compound-elements/cloze/cloze-child-elements/text-field-simple.component.ts b/projects/common/components/compound-elements/cloze/cloze-child-elements/text-field-simple.component.ts index 8783a49eb176425915eab58489440c4970b643b5..935c337b34a39386b03ed9c1c5041ea460ad38e4 100644 --- a/projects/common/components/compound-elements/cloze/cloze-child-elements/text-field-simple.component.ts +++ b/projects/common/components/compound-elements/cloze/cloze-child-elements/text-field-simple.component.ts @@ -32,7 +32,9 @@ import { `, styles: [ '.clozeChild {border: 1px solid rgba(0,0,0,.12); border-radius: 5px}', - 'input {width: calc(100% - 2px); height: calc(100% - 2px); vertical-align: top; padding: 0;}' + 'input {width: calc(100% - 2px); height: calc(100% - 2px); vertical-align: top; padding: 0;}', + 'input:hover {border: 1px solid currentColor;}', + 'input:focus {outline: 1px solid #3f51b5;}' ] }) export class TextFieldSimpleComponent extends FormElementComponent { diff --git a/projects/common/components/compound-elements/cloze/cloze-child-elements/toggle-button.component.ts b/projects/common/components/compound-elements/cloze/cloze-child-elements/toggle-button.component.ts index e5e64bcd1452e415b612567e44a49d376ce282f0..e77498d3cf68ef2ea4139b5fabb5f22c57a01deb 100644 --- a/projects/common/components/compound-elements/cloze/cloze-child-elements/toggle-button.component.ts +++ b/projects/common/components/compound-elements/cloze/cloze-child-elements/toggle-button.component.ts @@ -1,19 +1,19 @@ import { Component, Input } from '@angular/core'; -import { FormElementComponent } from '../../../../directives/form-element-component.directive'; +import { FormElementComponent } from 'common/directives/form-element-component.directive'; import { ToggleButtonElement } from 'common/models/elements/compound-elements/cloze/cloze-child-elements/toggle-button'; @Component({ selector: 'aspect-toggle-button', template: ` <mat-button-toggle-group [formControl]="elementFormControl" + [disabled]="elementModel.readOnly" [value]="elementModel.value" [vertical]="elementModel.verticalOrientation" [style.width]="elementModel.dynamicWidth ? 'unset' : '100%'"> <mat-button-toggle *ngFor="let option of elementModel.richTextOptions; let i = index" [value]="i" - [ngClass]="{ 'strike' : elementModel.strikeOtherOptions && - elementFormControl.value !== null && - elementFormControl.value !== i }" + [ngClass]="{ 'strike-other-options' : elementModel.strikeOtherOptions, + 'strike-selected-option' : elementModel.strikeSelectedOption }" [style.color]="elementModel.styling.fontColor" [style.font-size.px]="elementModel.styling.fontSize" [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" @@ -33,7 +33,10 @@ import { ToggleButtonElement } from 'common/models/elements/compound-elements/cl 'mat-button-toggle-group {display: inline-flex; min-width: 70px; min-height: 20px; max-width: 100%;}', 'mat-button-toggle-group {justify-content: center;}', ':host ::ng-deep .mat-button-toggle-label-content {line-height: unset}', - ':host ::ng-deep .strike .mat-button-toggle-label-content {text-decoration: line-through}', + ':host ::ng-deep .strike-selected-option.mat-button-toggle-checked .mat-button-toggle-label-content' + + '{text-decoration: line-through}', + ':host ::ng-deep .strike-other-options:not(.mat-button-toggle-checked) .mat-button-toggle-label-content' + + '{text-decoration: line-through}' ] }) export class ToggleButtonComponent extends FormElementComponent { diff --git a/projects/common/components/compound-elements/cloze/cloze.component.ts b/projects/common/components/compound-elements/cloze/cloze.component.ts index c556fb5d9d951d7ea23a277c810cee9c49a79cfa..9d7ebbde1917de31d61c48fc007737d2ef3ef277 100644 --- a/projects/common/components/compound-elements/cloze/cloze.component.ts +++ b/projects/common/components/compound-elements/cloze/cloze.component.ts @@ -11,9 +11,6 @@ import { ClozeElement } from 'common/models/elements/compound-elements/cloze/clo @Component({ selector: 'aspect-cloze', template: ` - <ng-container *ngIf="elementModel.document.content.length < 1"> - <i>Kein Dokument vorhanden</i> - </ng-container> <div [style.width.%]="100" [style.height]="'auto'" [style.column-count]="elementModel.columnCount"> diff --git a/projects/common/components/compound-elements/likert/likert-radio-button-group.component.ts b/projects/common/components/compound-elements/likert/likert-radio-button-group.component.ts index d010e3ef0b3c09fe48d24934f2ba8ae1fb0fb1f6..e802b20e639526d26a8395a8dff89ef553c8c43d 100644 --- a/projects/common/components/compound-elements/likert/likert-radio-button-group.component.ts +++ b/projects/common/components/compound-elements/likert/likert-radio-button-group.component.ts @@ -1,5 +1,5 @@ import { Component, Input } from '@angular/core'; -import { FormElementComponent } from '../../../directives/form-element-component.directive'; +import { FormElementComponent } from 'common/directives/form-element-component.directive'; import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row'; @Component({ @@ -17,15 +17,15 @@ import { LikertRowElement } from 'common/models/elements/compound-elements/liker [style.grid-row-end]="2" [style.place-self]="'start'" [style.align-items]="'center'" - [fxLayout]="elementModel.rowLabel.position === 'left' || - elementModel.rowLabel.position === 'right' ? 'row' : 'column'"> + [fxLayout]="elementModel.rowLabel.imgPosition === 'left' || + elementModel.rowLabel.imgPosition === 'right' ? 'row' : 'column'"> <img *ngIf="elementModel.rowLabel.imgSrc && - (elementModel.rowLabel.position === 'above' || elementModel.rowLabel.position === 'left')" + (elementModel.rowLabel.imgPosition === 'above' || elementModel.rowLabel.imgPosition === 'left')" [src]="elementModel.rowLabel.imgSrc | safeResourceUrl" alt="Image Placeholder" [style.object-fit]="'scale-down'" [style.max-width.%]="100"> - <ng-container>{{elementModel.rowLabel.text}}</ng-container> + <div [innerHTML]="elementModel.rowLabel.text"></div> <img *ngIf="elementModel.rowLabel.imgSrc && - (elementModel.rowLabel.position === 'below' || elementModel.rowLabel.position === 'right')" + (elementModel.rowLabel.imgPosition === 'below' || elementModel.rowLabel.imgPosition === 'right')" [src]="elementModel.rowLabel.imgSrc | safeResourceUrl" alt="Image Placeholder" [style.object-fit]="'scale-down'" [style.max-width.%]="100"> </div> diff --git a/projects/common/components/compound-elements/likert/likert.component.ts b/projects/common/components/compound-elements/likert/likert.component.ts index baeac85d2e8ca395630fc4e33683f3751c117b11..b82960fdc485e1e308a6e741619c2cac4651e4f3 100644 --- a/projects/common/components/compound-elements/likert/likert.component.ts +++ b/projects/common/components/compound-elements/likert/likert.component.ts @@ -1,24 +1,33 @@ import { Component, Input, QueryList, ViewChildren } from '@angular/core'; -import { LikertRadioButtonGroupComponent } from './likert-radio-button-group.component'; -import { CompoundElementComponent } from '../../../directives/compound-element.directive'; -import { ElementComponent } from '../../../directives/element-component.directive'; +import { CompoundElementComponent } from 'common/directives/compound-element.directive'; +import { ElementComponent } from 'common/directives/element-component.directive'; import { LikertElement } from 'common/models/elements/compound-elements/likert/likert'; -import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row'; +import { LikertRadioButtonGroupComponent } from './likert-radio-button-group.component'; @Component({ selector: 'aspect-likert', template: ` - <div *ngIf="elementModel.rows.length === 0 && elementModel.columns.length === 0"> + <div *ngIf="elementModel.rows.length === 0 && elementModel.options.length === 0"> Keine Zeilen oder Spalten vorhanden </div> <div [style.width.%]="100" [style.height.%]="100"> + <div class="label" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.line-height.%]="elementModel.styling.lineHeight" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> + {{elementModel.label}} + </div> <div class="mat-typography" [style.display]="'grid'" [style.grid-template-columns]="elementModel.firstColumnSizeRatio + 'fr ' + - '1fr '.repeat(elementModel.columns.length)" + '1fr '.repeat(elementModel.options.length)" [style.background-color]="elementModel.styling.backgroundColor" [style.color]="elementModel.styling.fontColor" [style.font-family]="elementModel.styling.font" @@ -27,17 +36,24 @@ import { LikertRowElement } from 'common/models/elements/compound-elements/liker [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" [style.font-style]="elementModel.styling.italic ? 'italic' : ''" [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> - <div *ngFor="let column of elementModel.columns; let i = index" + <div *ngIf="elementModel.options.length > 0" + [style.grid-column-start]="1" + [style.grid-column-end]="1" + [style.grid-row-start]="1" + [style.grid-row-end]="1"> + {{elementModel.label2}} + </div> + <div *ngFor="let column of elementModel.options; let i = index" class="columns" fxLayout="column" fxLayoutAlign="end center" [style.grid-column-start]="2 + i" [style.grid-column-end]="3 + i" [style.grid-row-start]="1" [style.grid-row-end]="2"> - <img *ngIf="column.imgSrc && column.position === 'above'" + <img *ngIf="column.imgSrc && column.imgPosition === 'above'" [src]="column.imgSrc | safeResourceUrl" alt="Image Placeholder" [style.object-fit]="'scale-down'"> <div [innerHTML]="sanitizer.bypassSecurityTrustHtml(column.text)"></div> - <img *ngIf="column.imgSrc && column.position === 'below'" + <img *ngIf="column.imgSrc && column.imgPosition === 'below'" [src]="column.imgSrc | safeResourceUrl" alt="Image Placeholder" [style.object-fit]="'scale-down'"> </div> @@ -47,7 +63,7 @@ import { LikertRowElement } from 'common/models/elements/compound-elements/liker [style.background-color]="elementModel.styling.lineColoring && i % 2 === 0 ? elementModel.styling.lineColoringColor : ''" [style.grid-column-start]="1" - [style.grid-column-end]="elementModel.columns.length + 2" + [style.grid-column-end]="elementModel.options.length + 2" [style.grid-row-start]="2 + i" [style.grid-row-end]="3 + i" [style.padding.px]="3" @@ -62,7 +78,8 @@ import { LikertRowElement } from 'common/models/elements/compound-elements/liker styles: [ 'img {object-fit: contain; max-height: 100%; max-width: 100%; margin: 10px}', '.columns {text-align: center;}', - ':host ::ng-deep mat-radio-button span.mat-radio-container {left: calc(50% - 10px)}' + ':host ::ng-deep mat-radio-button span.mat-radio-container {left: calc(50% - 10px)}', + '.label {margin-bottom: 10px;}' ] }) export class LikertComponent extends CompoundElementComponent { diff --git a/projects/common/components/frame/frame.component.ts b/projects/common/components/frame/frame.component.ts index c731c1352cd920551f74672e49a1bc4bdfd7f663..a8317ef778161f03e59103800de2a0637636e541 100644 --- a/projects/common/components/frame/frame.component.ts +++ b/projects/common/components/frame/frame.component.ts @@ -1,17 +1,20 @@ import { Component, Input } from '@angular/core'; -import { ElementComponent } from '../../directives/element-component.directive'; import { FrameElement } from 'common/models/elements/frame/frame'; +import { ElementComponent } from '../../directives/element-component.directive'; @Component({ selector: 'aspect-frame', template: ` <div [style.width]="'calc(100% - ' + (elementModel.styling.borderWidth * 2) + 'px)'" [style.height]="'calc(100% - ' + (elementModel.styling.borderWidth * 2) + 'px)'" - [style.border-style]="elementModel.styling.borderStyle" [style.border-width.px]="elementModel.styling.borderWidth" [style.border-color]="elementModel.styling.borderColor" [style.border-radius.px]="elementModel.styling.borderRadius" - [style.background-color]="elementModel.styling.backgroundColor"> + [style.background-color]="elementModel.styling.backgroundColor" + [style.border-top-style]="elementModel.hasBorderTop ? elementModel.styling.borderStyle : 'none'" + [style.border-bottom-style]="elementModel.hasBorderBottom ? elementModel.styling.borderStyle : 'none'" + [style.border-left-style]="elementModel.hasBorderLeft ? elementModel.styling.borderStyle : 'none'" + [style.border-right-style]="elementModel.hasBorderRight ? elementModel.styling.borderStyle : 'none'"> </div> ` }) diff --git a/projects/common/components/input-elements/checkbox.component.ts b/projects/common/components/input-elements/checkbox.component.ts index 252fc3c761049a97cac80e22e371d420f0a06bce..68b20ba0fdc47192ca0925d76b969318bf2b70ee 100644 --- a/projects/common/components/input-elements/checkbox.component.ts +++ b/projects/common/components/input-elements/checkbox.component.ts @@ -1,6 +1,6 @@ import { Component, Input } from '@angular/core'; -import { FormElementComponent } from '../../directives/form-element-component.directive'; import { CheckboxElement } from 'common/models/elements/input-elements/checkbox'; +import { FormElementComponent } from '../../directives/form-element-component.directive'; @Component({ selector: 'aspect-checkbox', @@ -18,7 +18,7 @@ import { CheckboxElement } from 'common/models/elements/input-elements/checkbox' [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" [style.font-style]="elementModel.styling.italic ? 'italic' : ''" [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''" - (click)="elementModel.readOnly ? $event.preventDefault() : null"> + (click)="elementModel.readOnly && $event.preventDefault()"> <div [innerHTML]="elementModel.label"></div> </mat-checkbox> <mat-error *ngIf="elementFormControl.errors && elementFormControl.touched" diff --git a/projects/common/components/input-elements/drop-list.component.ts b/projects/common/components/input-elements/drop-list.component.ts index 99a012af1bd983764478a32e2c3bccab41b4a115..4cb89095331dbb60b064cf78f38a09f49104885c 100644 --- a/projects/common/components/input-elements/drop-list.component.ts +++ b/projects/common/components/input-elements/drop-list.component.ts @@ -3,9 +3,9 @@ import { CdkDragDrop } from '@angular/cdk/drag-drop/drag-events'; import { CdkDrag, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop'; -import { FormElementComponent } from '../../directives/form-element-component.directive'; import { DropListElement } from 'common/models/elements/input-elements/drop-list'; import { DragNDropValueObject } from 'common/models/elements/element'; +import { FormElementComponent } from '../../directives/form-element-component.directive'; @Component({ selector: 'aspect-drop-list', @@ -54,7 +54,7 @@ import { DragNDropValueObject } from 'common/models/elements/element'; </ng-container> <!--Leave template within the dom to ensure dragNdrop--> <ng-template #dropObject let-dropListValueElement let-index="index"> - <div class="item text-item" *ngIf="!dropListValueElement.imgSrcValue" + <div class="item text-item" *ngIf="!dropListValueElement.imgSrc" [ngClass]="{ 'vertical-orientation' : elementModel.orientation === 'vertical', 'horizontal-orientation' : elementModel.orientation === 'horizontal'}" [style.background-color]="elementModel.styling.itemBackgroundColor" @@ -64,12 +64,12 @@ import { DragNDropValueObject } from 'common/models/elements/element'; <div *cdkDragPreview [style.font-size.px]="elementModel.styling.fontSize" [style.background-color]="elementModel.styling.itemBackgroundColor"> - {{dropListValueElement.stringValue}} + {{dropListValueElement.text}} </div> <div class="drag-placeholder" *cdkDragPlaceholder [style.min-height.px]="elementModel.styling.fontSize"> </div> - {{dropListValueElement.stringValue}} + {{dropListValueElement.text}} </div> <!-- actual placeholder when item is being dragged from copy-list --> @@ -78,11 +78,11 @@ import { DragNDropValueObject } from 'common/models/elements/element'; [ngClass]="{ 'vertical-orientation' : elementModel.orientation === 'vertical', 'horizontal-orientation' : elementModel.orientation === 'horizontal'}" [style.background-color]="elementModel.styling.itemBackgroundColor"> - {{dropListValueElement.stringValue}} + {{dropListValueElement.text}} </div> - <img *ngIf="dropListValueElement.imgSrcValue" - [src]="dropListValueElement.imgSrcValue | safeResourceUrl" alt="Image Placeholder" + <img *ngIf="dropListValueElement.imgSrc" + [src]="dropListValueElement.imgSrc | safeResourceUrl" alt="Image Placeholder" [style.display]="elementModel.orientation === 'flex' ? '' : 'block'" class="item" [ngClass]="{ 'vertical-orientation' : elementModel.orientation === 'vertical', @@ -90,8 +90,8 @@ import { DragNDropValueObject } from 'common/models/elements/element'; cdkDrag [cdkDragData]="{ element: dropListValueElement, index: index }" (cdkDragStarted)=dragStart(index) (cdkDragEnded)="dragEnd()" [style.object-fit]="'scale-down'"> - <img *ngIf="elementModel.copyOnDrop && draggedItemIndex === index && dropListValueElement.imgSrcValue" - [src]="dropListValueElement.imgSrcValue | safeResourceUrl" alt="Image Placeholder" + <img *ngIf="elementModel.copyOnDrop && draggedItemIndex === index && dropListValueElement.imgSrc" + [src]="dropListValueElement.imgSrc | safeResourceUrl" alt="Image Placeholder" [style.display]="elementModel.orientation === 'flex' ? '' : 'block'" class="item" [ngClass]="{ 'vertical-orientation' : elementModel.orientation === 'vertical', @@ -148,7 +148,7 @@ export class DropListComponent extends FormElementComponent { drop(event: CdkDragDrop<DropListComponent>): void { if (event.previousContainer === event.container && !event.container.data.elementModel.copyOnDrop) { moveItemInArray(event.container.data.elementFormControl.value as unknown as DragNDropValueObject[], - event.previousIndex, event.currentIndex); + event.previousIndex, event.currentIndex); this.elementFormControl.setValue( (event.container.data.elementFormControl.value as DragNDropValueObject[]) ); diff --git a/projects/common/components/input-elements/radio-button-group.component.ts b/projects/common/components/input-elements/radio-button-group.component.ts index 2f68f7b23b6af05d937d23dd296d1655e62b43f8..542fbad7d9c7bf78d56886da2430908e55e584f8 100644 --- a/projects/common/components/input-elements/radio-button-group.component.ts +++ b/projects/common/components/input-elements/radio-button-group.component.ts @@ -1,6 +1,6 @@ import { Component, Input } from '@angular/core'; -import { FormElementComponent } from '../../directives/form-element-component.directive'; import { RadioButtonGroupElement } from 'common/models/elements/input-elements/radio-button-group'; +import { FormElementComponent } from '../../directives/form-element-component.directive'; @Component({ selector: 'aspect-radio-button-group', @@ -23,14 +23,14 @@ import { RadioButtonGroupElement } from 'common/models/elements/input-elements/r [formControl]="elementFormControl" [value]="elementModel.value" [style.margin-top.px]="elementModel.label !== '' ? 10 : 0"> - <mat-radio-button *ngFor="let option of elementModel.richTextOptions; let i = index" + <mat-radio-button *ngFor="let option of elementModel.options; let i = index" [ngClass]="{ 'strike' : elementModel.strikeOtherOptions && elementFormControl.value !== null && elementFormControl.value !== i }" [value]="i" [style.pointer-events]="elementModel.readOnly ? 'none' : 'unset'" [style.line-height.%]="elementModel.styling.lineHeight"> - <div class="radio-button-label" [innerHTML]="sanitizer.bypassSecurityTrustHtml(option)"></div> + <div class="radio-button-label" [innerHTML]="sanitizer.bypassSecurityTrustHtml(option.text)"></div> </mat-radio-button> <mat-error *ngIf="elementFormControl.errors && elementFormControl.touched" class="error-message"> diff --git a/projects/common/components/input-elements/radio-group-images.component.ts b/projects/common/components/input-elements/radio-group-images.component.ts index 73aedcc45ef36f405e4f82e2cc4ab08cd05666c6..161d30f69345df887fe1d9372698eade8c23e0ac 100644 --- a/projects/common/components/input-elements/radio-group-images.component.ts +++ b/projects/common/components/input-elements/radio-group-images.component.ts @@ -1,77 +1,53 @@ import { Component, Input } from '@angular/core'; -import { FormElementComponent } from '../../directives/form-element-component.directive'; import { RadioButtonGroupComplexElement } from 'common/models/elements/input-elements/radio-button-group-complex'; +import { FormElementComponent } from '../../directives/form-element-component.directive'; @Component({ selector: 'aspect-radio-group-images', template: ` - <div [style.width.%]="100" - [style.height.%]="100" - [style.display]="'grid !important'" - [style.grid-template-columns]="'1fr '.repeat(elementModel.columns.length)" - [style.background-color]="elementModel.styling.backgroundColor" - [style.color]="elementModel.styling.fontColor" - [style.font-family]="elementModel.styling.font" - [style.font-size.px]="elementModel.styling.fontSize" - [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" - [style.font-style]="elementModel.styling.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> - <label id="radio-group-label" class="label" - [style.grid-column-start]="1" - [style.grid-column-end]="2 + elementModel.columns.length" - [style.grid-row-start]="1" - [style.grid-row-end]="2" - [innerHTML]="elementModel.label"> + <label id="radio-group-label" + [innerHTML]="elementModel.label"> </label> - <div *ngFor="let option of elementModel.columns; let i = index" - class="columns" fxLayout="column" fxLayoutAlign="center center" - [style.grid-column-start]="1 + i" - [style.grid-column-end]="2 + i" - [style.grid-row-start]="2" - [style.grid-row-end]="3" - (click)="selectOption(i)"> - <img *ngIf="option.imgSrc && option.position === 'above'" - [style.object-fit]="'scale-down'" - [style.max-width.%]="100" - [src]="option.imgSrc | safeResourceUrl" alt="Image Placeholder"> - <div [innerHTML]="sanitizer.bypassSecurityTrustHtml(option.text)"></div> - <img *ngIf="option.imgSrc && option.position === 'below'" - [style.object-fit]="'scale-down'" - [style.max-width.%]="100" - [src]="option.imgSrc | safeResourceUrl" alt="Image Placeholder"> - </div> <mat-radio-group aria-labelledby="radio-group-label" + [style.grid-template-columns]="elementModel.itemsPerRow !== null ? + 'repeat(' + elementModel.itemsPerRow + ', 1fr)' : + 'repeat(' + elementModel.options.length + ', 1fr)'" [formControl]="elementFormControl" - [style.display]="'grid'" - [style.grid-template-columns]="'1fr '.repeat(elementModel.columns.length)" - [style.grid-column-start]="1" - [style.grid-column-end]="2 + elementModel.columns.length" - [style.grid-row-start]="3" - [style.grid-row-end]="4" [value]="elementModel.value"> - <mat-radio-button *ngFor="let option of elementModel.columns; let i = index" - aria-labelledby="radio-group-label" + <mat-radio-button *ngFor="let option of elementModel.options; let i = index" [style.pointer-events]="elementModel.readOnly ? 'none' : 'unset'" - [value]="i" - [style.grid-column-start]="1 + i" - [style.grid-column-end]="2 + i" - [style.grid-row-start]="1" - [style.grid-row-end]="2"> + fxLayout="column" fxLayoutAlign="end center" + [value]="i"> + <img *ngIf="option.imgSrc && (option.imgPosition === 'above' || option.imgPosition === 'left')" + [style.object-fit]="'scale-down'" + [style.max-width.%]="100" + [src]="option.imgSrc | safeResourceUrl" alt="Image Placeholder"> + <div [innerHTML]="sanitizer.bypassSecurityTrustHtml(option.text)" + [style.background-color]="elementModel.styling.backgroundColor" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"></div> + <img *ngIf="option.imgSrc && (option.imgPosition === 'below' || option.imgPosition === 'right')" + [style.object-fit]="'scale-down'" + [style.max-width.%]="100" + [src]="option.imgSrc | safeResourceUrl" alt="Image Placeholder"> </mat-radio-button> </mat-radio-group> <mat-error *ngIf="elementFormControl.errors && elementFormControl.touched" class="error-message"> {{elementFormControl.errors | errorTransform: elementModel}} </mat-error> - </div> `, styles: [ - '.columns {text-align: center; margin: 0 5px;}', - '.grid-layout .columns img {cursor: pointer;}', - ':host ::ng-deep mat-radio-button span.mat-radio-container {left: calc(50% - 10px)}', - 'mat-radio-group {margin-top: 10px}', - '.error-message { font-size: 75% }', - '.grid-layout mat-radio-button {margin-top: 15px}' + 'mat-radio-group {display: grid}', + ':host ::ng-deep .mat-radio-label {flex-direction: column-reverse;}', + ':host ::ng-deep .mat-radio-label .mat-radio-container {margin-top: 15px; margin-left: 10px;}', + ':host ::ng-deep .mat-radio-label .mat-radio-label-content {text-align: center;}', + 'mat-radio-button {margin-bottom: 60px;}', + '.error-message { font-size: 75% }' ] }) export class RadioGroupImagesComponent extends FormElementComponent { diff --git a/projects/common/components/input-elements/slider.component.ts b/projects/common/components/input-elements/slider.component.ts index a5e921e8db083a3828e622edbb15f9e7e9ea9c25..5d5fac6c4b95049a38d06f98dfd64865d33869e8 100644 --- a/projects/common/components/input-elements/slider.component.ts +++ b/projects/common/components/input-elements/slider.component.ts @@ -1,9 +1,6 @@ -import { - Component, Input, OnInit, ViewChild -} from '@angular/core'; -import { MatSlider } from '@angular/material/slider'; -import { FormElementComponent } from '../../directives/form-element-component.directive'; +import { Component, Input } from '@angular/core'; import { SliderElement } from 'common/models/elements/input-elements/slider'; +import { FormElementComponent } from '../../directives/form-element-component.directive'; @Component({ selector: 'aspect-slider', @@ -76,6 +73,7 @@ import { SliderElement } from 'common/models/elements/input-elements/slider'; [style.margin-left.px]="elementModel.barStyle ? valueMin.offsetWidth/2 - 8: valueMin.offsetWidth" [style.margin-top.px]="elementModel.barStyle ? -32 : -valueContainer.offsetHeight"> <mat-slider [class]="elementModel.barStyle ? 'bar-style' : ''" + [disabled]="elementModel.readOnly" [thumbLabel]="elementModel.thumbLabel" [formControl]="elementFormControl" [style.width.%]="100" @@ -95,23 +93,13 @@ import { SliderElement } from 'common/models/elements/input-elements/slider'; '.value-container-min {min-width: 8px}', '.arrow-line {height: 2px; width: 100%; background-color: #555;}', '.number-marker {width: 2px; height: 20px; background-color: #555; margin: 10px auto 0 auto}', - // eslint-disable-next-line max-len '.arrow-head {width: 0; height: 0; border-top: 8px solid transparent; border-bottom: 8px solid transparent; border-left: 20px solid #555;}', // Background color must use !important to be displayed also in the editor - // eslint-disable-next-line max-len ':host ::ng-deep .bar-style .mat-slider-thumb {border-radius: 0; border: none; width: 9px; height: 40px; bottom: -20px; margin-right: 5px; background-color: #006064 !important}', ':host ::ng-deep .bar-style .mat-slider-track-fill {background-color: rgba(0,0,0,0);}', ':host ::ng-deep .bar-style .mat-slider-track-background {background-color: rgba(0,0,0,0);}' ] }) -export class SliderComponent extends FormElementComponent implements OnInit { - @ViewChild(MatSlider) inputElement!: MatSlider; +export class SliderComponent extends FormElementComponent { @Input() elementModel!: SliderElement; - - ngOnInit(): void { - super.ngOnInit(); - if (this.inputElement) { - this.inputElement.disabled = this.elementModel.readOnly as boolean; - } - } } diff --git a/projects/common/components/input-elements/text-area.component.ts b/projects/common/components/input-elements/text-area.component.ts index 257ae1c6655fdb113f2afba04f83ac5b6c0a0328..05670801d691c783b9c54547ccc9113ea8e14368 100644 --- a/projects/common/components/input-elements/text-area.component.ts +++ b/projects/common/components/input-elements/text-area.component.ts @@ -1,8 +1,8 @@ import { Component, Output, EventEmitter, Input } from '@angular/core'; -import { FormElementComponent } from '../../directives/form-element-component.directive'; import { TextAreaElement } from 'common/models/elements/input-elements/text-area'; +import { FormElementComponent } from '../../directives/form-element-component.directive'; @Component({ selector: 'aspect-text-area', diff --git a/projects/common/components/media-elements/media-player-control-bar/media-player-control-bar.component.ts b/projects/common/components/media-elements/media-player-control-bar/media-player-control-bar.component.ts index 6796b9e14d3db07749f6b6edace8424fb6887dae..196b04f2069b6edc9bc96636c26d2dcd818e0111 100644 --- a/projects/common/components/media-elements/media-player-control-bar/media-player-control-bar.component.ts +++ b/projects/common/components/media-elements/media-player-control-bar/media-player-control-bar.component.ts @@ -3,6 +3,10 @@ import { } from '@angular/core'; import { MatSliderChange } from '@angular/material/slider'; import { PlayerProperties, ValueChangeElement } from 'common/models/elements/element'; +import { + fromEvent, Subject, tap, throttleTime +} from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'aspect-media-player-control-bar', @@ -34,16 +38,23 @@ export class MediaPlayerControlBarComponent implements OnInit, OnChanges, OnDest playbackTime: number = 0; valid: boolean = false; + private ngUnsubscribe = new Subject<void>(); + ngOnInit(): void { this.playbackTime = this.savedPlaybackTime || this.playerProperties.playbackTime; this.started = this.playbackTime > 0; this.runCounter = Math.floor(this.playbackTime); this.player.ondurationchange = () => this.initTimeValues(); - this.player.ontimeupdate = () => { - this.currentTime = this.player.currentTime / 60; - this.currentRestTime = this.player.duration ? (this.player.duration - this.player.currentTime) / 60 : 0; - this.sendPlaybackTimeChanged(); - }; + fromEvent(this.player, 'timeupdate') + .pipe( + takeUntil(this.ngUnsubscribe), + tap(() => { + this.currentTime = this.player.currentTime / 60; + this.currentRestTime = this.player.duration ? (this.player.duration - this.player.currentTime) / 60 : 0; + }), + throttleTime(5000) + ) + .subscribe(() => this.sendPlaybackTimeChanged()); this.player.onpause = () => { this.playing = false; this.pausing = true; @@ -186,5 +197,7 @@ export class MediaPlayerControlBarComponent implements OnInit, OnChanges, OnDest ngOnDestroy(): void { this.player.pause(); + this.ngUnsubscribe.next(); + this.ngUnsubscribe.complete(); } } diff --git a/projects/common/directives/media-player-element-component.directive.ts b/projects/common/directives/media-player-element-component.directive.ts index de525feb42cbab1cd0df6b466e762da5a3602e39..812567cdacc216ac27978cf81e878165ba98eee4 100644 --- a/projects/common/directives/media-player-element-component.directive.ts +++ b/projects/common/directives/media-player-element-component.directive.ts @@ -3,10 +3,10 @@ import { } from '@angular/core'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; -import { ElementComponent } from './element-component.directive'; import { AudioElement } from 'common/models/elements/media-elements/audio'; import { VideoElement } from 'common/models/elements/media-elements/video'; import { ValueChangeElement } from 'common/models/elements/element'; +import { ElementComponent } from './element-component.directive'; @Directive() export abstract class MediaPlayerElementComponent extends ElementComponent implements OnInit, OnDestroy { diff --git a/projects/common/models/elements/button/button.ts b/projects/common/models/elements/button/button.ts index a97786f59a6f6764e8891a7eaa7e5b59517293ff..4b44a4cc2c79e14acb660667ef62c04f9c781592 100644 --- a/projects/common/models/elements/button/button.ts +++ b/projects/common/models/elements/button/button.ts @@ -1,6 +1,8 @@ import { Type } from '@angular/core'; import { ElementFactory } from 'common/util/element.factory'; -import { BasicStyles, PositionedUIElement, PositionProperties, UIElement } from 'common/models/elements/element'; +import { + BasicStyles, PositionedUIElement, PositionProperties, UIElement +} from 'common/models/elements/element'; import { ButtonComponent } from 'common/components/button/button.component'; import { ElementComponent } from 'common/directives/element-component.directive'; @@ -28,7 +30,7 @@ export class ButtonElement extends UIElement implements PositionedUIElement { }; } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return ButtonComponent; } } diff --git a/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/drop-list-simple.ts b/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/drop-list-simple.ts index cc50c783c1e7f993fcc8dbe0b36ef5a7b52217da..dc42abf27ffec836172725387e9b94200d0d29b4 100644 --- a/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/drop-list-simple.ts +++ b/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/drop-list-simple.ts @@ -1,9 +1,6 @@ import { ElementFactory } from 'common/util/element.factory'; import { - BasicStyles, - DragNDropValueObject, - InputElement, - AnswerScheme, + BasicStyles, DragNDropValueObject, InputElement, UIElementValue, AnswerScheme, AnswerSchemeValue } from 'common/models/elements/element'; import { Type } from '@angular/core'; @@ -11,9 +8,11 @@ import { ElementComponent } from 'common/directives/element-component.directive' import { DropListSimpleComponent } from 'common/components/compound-elements/cloze/cloze-child-elements/drop-list-simple.component'; +import { IDManager } from 'common/util/id-manager'; import { DropListElement } from 'common/models/elements/input-elements/drop-list'; export class DropListSimpleElement extends InputElement { + value: DragNDropValueObject[] = []; connectedTo: string[] = []; copyOnDrop: boolean = false; highlightReceivingDropList: boolean = false; @@ -24,16 +23,21 @@ export class DropListSimpleElement extends InputElement { constructor(element: Partial<DropListSimpleElement>, ...args: unknown[]) { super({ width: 150, height: 30, ...element }, ...args); - this.value = element.value || []; + if (element.value) { + this.value = DropListSimpleElement.checkAndRepairValueIDs(element.value); + } if (element.connectedTo) this.connectedTo = element.connectedTo; if (element.copyOnDrop) this.copyOnDrop = element.copyOnDrop; if (element.highlightReceivingDropList) this.highlightReceivingDropList = element.highlightReceivingDropList; - if (element.highlightReceivingDropListColor) this.highlightReceivingDropListColor = element.highlightReceivingDropListColor; + if (element.highlightReceivingDropListColor) { + this.highlightReceivingDropListColor = element.highlightReceivingDropListColor; + } this.styling = { ...ElementFactory.initStylingProps({ backgroundColor: '#f4f4f2', itemBackgroundColor: '#c9e0e0', - ...element.styling }) + ...element.styling + }) }; } @@ -61,7 +65,26 @@ export class DropListSimpleElement extends InputElement { .map(option => ({ value: option.id, label: option.stringValue as string })); // TODO: imageValueSrc } - getComponentFactory(): Type<ElementComponent> { + setProperty(property: string, value: UIElementValue) { + if (property === 'value') { + this.value = DropListSimpleElement.checkAndRepairValueIDs(value as DragNDropValueObject[]); + } else { + super.setProperty(property, value); + } + } + + private static checkAndRepairValueIDs(valueList: DragNDropValueObject[]): DragNDropValueObject[] { + valueList.forEach(valueObject => { + if (IDManager.getInstance().isIdAvailable(valueObject.id)) { + IDManager.getInstance().addID(valueObject.id); + } else { + valueObject.id = IDManager.getInstance().getNewID('value'); + } + }); + return valueList; + } + + getElementComponent(): Type<ElementComponent> { return DropListSimpleComponent; } } diff --git a/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/text-field-simple.ts b/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/text-field-simple.ts index 4abc32a4b8209742822bcf76c4826fdc70801a0e..cdce6dd3a0a9e9b9b6668ef83c0aecdd670ffcae 100644 --- a/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/text-field-simple.ts +++ b/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/text-field-simple.ts @@ -62,7 +62,7 @@ export class TextFieldSimpleElement extends InputElement { }; } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return TextFieldSimpleComponent; } } diff --git a/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/toggle-button.ts b/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/toggle-button.ts index 08c9889b8d19c48ec3f7022f26d5b00aa296e78e..185b4b2e2d9dc01187229f6084ae72e114ba6937 100644 --- a/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/toggle-button.ts +++ b/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/toggle-button.ts @@ -9,6 +9,7 @@ import { export class ToggleButtonElement extends InputElement { richTextOptions: string[] = []; strikeOtherOptions: boolean = false; + strikeSelectedOption: boolean = false; verticalOrientation: boolean = false; dynamicWidth: boolean = true; styling: BasicStyles & { @@ -20,6 +21,7 @@ export class ToggleButtonElement extends InputElement { super({ height: 25, ...element }, ...args); if (element.richTextOptions) this.richTextOptions = element.richTextOptions; if (element.strikeOtherOptions) this.strikeOtherOptions = element.strikeOtherOptions; + if (element.strikeSelectedOption) this.strikeSelectedOption = element.strikeSelectedOption; if (element.verticalOrientation) this.verticalOrientation = element.verticalOrientation; if (element.dynamicWidth !== undefined) this.dynamicWidth = element.dynamicWidth; this.styling = { @@ -53,7 +55,7 @@ export class ToggleButtonElement extends InputElement { .map((option, index) => ({ value: (index + 1).toString(), label: option })); } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return ToggleButtonComponent; } } diff --git a/projects/common/models/elements/compound-elements/cloze/cloze.ts b/projects/common/models/elements/compound-elements/cloze/cloze.ts index a90b6dc5b5b6f3146208bbea07b4230f32131655..882ef90946bd1c9d98b53f64c73f5a0757fb0c0a 100644 --- a/projects/common/models/elements/compound-elements/cloze/cloze.ts +++ b/projects/common/models/elements/compound-elements/cloze/cloze.ts @@ -40,7 +40,6 @@ export class ClozeElement extends CompoundElement implements PositionedUIElement setProperty(property: string, value: UIElementValue): void { if (property === 'document') { this.document = value as ClozeDocument; - this.document.content.forEach((node: any) => { if (node.type === 'paragraph' || node.type === 'heading') { ClozeElement.createSubNodeElements(node); @@ -56,7 +55,6 @@ export class ClozeElement extends CompoundElement implements PositionedUIElement }); } }); - } else { super.setProperty(property, value); } @@ -76,10 +74,11 @@ export class ClozeElement extends CompoundElement implements PositionedUIElement private initDocument(element: Partial<ClozeElement>, idManager?: IDManager): ClozeDocument { return { ...element.document, + type: 'doc', content: element.document?.content ? element.document.content .map((paragraph: ClozeDocumentParagraph) => ({ ...paragraph, - content: paragraph.content + content: paragraph.content ? paragraph.content .map((paraPart: ClozeDocumentParagraphPart) => ( ['TextField', 'DropList', 'ToggleButton'].includes(paraPart.type) ? { @@ -92,12 +91,27 @@ export class ClozeElement extends CompoundElement implements PositionedUIElement { ...paraPart } - )) - })) : [] + )) : undefined + })) : [{ + type: 'paragraph', + attrs: { + textAlign: 'left', + indent: null, + indentSize: 20, + hangingIndent: false, + margin: 0 + }, + content: [ + { + text: 'Lorem Ipsum', + type: 'text' + } + ] + }] } as ClozeDocument; } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return ClozeComponent; } diff --git a/projects/common/models/elements/compound-elements/likert/likert-row.ts b/projects/common/models/elements/compound-elements/likert/likert-row.ts index 09009d38c7fa6a5dab9bdf18738308e3eb0942b6..32d132be77b79101ab42c904d549efd4473b6c7e 100644 --- a/projects/common/models/elements/compound-elements/likert/likert-row.ts +++ b/projects/common/models/elements/compound-elements/likert/likert-row.ts @@ -6,14 +6,14 @@ import { } from 'common/components/compound-elements/likert/likert-radio-button-group.component'; export class LikertRowElement extends InputElement { - rowLabel: TextImageLabel = { text: '', imgSrc: null, position: 'above' }; + rowLabel: TextImageLabel = { text: '', imgSrc: null, imgPosition: 'above' }; columnCount: number = 0; firstColumnSizeRatio: number = 5; verticalButtonAlignment: 'auto' | 'center' = 'center'; constructor(element: Partial<LikertRowElement>, ...args: unknown[]) { super(element, ...args); - if (element.rowLabel) this.rowLabel = element.rowLabel; + if (element.rowLabel) this.rowLabel = { ...element.rowLabel }; if (element.columnCount) this.columnCount = element.columnCount; if (element.firstColumnSizeRatio) this.firstColumnSizeRatio = element.firstColumnSizeRatio; if (element.verticalButtonAlignment) this.verticalButtonAlignment = element.verticalButtonAlignment; @@ -43,7 +43,7 @@ export class LikertRowElement extends InputElement { ]; } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return LikertRadioButtonGroupComponent; } } diff --git a/projects/common/models/elements/compound-elements/likert/likert.ts b/projects/common/models/elements/compound-elements/likert/likert.ts index 076ad6d8c76cad39d8d4ccafada6d971944196f7..349c079dee8a973a58cb9c67b20fde0a6f206a89 100644 --- a/projects/common/models/elements/compound-elements/likert/likert.ts +++ b/projects/common/models/elements/compound-elements/likert/likert.ts @@ -2,16 +2,18 @@ import { Type } from '@angular/core'; import { ElementFactory } from 'common/util/element.factory'; import { BasicStyles, CompoundElement, UIElement, - PositionedUIElement, PositionProperties, TextImageLabel + PositionedUIElement, PositionProperties, UIElementValue, TextImageLabel, OptionElement } from 'common/models/elements/element'; import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row'; import { ElementComponent } from 'common/directives/element-component.directive'; import { LikertComponent } from 'common/components/compound-elements/likert/likert.component'; -export class LikertElement extends CompoundElement implements PositionedUIElement { +export class LikertElement extends CompoundElement implements PositionedUIElement, OptionElement { rows: LikertRowElement[] = []; - columns: TextImageLabel[] = []; + options: TextImageLabel[] = []; firstColumnSizeRatio: number = 5; + label: string = 'Optionentabelle Beschriftung'; + label2: string = 'Beschriftung Erste Spalte'; position: PositionProperties; styling: BasicStyles & { lineHeight: number; @@ -21,9 +23,11 @@ export class LikertElement extends CompoundElement implements PositionedUIElemen constructor(element: Partial<LikertElement>, ...args: unknown[]) { super({ width: 250, height: 200, ...element }, ...args); - if (element.columns) this.columns = element.columns; + if (element.options) this.options = [...element.options]; if (element.firstColumnSizeRatio) this.firstColumnSizeRatio = element.firstColumnSizeRatio; this.rows = element.rows !== undefined ? element.rows?.map(row => new LikertRowElement(row, ...args)) : []; + this.label = element.label !== undefined ? element.label : 'Optionentabelle Beschriftung'; + this.label2 = element.label2 !== undefined ? element.label2 : 'Optionentabelle Erste Spalte'; this.position = ElementFactory.initPositionProps(element.position); this.styling = { ...ElementFactory.initStylingProps({ @@ -36,7 +40,24 @@ export class LikertElement extends CompoundElement implements PositionedUIElemen }; } - getComponentFactory(): Type<ElementComponent> { + getNewOptionLabel(optionText: string): TextImageLabel { + return ElementFactory.createOptionLabel(optionText, true) as TextImageLabel; + } + + setProperty(property: string, value: UIElementValue): void { + super.setProperty(property, value); + if (property === 'rows') { + this.rows = value as LikertRowElement[]; + } + if (property === 'options') { + this.getChildElements().forEach(childElement => childElement.setProperty('columnCount', this.options.length)); + } + if (property === 'readOnly') { + this.getChildElements().forEach(childElement => childElement.setProperty('readOnly', value)); + } + } + + getElementComponent(): Type<ElementComponent> { return LikertComponent; } diff --git a/projects/common/models/elements/element.ts b/projects/common/models/elements/element.ts index 89828479776ce66b25e535fffaacc5f203430506..509e778bfbb3560b477ca6ab670c01f46175985d 100644 --- a/projects/common/models/elements/element.ts +++ b/projects/common/models/elements/element.ts @@ -1,15 +1,17 @@ +// eslint-disable-next-line max-classes-per-file import { ElementComponent } from 'common/directives/element-component.directive'; import { Type } from '@angular/core'; import { ClozeDocument } from 'common/models/elements/compound-elements/cloze/cloze'; import { ElementFactory } from 'common/util/element.factory'; import { IDManager } from 'common/util/id-manager'; +import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row'; export type UIElementType = 'text' | 'button' | 'text-field' | 'text-field-simple' | 'text-area' | 'checkbox' | 'dropdown' | 'radio' | 'image' | 'audio' | 'video' | 'likert' | 'likert-row' | 'radio-group-images' | 'drop-list' | 'drop-list-simple' | 'cloze' | 'spell-correct' | 'slider' | 'frame' | 'toggle-button'; export type UIElementValue = string | number | boolean | undefined | UIElementType | InputElementValue | -TextImageLabel[] | ClozeDocument | TextImageLabel | +TextLabel | TextLabel[] | ClozeDocument | LikertRowElement[] | PositionProperties | PlayerProperties | BasicStyles; export type InputAssistancePreset = null | 'french' | 'numbers' | 'numbersAndOperators' | 'numbersAndBasicOperators' @@ -75,9 +77,10 @@ export abstract class UIElement { } abstract getComponentFactory(): Type<ElementComponent>; + abstract getElementComponent(): Type<ElementComponent>; } -export type InputElementValue = string[] | string | number | boolean | DragNDropValueObject[] | null; +export type InputElementValue = string[] | string | number | boolean | TextLabel[] | null; export abstract class InputElement extends UIElement { label: string = 'Beschriftung'; @@ -222,14 +225,21 @@ export interface ValueChangeElement { value: InputElementValue; } -export interface TextImageLabel { +export interface OptionElement extends UIElement { + getNewOptionLabel(optionText: string): Label; +} + +export interface TextLabel { text: string; +} + +export interface TextImageLabel extends TextLabel { imgSrc: string | null; - position: 'above' | 'below' | 'left' | 'right'; + imgPosition: 'above' | 'below' | 'left' | 'right'; } -export type DragNDropValueObject = { +export interface DragNDropValueObject extends TextImageLabel { id: string; - stringValue?: string; - imgSrcValue?: string; -}; +} + +export type Label = TextLabel | TextImageLabel | DragNDropValueObject; diff --git a/projects/common/models/elements/frame/frame.ts b/projects/common/models/elements/frame/frame.ts index 5a63eafeff14fbb031b76753d97b77cfdb196190..371dd50d10911ab84002147e77d98928158d5dfa 100644 --- a/projects/common/models/elements/frame/frame.ts +++ b/projects/common/models/elements/frame/frame.ts @@ -1,10 +1,17 @@ import { Type } from '@angular/core'; import { ElementFactory } from 'common/util/element.factory'; -import { BasicStyles, PositionedUIElement, PositionProperties, UIElement } from 'common/models/elements/element'; +import { + BasicStyles, PositionedUIElement, PositionProperties, UIElement +} from 'common/models/elements/element'; import { FrameComponent } from 'common/components/frame/frame.component'; import { ElementComponent } from 'common/directives/element-component.directive'; export class FrameElement extends UIElement implements PositionedUIElement { + hasBorderTop: boolean = true; + hasBorderBottom: boolean = true; + hasBorderLeft: boolean = true; + hasBorderRight: boolean = true; + position: PositionProperties; styling: BasicStyles & { borderWidth: number; @@ -22,13 +29,13 @@ export class FrameElement extends UIElement implements PositionedUIElement { borderWidth: 1, borderColor: 'black', borderStyle: 'solid', - borderRadius: 0, + borderRadius: 0, ...element.styling }) }; } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return FrameComponent; } } diff --git a/projects/common/models/elements/input-elements/checkbox.ts b/projects/common/models/elements/input-elements/checkbox.ts index aa820b603f78d1310f73375e837c0e39dc74f0d5..cd4136357b5a85d6775a93f6c5698e210d6ac439 100644 --- a/projects/common/models/elements/input-elements/checkbox.ts +++ b/projects/common/models/elements/input-elements/checkbox.ts @@ -1,12 +1,6 @@ import { Type } from '@angular/core'; import { ElementFactory } from 'common/util/element.factory'; -import { - BasicStyles, - InputElement, - PositionedUIElement, - PositionProperties, - AnswerScheme, AnswerSchemeValue -} from 'common/models/elements/element'; +import { BasicStyles, InputElement, PositionedUIElement, PositionProperties, AnswerScheme, AnswerSchemeValue } from 'common/models/elements/element'; import { ElementComponent } from 'common/directives/element-component.directive'; import { CheckboxComponent } from 'common/components/input-elements/checkbox.component'; @@ -43,7 +37,7 @@ export class CheckboxElement extends InputElement implements PositionedUIElement ]; } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return CheckboxComponent; } } diff --git a/projects/common/models/elements/input-elements/drop-list.ts b/projects/common/models/elements/input-elements/drop-list.ts index 9cb089f7e66a81bc1427d246a9c820d8de1a1fb4..43d63b33295ee2060b5b82bc72cb813d818be953 100644 --- a/projects/common/models/elements/input-elements/drop-list.ts +++ b/projects/common/models/elements/input-elements/drop-list.ts @@ -3,7 +3,8 @@ import { ElementFactory } from 'common/util/element.factory'; import { InputElement, PositionedUIElement, DragNDropValueObject, - BasicStyles, PositionProperties, AnswerScheme, AnswerSchemeValue + BasicStyles, PositionProperties, + AnswerScheme, AnswerSchemeValue } from 'common/models/elements/element'; import { ElementComponent } from 'common/directives/element-component.directive'; import { DropListComponent } from 'common/components/input-elements/drop-list.component'; @@ -13,6 +14,7 @@ import { } from 'common/models/elements/compound-elements/cloze/cloze-child-elements/drop-list-simple'; export class DropListElement extends InputElement implements PositionedUIElement { + value: DragNDropValueObject[]; onlyOneItem: boolean = false; connectedTo: string[] = []; copyOnDrop: boolean = false; @@ -35,8 +37,9 @@ export class DropListElement extends InputElement implements PositionedUIElement if (element.copyOnDrop) this.copyOnDrop = element.copyOnDrop; if (element.orientation) this.orientation = element.orientation; if (element.highlightReceivingDropList) this.highlightReceivingDropList = element.highlightReceivingDropList; - if (element.highlightReceivingDropListColor) this.highlightReceivingDropListColor = - element.highlightReceivingDropListColor; + if (element.highlightReceivingDropListColor) { + this.highlightReceivingDropListColor = element.highlightReceivingDropListColor; + } this.position = ElementFactory.initPositionProps({ useMinHeight: true, ...element.position }); this.styling = { ...ElementFactory.initStylingProps({ @@ -78,7 +81,7 @@ export class DropListElement extends InputElement implements PositionedUIElement return (!this.connectedTo.length && (this.value as DragNDropValueObject[]).length > 1); } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return DropListComponent; } } diff --git a/projects/common/models/elements/input-elements/dropdown.ts b/projects/common/models/elements/input-elements/dropdown.ts index ab1ece7b2f9473d47333fb1dabef0419b945deb7..234c1921fec2198ea487dac69f97d5d0b58fce07 100644 --- a/projects/common/models/elements/input-elements/dropdown.ts +++ b/projects/common/models/elements/input-elements/dropdown.ts @@ -1,24 +1,21 @@ import { Type } from '@angular/core'; import { ElementFactory } from 'common/util/element.factory'; import { - BasicStyles, - InputElement, - PositionedUIElement, - PositionProperties, + BasicStyles, InputElement, TextLabel, PositionedUIElement, PositionProperties, OptionElement, AnswerScheme, AnswerSchemeValue } from 'common/models/elements/element'; import { ElementComponent } from 'common/directives/element-component.directive'; import { DropdownComponent } from 'common/components/input-elements/dropdown.component'; -export class DropdownElement extends InputElement implements PositionedUIElement { - options: string[] = []; +export class DropdownElement extends InputElement implements PositionedUIElement, OptionElement { + options: TextLabel[] = []; allowUnset: boolean = false; position: PositionProperties; styling: BasicStyles; constructor(element: Partial<DropdownElement>, ...args: unknown[]) { super({ width: 240, height: 83, ...element }, ...args); - if (element.options) this.options = element.options; + if (element.options) this.options = [...element.options]; if (element.allowUnset) this.allowUnset = element.allowUnset; this.position = ElementFactory.initPositionProps(element.position); this.styling = { @@ -46,7 +43,11 @@ export class DropdownElement extends InputElement implements PositionedUIElement return this.options.map((option, index) => ({ value: (index + 1).toString(), label: option })); } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return DropdownComponent; } + + getNewOptionLabel(optionText: string): TextLabel { + return ElementFactory.createOptionLabel(optionText) as TextLabel; + } } diff --git a/projects/common/models/elements/input-elements/radio-button-group-complex.ts b/projects/common/models/elements/input-elements/radio-button-group-complex.ts index 4b147d993854b2ed65ac58b215a5cf66ba5b2aa4..d31bbc7c7092a231794afd71eb6cd4b098d6e7ef 100644 --- a/projects/common/models/elements/input-elements/radio-button-group-complex.ts +++ b/projects/common/models/elements/input-elements/radio-button-group-complex.ts @@ -1,23 +1,23 @@ import { Type } from '@angular/core'; import { ElementFactory } from 'common/util/element.factory'; import { - BasicStyles, - InputElement, - PositionedUIElement, - PositionProperties, AnswerScheme, AnswerSchemeValue, - TextImageLabel + BasicStyles, InputElement, OptionElement, + PositionedUIElement, PositionProperties, TextImageLabel, + AnswerScheme, AnswerSchemeValue, } from 'common/models/elements/element'; import { ElementComponent } from 'common/directives/element-component.directive'; import { RadioGroupImagesComponent } from 'common/components/input-elements/radio-group-images.component'; -export class RadioButtonGroupComplexElement extends InputElement implements PositionedUIElement { - columns: TextImageLabel[] = []; +export class RadioButtonGroupComplexElement extends InputElement implements PositionedUIElement, OptionElement { + options: TextImageLabel[] = []; + itemsPerRow: number | null; position: PositionProperties; styling: BasicStyles; constructor(element: Partial<RadioButtonGroupComplexElement>, ...args: unknown[]) { super({ height: 100, ...element }, ...args); - if (element.columns) this.columns = element.columns; + if (element.options) this.options = [...element.options]; + this.itemsPerRow = element.itemsPerRow !== undefined ? element.itemsPerRow : null; this.position = ElementFactory.initPositionProps({ marginBottom: 40, ...element.position }); this.styling = { ...ElementFactory.initStylingProps({ backgroundColor: 'transparent', ...element.styling }) @@ -45,7 +45,11 @@ export class RadioButtonGroupComplexElement extends InputElement implements Posi .map((option, index) => ({ value: (index + 1).toString(), label: option.text })); //TODO iMAGE } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return RadioGroupImagesComponent; } + + getNewOptionLabel(optionText: string): TextImageLabel { + return ElementFactory.createOptionLabel(optionText, true) as TextImageLabel; + } } diff --git a/projects/common/models/elements/input-elements/radio-button-group.ts b/projects/common/models/elements/input-elements/radio-button-group.ts index a97c5c4f79a3f84c73431aa8c30aa5a9c76c92f6..89e555c612312016e0cdfb4ab330826de24ffc04 100644 --- a/projects/common/models/elements/input-elements/radio-button-group.ts +++ b/projects/common/models/elements/input-elements/radio-button-group.ts @@ -1,17 +1,14 @@ import { Type } from '@angular/core'; import { ElementFactory } from 'common/util/element.factory'; import { - BasicStyles, - InputElement, - PositionedUIElement, - PositionProperties, + BasicStyles, InputElement, TextLabel, PositionedUIElement, PositionProperties, OptionElement, AnswerScheme, AnswerSchemeValue } from 'common/models/elements/element'; import { ElementComponent } from 'common/directives/element-component.directive'; import { RadioButtonGroupComponent } from 'common/components/input-elements/radio-button-group.component'; -export class RadioButtonGroupElement extends InputElement implements PositionedUIElement { - richTextOptions: string[] = []; +export class RadioButtonGroupElement extends InputElement implements PositionedUIElement, OptionElement { + options: TextLabel[] = []; alignment: 'column' | 'row' = 'column'; strikeOtherOptions: boolean = false; position: PositionProperties; @@ -21,9 +18,12 @@ export class RadioButtonGroupElement extends InputElement implements PositionedU constructor(element: Partial<RadioButtonGroupElement>, ...args: unknown[]) { super({ height: 100, ...element }, ...args); - if (element.richTextOptions) this.richTextOptions = element.richTextOptions; + if (element.options) this.options = [...element.options]; if (element.alignment) this.alignment = element.alignment; if (element.strikeOtherOptions) this.strikeOtherOptions = element.strikeOtherOptions; + + this.value = element.value !== undefined ? element.value : []; + this.position = ElementFactory.initPositionProps({ marginBottom: 30, ...element.position }); this.styling = { ...ElementFactory.initStylingProps({ @@ -55,7 +55,11 @@ export class RadioButtonGroupElement extends InputElement implements PositionedU .map((option, index) => ({ value: (index + 1).toString(), label: option })); } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return RadioButtonGroupComponent; } + + getNewOptionLabel(optionText: string): TextLabel { + return ElementFactory.createOptionLabel(optionText) as TextLabel; + } } diff --git a/projects/common/models/elements/input-elements/slider.ts b/projects/common/models/elements/input-elements/slider.ts index 756f989a426a9bc9f95a27e24f8fdd3e671ea9ef..8cd2ceb5ba191fbd35fb675124279192260d5600 100644 --- a/projects/common/models/elements/input-elements/slider.ts +++ b/projects/common/models/elements/input-elements/slider.ts @@ -1,11 +1,7 @@ import { Type } from '@angular/core'; import { ElementFactory } from 'common/util/element.factory'; import { - BasicStyles, - InputElement, - PositionedUIElement, - PositionProperties, - AnswerScheme, AnswerSchemeValue + BasicStyles, InputElement, PositionedUIElement, PositionProperties, AnswerScheme, AnswerSchemeValue } from 'common/models/elements/element'; import { ElementComponent } from 'common/directives/element-component.directive'; import { SliderComponent } from 'common/components/input-elements/slider.component'; @@ -60,7 +56,7 @@ export class SliderElement extends InputElement implements PositionedUIElement { )) as AnswerSchemeValue[]; } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return SliderComponent; } } diff --git a/projects/common/models/elements/input-elements/spell-correct.ts b/projects/common/models/elements/input-elements/spell-correct.ts index 219a1585a4cbfe634b2ede5a73481e488cd15eeb..7b18ae065ed41540142ac2584107ab340bbfc0f9 100644 --- a/projects/common/models/elements/input-elements/spell-correct.ts +++ b/projects/common/models/elements/input-elements/spell-correct.ts @@ -50,7 +50,7 @@ export class SpellCorrectElement extends InputElement implements PositionedUIEle }; } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return SpellCorrectComponent; } } diff --git a/projects/common/models/elements/input-elements/text-area.ts b/projects/common/models/elements/input-elements/text-area.ts index 434f578a3338d3a68a6a10987325d2b29e206cbd..9c9ba32e000e515af25b5f9cfe267b94b46df004 100644 --- a/projects/common/models/elements/input-elements/text-area.ts +++ b/projects/common/models/elements/input-elements/text-area.ts @@ -62,7 +62,7 @@ export class TextAreaElement extends InputElement implements PositionedUIElement }; } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return TextAreaComponent; } } diff --git a/projects/common/models/elements/input-elements/text-field.ts b/projects/common/models/elements/input-elements/text-field.ts index 55cbbf353e4d59cf866f4718c13021a5b4227484..3ced6845e32f98207b3a427f813275ea3bcc4805 100644 --- a/projects/common/models/elements/input-elements/text-field.ts +++ b/projects/common/models/elements/input-elements/text-field.ts @@ -9,11 +9,11 @@ import { TextFieldComponent } from 'common/components/input-elements/text-field. export class TextFieldElement extends InputElement implements PositionedUIElement { appearance: 'fill' | 'outline' = 'outline'; - minLength: number | undefined; + minLength: number | null = null; minLengthWarnMessage: string = 'Eingabe zu kurz'; - maxLength: number | undefined; + maxLength: number | null = null; maxLengthWarnMessage: string = 'Eingabe zu lang'; - pattern: string | undefined; + pattern: string | null = null; patternWarnMessage: string = 'Eingabe entspricht nicht der Vorgabe'; inputAssistancePreset: InputAssistancePreset = null; inputAssistancePosition: 'floating' | 'right' = 'floating'; @@ -29,11 +29,11 @@ export class TextFieldElement extends InputElement implements PositionedUIElemen constructor(element: Partial<TextFieldElement>, ...args: unknown[]) { super({ width: 180, height: 120, ...element }, ...args); if (element.appearance) this.appearance = element.appearance; - if (element.minLength) this.minLength = element.minLength; + if (element.minLength !== undefined) this.minLength = element.minLength; if (element.minLengthWarnMessage) this.minLengthWarnMessage = element.minLengthWarnMessage; - if (element.maxLength) this.maxLength = element.maxLength; + if (element.maxLength !== undefined) this.maxLength = element.maxLength; if (element.maxLengthWarnMessage) this.maxLengthWarnMessage = element.maxLengthWarnMessage; - if (element.pattern) this.pattern = element.pattern; + if (element.pattern !== undefined) this.pattern = element.pattern; if (element.patternWarnMessage) this.patternWarnMessage = element.patternWarnMessage; if (element.inputAssistancePreset) this.inputAssistancePreset = element.inputAssistancePreset; if (element.inputAssistancePosition) this.inputAssistancePosition = element.inputAssistancePosition; @@ -69,7 +69,7 @@ export class TextFieldElement extends InputElement implements PositionedUIElemen }; } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return TextFieldComponent; } } diff --git a/projects/common/models/elements/media-elements/audio.ts b/projects/common/models/elements/media-elements/audio.ts index 388ac28b0c37f72d709b77e63f04cd14fb9e284b..1bce01b88e5fe095e0eb8cd22a47b35d15b1ea03 100644 --- a/projects/common/models/elements/media-elements/audio.ts +++ b/projects/common/models/elements/media-elements/audio.ts @@ -14,7 +14,7 @@ export class AudioElement extends PlayerElement implements PositionedUIElement { this.position = ElementFactory.initPositionProps(element.position); } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return AudioComponent; } } diff --git a/projects/common/models/elements/media-elements/image.ts b/projects/common/models/elements/media-elements/image.ts index 3c72fd0cc7704774614147e6514fcee9000c0570..38b2e1354a44825cc5ccaffbebb8d76b5dcb3191 100644 --- a/projects/common/models/elements/media-elements/image.ts +++ b/projects/common/models/elements/media-elements/image.ts @@ -24,7 +24,7 @@ export class ImageElement extends UIElement implements PositionedUIElement { this.position = ElementFactory.initPositionProps(element.position); } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return ImageComponent; } diff --git a/projects/common/models/elements/media-elements/video.ts b/projects/common/models/elements/media-elements/video.ts index 89ff862e1664ea3f8bf9793b51aafbfd3eaad7d8..b3e74720711cee7cb4b2204a9b467dd56d52e96c 100644 --- a/projects/common/models/elements/media-elements/video.ts +++ b/projects/common/models/elements/media-elements/video.ts @@ -16,7 +16,7 @@ export class VideoElement extends PlayerElement implements PositionedUIElement { this.position = ElementFactory.initPositionProps(element.position); } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return VideoComponent; } } diff --git a/projects/common/models/elements/text/text.ts b/projects/common/models/elements/text/text.ts index 7b122d20e4ec1e0cbe581f568bbca9a6b89fda1b..277b566de7eb3c7d1105c46f8520c7e7e631bd62 100644 --- a/projects/common/models/elements/text/text.ts +++ b/projects/common/models/elements/text/text.ts @@ -57,7 +57,7 @@ export class TextElement extends UIElement implements PositionedUIElement { }; } - getComponentFactory(): Type<ElementComponent> { + getElementComponent(): Type<ElementComponent> { return TextComponent; } } diff --git a/projects/common/models/section.ts b/projects/common/models/section.ts index 7a02910058ca32ff172427d16c4d0464776c6e93..3eae3dba822670a52b7929c6117e9ad6389f2522 100644 --- a/projects/common/models/section.ts +++ b/projects/common/models/section.ts @@ -1,11 +1,7 @@ import { Type } from '@angular/core'; import { IDManager } from 'common/util/id-manager'; import { - InputElement, PlayerElement, - PositionedUIElement, - AnswerScheme, - UIElement, - UIElementValue + CompoundElement, PositionedUIElement, UIElement, UIElementValue, AnswerScheme } from 'common/models/elements/element'; import { ButtonElement } from 'common/models/elements/button/button'; import { TextElement } from 'common/models/elements/text/text'; @@ -45,25 +41,25 @@ export class Section { activeAfterID: string | null = null; static ELEMENT_CLASSES: Record<string, Type<UIElement>> = { - 'text': TextElement, - 'button': ButtonElement, + text: TextElement, + button: ButtonElement, 'text-field': TextFieldElement, 'text-field-simple': TextFieldSimpleElement, 'text-area': TextAreaElement, - 'checkbox': CheckboxElement, - 'dropdown': DropdownElement, - 'radio': RadioButtonGroupElement, - 'image': ImageElement, - 'audio': AudioElement, - 'video': VideoElement, - 'likert': LikertElement, + checkbox: CheckboxElement, + dropdown: DropdownElement, + radio: RadioButtonGroupElement, + image: ImageElement, + audio: AudioElement, + video: VideoElement, + likert: LikertElement, 'radio-group-images': RadioButtonGroupComplexElement, 'drop-list': DropListElement, 'drop-list-simple': DropListSimpleElement, - 'cloze': ClozeElement, - 'slider': SliderElement, + cloze: ClozeElement, + slider: SliderElement, 'spell-correct': SpellCorrectElement, - 'frame': FrameElement, + frame: FrameElement, 'toggle-button': ToggleButtonElement }; @@ -90,13 +86,14 @@ export class Section { } addElement(element: PositionedUIElement): void { + element.position.dynamicPositioning = this.dynamicPositioning; this.elements.push(element); } /* Includes children of children, i.e. compound children. */ getAllElements(elementType?: string): UIElement[] { let allElements: UIElement[] = - this.elements.map(element => [element, ...element.getChildElements()]) + this.elements.map(element => [element, ...(element as CompoundElement).getChildElements() || []]) .flat(); if (elementType) { allElements = allElements.filter(element => element.type === elementType); diff --git a/projects/common/pipes/safe-resource-html.pipe.ts b/projects/common/pipes/safe-resource-html.pipe.ts index f7e63e0fbf8ab81520599c7f84d415feb7cc0de8..e22d8597b8cd28f65b1e16f642416d5de6146d22 100644 --- a/projects/common/pipes/safe-resource-html.pipe.ts +++ b/projects/common/pipes/safe-resource-html.pipe.ts @@ -1,5 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core'; -import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; +import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; @Pipe({ name: 'safeResourceHTML' @@ -7,7 +7,7 @@ import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; export class SafeResourceHTMLPipe implements PipeTransform { constructor(private sanitizer: DomSanitizer) {} - transform(resourceUrl: string): SafeResourceUrl { - return this.sanitizer.bypassSecurityTrustHtml(resourceUrl); + transform(safeHtml: string): SafeHtml { + return this.sanitizer.bypassSecurityTrustHtml(safeHtml); } } diff --git a/projects/common/services/id.service.spec.ts b/projects/common/services/id.service.spec.ts deleted file mode 100644 index efa25193236da5c700a42cf861206e3501e24cc1..0000000000000000000000000000000000000000 --- a/projects/common/services/id.service.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { IDManager } from 'common/util/id-manager'; - -describe('IDService', () => { - let service: IDManager; - - beforeEach(() => { - service = IDManager.getInstance(); - service.reset(); - }); - - it('getNewID should fail on empty string param', () => { - expect(() => { service.getNewID(''); }).toThrow(Error('ID-Service: No type given!')); - }); - - it('getNewID should return first ID', () => { - expect(service.getNewID('text')).toBe('text_1'); - }); - - it('getNewID should return different IDs - counting up', () => { - service.getNewID('text'); - expect(service.getNewID('text')).toBe('text_2'); - }); - - it('service should return next id when one is already taken', () => { - service.addID('text_1'); - expect(service.getNewID('text')).toBe('text_2'); - }); - - it('isIdAvailable should return false when id is already taken', () => { - expect(service.isIdAvailable('text_1')).toBe(true); - service.addID('text_1'); - expect(service.isIdAvailable('text_1')).toBe(false); - expect(service.isIdAvailable('text_2')).toBe(true); - }); - - it('isIdAvailable should return true when ID is returned (freed up)', () => { - expect(service.isIdAvailable('text_1')).toBe(true); - service.addID('text_1'); - expect(service.isIdAvailable('text_1')).toBe(false); - service.removeId('text_1'); - expect(service.isIdAvailable('text_1')).toBe(true); - }); -}); diff --git a/projects/common/services/sanitization.service.ts b/projects/common/services/sanitization.service.ts index bf57a0c950602eedee9021dc8a17ef396a98d676..0da73c37bb67d241c56cd5b213d33da613d5f28d 100644 --- a/projects/common/services/sanitization.service.ts +++ b/projects/common/services/sanitization.service.ts @@ -1,7 +1,5 @@ import { Injectable } from '@angular/core'; -import packageJSON from '../../../package.json'; import { Editor } from '@tiptap/core'; -import StarterKit from '@tiptap/starter-kit'; import ToggleButtonExtension from 'common/models/elements/compound-elements/cloze/tiptap-editor-extensions/toggle-button'; import DropListExtension from 'common/models/elements/compound-elements/cloze/tiptap-editor-extensions/drop-list'; @@ -10,7 +8,7 @@ import { Unit } from 'common/models/unit'; import { BasicStyles, DragNDropValueObject, ExtendedStyles, InputElement, PlayerProperties, - PositionedUIElement, PositionProperties, TextImageLabel, + PositionedUIElement, PositionProperties, TextImageLabel, TextLabel, UIElement, UIElementValue } from 'common/models/elements/element'; import { LikertElement } from 'common/models/elements/compound-elements/likert/likert'; @@ -28,12 +26,13 @@ import { DropListElement } from 'common/models/elements/input-elements/drop-list import { Page } from 'common/models/page'; import { Section } from 'common/models/section'; import { IDManager } from 'common/util/id-manager'; +import packageJSON from '../../../package.json'; +import { RadioButtonGroupComplexElement } from 'common/models/elements/input-elements/radio-button-group-complex'; @Injectable({ providedIn: 'root' }) export class SanitizationService { - private static expectedUnitVersion: [number, number, number] = packageJSON.config.unit_definition_version.split('.') as unknown as [number, number, number]; @@ -118,8 +117,8 @@ export class SanitizationService { .includes(newElement.type as string)) { newElement = SanitizationService.handlePlusOne(newElement as InputElement); } - if (['radio'].includes(newElement.type as string)) { - newElement = SanitizationService.handleRadioButtonGroupElement(newElement as RadioButtonGroupElement); + if (['radio-group-images'].includes(newElement.type as string)) { + newElement = SanitizationService.fixImageLabel(newElement as RadioButtonGroupComplexElement); } if (['likert'].includes(newElement.type as string)) { newElement = this.handleLikertElement(newElement as LikertElement); @@ -308,7 +307,7 @@ export class SanitizationService { }); const editor = new Editor({ - extensions: [StarterKit, ToggleButtonExtension, DropListExtension, TextFieldExtension], + extensions: [ToggleButtonExtension, DropListExtension, TextFieldExtension], content: replacedText }); return editor.getJSON() as ClozeDocument; @@ -324,7 +323,9 @@ export class SanitizationService { (newElement.options as string[]).forEach(option => { (newElement.value as DragNDropValueObject[]).push({ id: IDManager.getInstance().getNewID('value'), - stringValue: option + text: option, + imgSrc: null, + imgPosition: 'above' }); }); } @@ -333,9 +334,18 @@ export class SanitizationService { (newElement.value as string[]).forEach(value => { newValues.push({ id: IDManager.getInstance().getNewID('value'), - stringValue: value + text: value, + imgSrc: null, + imgPosition: 'above' }); }); + // fix DragNDropValueObject stringValue -> text + // imgSrcValue -> imgSrc + (newElement as DropListElement).value.forEach((valueObject: any) => { + valueObject.text = valueObject.text || valueObject.stringValue; + valueObject.imgSrc = valueObject.text || valueObject.imgSrcValue; + valueObject.imgPosition = valueObject.imgPosition || valueObject.position; + }); newElement.value = newValues; } return newElement as DropListElement; @@ -344,21 +354,19 @@ export class SanitizationService { private handleLikertElement(element: LikertElement): LikertElement { return new LikertElement({ ...element, + options: element.options || element.columns, rows: element.rows .map((row: LikertRowElement) => this.sanitizeElement(row as Record<string, UIElementValue>) as LikertRowElement) }); } private static handleLikertRowElement(element: Record<string, UIElementValue>): Partial<LikertRowElement> { - if (element.rowLabel) { - return element; - } return new LikertRowElement({ ...element, rowLabel: { text: element.text, - imgSrc: null, - position: 'above' + imgSrc: element.imgSrc, + imgPosition: element.imgPosition || element.position || 'above' } as TextImageLabel }); } @@ -373,23 +381,20 @@ export class SanitizationService { element; } - private static handleRadioButtonGroupElement(element: RadioButtonGroupElement): RadioButtonGroupElement { + private static handleToggleButtonElement(element: ToggleButtonElement): ToggleButtonElement { if (element.richTextOptions) { return element; } - return new RadioButtonGroupElement({ + return new ToggleButtonElement({ ...element, richTextOptions: element.options as string[] }); } - private static handleToggleButtonElement(element: ToggleButtonElement): ToggleButtonElement { - if (element.richTextOptions) { - return element; - } - return new ToggleButtonElement({ - ...element, - richTextOptions: element.options as string[] + private static fixImageLabel(element: RadioButtonGroupComplexElement) { + element.options.forEach(option => { + option.imgPosition = option.imgPosition || (option as any).position || 'above'; }); + return element; } } diff --git a/projects/common/util/element.factory.ts b/projects/common/util/element.factory.ts index 7573c6f82d674b400bea45fdb2b718171d48b3ef..f0a15675a39b1a82a7d744da472f7be69a95a536 100644 --- a/projects/common/util/element.factory.ts +++ b/projects/common/util/element.factory.ts @@ -35,14 +35,6 @@ export abstract class ElementFactory { }; } - static initTextImageLabel(): TextImageLabel { - return { - text: '', - imgSrc: null, - position: 'above' - }; - } - static initPlayerProps(defaults: Partial<PlayerProperties> = {}): PlayerProperties { return { autostart: defaults.autostart !== undefined ? defaults.autostart as boolean : false, @@ -71,4 +63,12 @@ export abstract class ElementFactory { playbackTime: defaults.playbackTime !== undefined ? defaults.playbackTime as number : 0 }; } + + static createOptionLabel(optionText: string, addImg: boolean = false) { + return { + text: optionText, + imgSrc: addImg ? null : undefined, + imgPosition: addImg ? 'above' : undefined + }; + } } diff --git a/projects/common/util/id-manager.spec.ts b/projects/common/util/id-manager.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..9ecc529763e77fba4475f498e7349276c290bf4e --- /dev/null +++ b/projects/common/util/id-manager.spec.ts @@ -0,0 +1,43 @@ +import { IDManager } from 'common/util/id-manager'; + +describe('IDService', () => { + let manager: IDManager; + + beforeEach(() => { + manager = IDManager.getInstance(); + manager.reset(); + }); + + it('getNewID should fail on empty string param', () => { + expect(() => { manager.getNewID(''); }).toThrow(Error('ID-Service: No type given!')); + }); + + it('getNewID should return first ID', () => { + expect(manager.getNewID('text')).toBe('text_1'); + }); + + it('getNewID should return different IDs - counting up', () => { + manager.getNewID('text'); + expect(manager.getNewID('text')).toBe('text_2'); + }); + + it('manager should return next id when one is already taken', () => { + manager.addID('text_1'); + expect(manager.getNewID('text')).toBe('text_2'); + }); + + it('isIdAvailable should return false when id is already taken', () => { + expect(manager.isIdAvailable('text_1')).toBe(true); + manager.addID('text_1'); + expect(manager.isIdAvailable('text_1')).toBe(false); + expect(manager.isIdAvailable('text_2')).toBe(true); + }); + + it('isIdAvailable should return true when ID is returned (freed up)', () => { + expect(manager.isIdAvailable('text_1')).toBe(true); + manager.addID('text_1'); + expect(manager.isIdAvailable('text_1')).toBe(false); + manager.removeId('text_1'); + expect(manager.isIdAvailable('text_1')).toBe(true); + }); +}); diff --git a/projects/common/util/id-manager.ts b/projects/common/util/id-manager.ts index 09bc3eb92a4458c4a671b5d1d9601ab585a36745..71c523239dfce0c81cb971e87db923e4a0356d85 100644 --- a/projects/common/util/id-manager.ts +++ b/projects/common/util/id-manager.ts @@ -28,8 +28,11 @@ export class IDManager { value: 0 }; - static getInstance() { - return this.instance || (this.instance = new this()); + static getInstance(): IDManager { + if (!this.instance) { + this.instance = new this(); + } + return this.instance; } getNewID(type: string): string { diff --git a/projects/editor/src/app/app.component.ts b/projects/editor/src/app/app.component.ts index 518cfb9ee1ad4a0cb50f08f67ab9a07dcde40212..09a99e46fa02d5fec443e64208374ba84fbf8423 100644 --- a/projects/editor/src/app/app.component.ts +++ b/projects/editor/src/app/app.component.ts @@ -9,7 +9,7 @@ import { UnitService } from './services/unit.service'; selector: 'aspect-editor', template: ` <div fxLayout="column" class="mainView"> - <aspect-toolbar *ngIf="isStandalone()"></aspect-toolbar> + <aspect-toolbar *ngIf="isStandalone"></aspect-toolbar> <aspect-unit-view fxFlex></aspect-unit-view> </div> `, @@ -18,7 +18,7 @@ import { UnitService } from './services/unit.service'; ] }) export class AppComponent implements OnInit { - isStandalone = (): boolean => window === window.parent; + isStandalone = window === window.parent; constructor(private unitService: UnitService, private translateService: TranslateService, diff --git a/projects/editor/src/app/app.module.ts b/projects/editor/src/app/app.module.ts index 605bbb69eaa6b5a75852a5986f79959678306e6e..8a53bd3fa9b27cef8e154992b5e39ca1b497c3d3 100644 --- a/projects/editor/src/app/app.module.ts +++ b/projects/editor/src/app/app.module.ts @@ -19,17 +19,18 @@ import { MatDividerModule } from '@angular/material/divider'; import { MatInputModule } from '@angular/material/input'; import { MatListModule } from '@angular/material/list'; +import { SharedModule } from 'common/shared.module'; +import { SectionInsertDialogComponent } from 'editor/src/app/components/dialogs/section-insert-dialog.component'; import { AppComponent } from './app.component'; import { ToolbarComponent } from './components/toolbar/toolbar.component'; import { UiElementToolboxComponent } from - './components/new-ui-element-panel/ui-element-toolbox.component'; + './components/new-ui-element-panel/ui-element-toolbox.component'; import { UnitViewComponent } from './components/unit-view/unit-view.component'; import { CanvasComponent } from './components/canvas/canvas.component'; import { StaticCanvasOverlayComponent } from - './components/canvas/overlays/static-canvas-overlay.component'; + './components/canvas/overlays/static-canvas-overlay.component'; import { DynamicCanvasOverlayComponent } from - './components/canvas/overlays/dynamic-canvas-overlay.component'; -import { SharedModule } from 'common/shared.module'; + './components/canvas/overlays/dynamic-canvas-overlay.component'; import { EditorTranslateLoader } from './editor-translate-loader'; import { SectionMenuComponent } from './components/canvas/section-menu.component'; import { SectionStaticComponent } from './components/canvas/section-static.component'; @@ -39,7 +40,6 @@ import { ConfirmationDialogComponent } from './components/dialogs/confirmation-d import { TextEditDialogComponent } from './components/dialogs/text-edit-dialog.component'; import { TextEditMultilineDialogComponent } from './components/dialogs/text-edit-multiline-dialog.component'; import { PlayerEditDialogComponent } from './components/dialogs/player-edit-dialog.component'; -import { ColumnHeaderEditDialogComponent } from './components/dialogs/column-header-edit-dialog.component'; import { LikertRowEditDialogComponent } from './components/dialogs/likert-row-edit-dialog.component'; import { RichTextEditDialogComponent } from './components/dialogs/rich-text-edit-dialog.component'; import { DropListOptionEditDialogComponent } from './components/dialogs/drop-list-option-edit-dialog.component'; @@ -48,29 +48,45 @@ import { ToggleButtonNodeviewComponent } from './text-editor/angular-node-views/ import { TextFieldNodeviewComponent } from './text-editor/angular-node-views/text-field-nodeview.component'; import { DropListNodeviewComponent } from './text-editor/angular-node-views/drop-list-nodeview.component'; import { PositionFieldSetComponent } from - './components/properties-panel/position-properties-tab/input-groups/position-field-set.component'; + './components/properties-panel/position-properties-tab/input-groups/position-field-set.component'; import { DimensionFieldSetComponent } from - './components/properties-panel/position-properties-tab/input-groups/dimension-field-set.component'; + './components/properties-panel/position-properties-tab/input-groups/dimension-field-set.component'; import { ElementPropertiesPanelComponent } from './components/properties-panel/element-properties-panel.component'; import { ElementPositionPropertiesComponent } from - './components/properties-panel/position-properties-tab/element-position-properties.component'; + './components/properties-panel/position-properties-tab/element-position-properties.component'; import { ElementStylePropertiesComponent } from - './components/properties-panel/style-properties-tab/element-style-properties.component'; + './components/properties-panel/style-properties-tab/element-style-properties.component'; import { ElementModelPropertiesComponent } from - './components/properties-panel/model-properties-tab/element-model-properties.component'; + './components/properties-panel/model-properties-tab/element-model-properties.component'; import { DynamicSectionHelperGridComponent } from './components/canvas/dynamic-section-helper-grid.component'; import { ElementGridChangeListenerDirective } from './components/canvas/element-grid-change-listener.directive'; -import { OptionsFieldSetComponent } from './components/properties-panel/model-properties-tab/input-groups/options-field-set.component'; -import { TextPropertiesFieldSetComponent } from './components/properties-panel/model-properties-tab/input-groups/text-properties-field-set.component'; -import { ButtonPropertiesComponent } from './components/properties-panel/model-properties-tab/input-groups/button-properties.component'; -import { SliderPropertiesComponent } from './components/properties-panel/model-properties-tab/input-groups/slider-properties.component'; -import { InputElementPropertiesComponent } from './components/properties-panel/model-properties-tab/input-groups/input-element-properties.component'; -import { ImagePropertiesComponent } from './components/properties-panel/model-properties-tab/input-groups/image-properties.component'; -import { DropListPropertiesComponent } from './components/properties-panel/model-properties-tab/input-groups/drop-list-properties.component'; +import { OptionsFieldSetComponent } from + './components/properties-panel/model-properties-tab/input-groups/options-field-set.component'; +import { TextPropertiesFieldSetComponent } from + './components/properties-panel/model-properties-tab/input-groups/text-properties-field-set.component'; +import { ButtonPropertiesComponent } from + './components/properties-panel/model-properties-tab/input-groups/button-properties.component'; +import { SliderPropertiesComponent } from + './components/properties-panel/model-properties-tab/input-groups/slider-properties.component'; +import { TextFieldElementPropertiesComponent } from + './components/properties-panel/model-properties-tab/input-groups/text-field-element-properties.component'; +import { ScaleAndZoomPropertiesComponent } from + './components/properties-panel/model-properties-tab/input-groups/scale-and-zoom-properties.component'; +import { DropListPropertiesComponent } from + './components/properties-panel/model-properties-tab/input-groups/drop-list-properties.component'; import { RichTextEditorSimpleComponent } from './text-editor-simple/rich-text-editor-simple.component'; import { RichTextSimpleEditDialogComponent } from './components/dialogs/rich-text-simple-edit-dialog.component'; -import { SectionInsertDialogComponent } from 'editor/src/app/components/dialogs/section-insert-dialog.component'; +import { SelectPropertiesComponent } from + './components/properties-panel/model-properties-tab/input-groups/select-properties.component'; +import { InputElementPropertiesComponent } from + './components/properties-panel/model-properties-tab/input-groups/input-element-properties.component'; +import { PresetValuePropertiesComponent } from + './components/properties-panel/model-properties-tab/input-groups/preset-value-properties.component'; +import { OptionListPanelComponent } from './components/properties-panel/option-list-panel.component'; +import { LikertRowLabelPipe } from './components/properties-panel/likert-row-label.pipe'; +import { LabelEditDialogComponent } from './components/dialogs/label-edit-dialog.component'; +import { BorderPropertiesComponent } from './components/properties-panel/model-properties-tab/input-groups/border-properties.component'; @NgModule({ declarations: [ @@ -95,7 +111,6 @@ import { SectionInsertDialogComponent } from 'editor/src/app/components/dialogs/ TextEditDialogComponent, TextEditMultilineDialogComponent, PlayerEditDialogComponent, - ColumnHeaderEditDialogComponent, LikertRowEditDialogComponent, RichTextEditDialogComponent, ElementModelPropertiesComponent, @@ -108,12 +123,19 @@ import { SectionInsertDialogComponent } from 'editor/src/app/components/dialogs/ TextPropertiesFieldSetComponent, ButtonPropertiesComponent, SliderPropertiesComponent, - InputElementPropertiesComponent, - ImagePropertiesComponent, + TextFieldElementPropertiesComponent, + ScaleAndZoomPropertiesComponent, DropListPropertiesComponent, RichTextEditorSimpleComponent, RichTextSimpleEditDialogComponent, - SectionInsertDialogComponent + SectionInsertDialogComponent, + SelectPropertiesComponent, + InputElementPropertiesComponent, + PresetValuePropertiesComponent, + OptionListPanelComponent, + LikertRowLabelPipe, + LabelEditDialogComponent, + BorderPropertiesComponent ], imports: [ BrowserModule, diff --git a/projects/editor/src/app/components/canvas/canvas.component.ts b/projects/editor/src/app/components/canvas/canvas.component.ts index 4fc948d6be6d257dbd081aa5b354b9941f5442b1..ff640c329afa5b5068e0d9c279038fe0d7fcef39 100644 --- a/projects/editor/src/app/components/canvas/canvas.component.ts +++ b/projects/editor/src/app/components/canvas/canvas.component.ts @@ -2,14 +2,14 @@ import { Component, Input, QueryList, ViewChildren } from '@angular/core'; import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { PositionedUIElement, UIElement } from 'common/models/elements/element'; +import { Page } from 'common/models/page'; +import { Section } from 'common/models/section'; import { UnitService } from '../../services/unit.service'; import { SelectionService } from '../../services/selection.service'; import { CanvasElementOverlay } from './overlays/canvas-element-overlay'; import { SectionStaticComponent } from './section-static.component'; import { SectionDynamicComponent } from './section-dynamic.component'; -import { PositionedUIElement, UIElement } from 'common/models/elements/element'; -import { Page } from 'common/models/page'; -import { Section } from 'common/models/section'; @Component({ selector: 'aspect-page-canvas', diff --git a/projects/editor/src/app/components/canvas/dynamic-section-helper-grid.component.ts b/projects/editor/src/app/components/canvas/dynamic-section-helper-grid.component.ts index f8c898252a7e5a65c19df8988a3519727aba2084..756b579929865842311ca037b2ba0289bc3cc181 100644 --- a/projects/editor/src/app/components/canvas/dynamic-section-helper-grid.component.ts +++ b/projects/editor/src/app/components/canvas/dynamic-section-helper-grid.component.ts @@ -2,9 +2,9 @@ import { CdkDragDrop } from '@angular/cdk/drag-drop/drag-events'; import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; -import { UnitService } from '../../services/unit.service'; import { UIElement, UIElementType } from 'common/models/elements/element'; import { Section } from 'common/models/section'; +import { UnitService } from '../../services/unit.service'; @Component({ selector: '[app-dynamic-section-helper-grid]', @@ -44,7 +44,7 @@ export class DynamicSectionHelperGridComponent implements OnInit, OnChanges { columnCountArray: unknown[] = []; rowCountArray: unknown[] = []; - constructor(public unitService: UnitService, public ele: ElementRef) {} + constructor(public unitService: UnitService) {} ngOnInit(): void { this.calculateColumnCount(); diff --git a/projects/editor/src/app/components/canvas/overlays/canvas-element-overlay.ts b/projects/editor/src/app/components/canvas/overlays/canvas-element-overlay.ts index 1388d7d083fd83c859ec15bce599e5434caac81e..a48bce6829376430281a886a844c94d54c9f244c 100644 --- a/projects/editor/src/app/components/canvas/overlays/canvas-element-overlay.ts +++ b/projects/editor/src/app/components/canvas/overlays/canvas-element-overlay.ts @@ -4,14 +4,14 @@ import { } from '@angular/core'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; -import { UnitService } from '../../../services/unit.service'; import { ElementComponent } from 'common/directives/element-component.directive'; -import { SelectionService } from '../../../services/selection.service'; import { CompoundElementComponent } from 'common/directives/compound-element.directive'; import { ClozeComponent } from 'common/components/compound-elements/cloze/cloze.component'; import { CompoundChildOverlayComponent } from 'common/components/compound-elements/cloze/compound-child-overlay.component'; import { UIElement } from 'common/models/elements/element'; +import { UnitService } from '../../../services/unit.service'; +import { SelectionService } from '../../../services/selection.service'; @Directive() export abstract class CanvasElementOverlay implements OnInit, OnDestroy { @@ -30,7 +30,7 @@ export abstract class CanvasElementOverlay implements OnInit, OnDestroy { private changeDetectorRef: ChangeDetectorRef) { } ngOnInit(): void { - this.childComponent = this.elementContainer.createComponent(this.element.getComponentFactory()); + this.childComponent = this.elementContainer.createComponent(this.element.getElementComponent()); this.childComponent.instance.elementModel = this.element; // Make children not clickable. This way the only relevant events are managed by the overlay. @@ -62,19 +62,16 @@ export abstract class CanvasElementOverlay implements OnInit, OnDestroy { this.changeDetectorRef.detectChanges(); } - selectElement(multiSelect: boolean = false): void { - if (multiSelect) { - this.selectionService.selectElement({ elementComponent: this, multiSelect: true }); - } else { - this.selectionService.selectElement({ elementComponent: this, multiSelect: false }); - } - } - - elementClicked(event: MouseEvent): void { //TODO method name + selectElement(event?: MouseEvent): void { if (!this.isSelected) { - this.selectElement(event.shiftKey); + // this.selectElement(event.shiftKey); + if (event?.shiftKey) { + this.selectionService.selectElement({ elementComponent: this, multiSelect: true }); + } else { + this.selectionService.selectElement({ elementComponent: this, multiSelect: false }); + } } - event.stopPropagation(); + event?.stopPropagation(); this.elementSelected.emit(); } diff --git a/projects/editor/src/app/components/canvas/overlays/dynamic-canvas-overlay.component.ts b/projects/editor/src/app/components/canvas/overlays/dynamic-canvas-overlay.component.ts index 463674409aae453bbea99dda06d134b1b1e03b5f..c0a9cb8d2969be02f15a7d6410c7a01226e67692 100644 --- a/projects/editor/src/app/components/canvas/overlays/dynamic-canvas-overlay.component.ts +++ b/projects/editor/src/app/components/canvas/overlays/dynamic-canvas-overlay.component.ts @@ -6,29 +6,29 @@ import { CanvasElementOverlay } from './canvas-element-overlay'; @Component({ selector: 'aspect-dynamic-canvas-overlay', template: ` - <!-- TabIndex is needed to make the div selectable and catch keyboard events (delete). --> - <!-- DragStart and DragEnd are part of a cursor hack to style the body. See global styling file. --> - <div #draggableElement class="draggable-element" - [class.fixed-size-content-wrapper]="element.position?.dynamicPositioning && + <!-- TabIndex is needed to make the div selectable and catch keyboard events (delete). --> + <!-- DragStart and DragEnd are part of a cursor hack to style the body. See global styling file. --> + <div #draggableElement class="draggable-element" + [class.fixed-size-content-wrapper]="element.position?.dynamicPositioning && element.position?.fixedSize" - [class.temporaryHighlight]="temporaryHighlight" - tabindex="-1" - cdkDrag [cdkDragData]="{dragType: 'move', element: element}" - (click)="elementClicked($event)" (dblclick)="openEditDialog()" - (cdkDragStarted)="moveDragStart()" - (cdkDragEnded)="moveDragEnd()" - [style.outline]="isSelected ? 'purple solid 1px' : ''" - [style.z-index]="isSelected ? 2 : 1"> - <div *cdkDragPlaceholder></div> - <div [class.fixed-size-content]="element.position?.dynamicPositioning && + [class.temporaryHighlight]="temporaryHighlight" + tabindex="-1" + cdkDrag [cdkDragData]="{dragType: 'move', element: element}" + (click)="selectElement($event)" (dblclick)="openEditDialog()" + (cdkDragStarted)="selectElement(); moveDragStart()" + (cdkDragEnded)="moveDragEnd()" + [style.outline]="isSelected ? 'purple solid 1px' : ''" + [style.z-index]="isSelected ? 2 : 1"> + <div *cdkDragPlaceholder></div> + <div [class.fixed-size-content]="element.position?.dynamicPositioning && element.position?.fixedSize" - [style.width]="element.position?.dynamicPositioning && element.position?.fixedSize ? + [style.width]="element.position?.dynamicPositioning && element.position?.fixedSize ? element.width + 'px' : '100%'" - [style.height]="element.position?.dynamicPositioning && element.position?.fixedSize ? + [style.height]="element.position?.dynamicPositioning && element.position?.fixedSize ? element.height + 'px' : '100%'"> - <ng-template #elementContainer></ng-template> - </div> + <ng-template #elementContainer></ng-template> </div> + </div> `, styles: [ '.draggable-element {width: 100%; height: 100%}', @@ -42,7 +42,6 @@ export class DynamicCanvasOverlayComponent extends CanvasElementOverlay { bodyElement: HTMLElement = document.body; moveDragStart(): void { - this.selectElement(); this.bodyElement.classList.add('inheritCursors'); this.bodyElement.style.cursor = 'move'; } diff --git a/projects/editor/src/app/components/canvas/overlays/static-canvas-overlay.component.ts b/projects/editor/src/app/components/canvas/overlays/static-canvas-overlay.component.ts index 1b6ee4fb6475f743f5d9fb892218d5e7aae64aa8..cd2a2f70d965ffa0f9d50650d1ea05921bf893a6 100644 --- a/projects/editor/src/app/components/canvas/overlays/static-canvas-overlay.component.ts +++ b/projects/editor/src/app/components/canvas/overlays/static-canvas-overlay.component.ts @@ -1,8 +1,8 @@ import { Component } from '@angular/core'; import { take } from 'rxjs/operators'; import { CdkDragEnd, CdkDragMove } from '@angular/cdk/drag-drop'; -import { CanvasElementOverlay } from './canvas-element-overlay'; import { UIElement } from 'common/models/elements/element'; +import { CanvasElementOverlay } from './canvas-element-overlay'; @Component({ selector: 'aspect-static-canvas-overlay', @@ -11,11 +11,11 @@ import { UIElement } from 'common/models/elements/element'; <!-- TabIndex is needed to make the div selectable and catch keyboard events (delete). --> <div class="draggable-element" [class.temporaryHighlight]="temporaryHighlight" - (click)="elementClicked($event)" + (click)="selectElement($event)" (dblclick)="openEditDialog()" (keyup.delete)="deleteSelectedElements()" tabindex="-1" cdkDrag [cdkDragData]="{dragType: 'move', element: element}" - (cdkDragStarted)="!isSelected && selectElement()" + (cdkDragStarted)="selectElement()" cdkDropList> <div *cdkDragPlaceholder></div> <!-- Needs extra div because styling can interfere with drag and drop--> diff --git a/projects/editor/src/app/components/canvas/section-dynamic.component.ts b/projects/editor/src/app/components/canvas/section-dynamic.component.ts index c0b2ff0015750ed9f5edec94c04fccbb10746a80..93d8af2610d3987831bd0592a7d9c40a90e37438 100644 --- a/projects/editor/src/app/components/canvas/section-dynamic.component.ts +++ b/projects/editor/src/app/components/canvas/section-dynamic.component.ts @@ -2,9 +2,9 @@ import { Component, Input, Output, EventEmitter, ViewChildren, QueryList, ViewChild } from '@angular/core'; +import { Section } from 'common/models/section'; import { CanvasElementOverlay } from './overlays/canvas-element-overlay'; import { DynamicSectionHelperGridComponent } from './dynamic-section-helper-grid.component'; -import { Section } from 'common/models/section'; @Component({ selector: 'aspect-section-dynamic', diff --git a/projects/editor/src/app/components/canvas/section-menu.component.ts b/projects/editor/src/app/components/canvas/section-menu.component.ts index b3ce0f25425cdfaa65f000d18d65ec4176607c73..6aba70d35d5adce4444370e4ab38c808026d0fcf 100644 --- a/projects/editor/src/app/components/canvas/section-menu.component.ts +++ b/projects/editor/src/app/components/canvas/section-menu.component.ts @@ -5,12 +5,12 @@ import { import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { Clipboard } from '@angular/cdk/clipboard'; -import { UnitService } from '../../services/unit.service'; -import { DialogService } from '../../services/dialog.service'; -import { SelectionService } from '../../services/selection.service'; import { MessageService } from 'common/services/message.service'; import { UIElement } from 'common/models/elements/element'; import { Section } from 'common/models/section'; +import { UnitService } from '../../services/unit.service'; +import { DialogService } from '../../services/dialog.service'; +import { SelectionService } from '../../services/selection.service'; @Component({ selector: 'aspect-section-menu', diff --git a/projects/editor/src/app/components/canvas/section-static.component.ts b/projects/editor/src/app/components/canvas/section-static.component.ts index 07ff70cd9f928f100896f5f1bc799f7efe27becb..79766c964a138d3ba56dc6fe6b0dca99481780f2 100644 --- a/projects/editor/src/app/components/canvas/section-static.component.ts +++ b/projects/editor/src/app/components/canvas/section-static.component.ts @@ -1,10 +1,10 @@ import { Component, ElementRef, EventEmitter, Input, Output, QueryList, ViewChild, ViewChildren } from '@angular/core'; -import { UnitService } from '../../services/unit.service'; -import { CanvasElementOverlay } from './overlays/canvas-element-overlay'; import { Section } from 'common/models/section'; import { UIElementType } from 'common/models/elements/element'; +import { UnitService } from '../../services/unit.service'; +import { CanvasElementOverlay } from './overlays/canvas-element-overlay'; @Component({ selector: 'aspect-section-static', diff --git a/projects/editor/src/app/components/dialogs/column-header-edit-dialog.component.ts b/projects/editor/src/app/components/dialogs/column-header-edit-dialog.component.ts deleted file mode 100644 index 2458150c02da51771e9d3c1c1e07f0fff2b80fc4..0000000000000000000000000000000000000000 --- a/projects/editor/src/app/components/dialogs/column-header-edit-dialog.component.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Component, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { FileService } from 'common/services/file.service'; -import { TextImageLabel } from 'common/models/elements/element'; - -@Component({ - selector: 'aspect-likert-column-edit-dialog', - template: ` - <mat-dialog-content fxLayout="column"> - <aspect-rich-text-editor-simple [content]="data.column.text" - [defaultFontSize]="data.defaultFontSize" - (contentChange)="this.textContent = $event"> - </aspect-rich-text-editor-simple> - - <button mat-raised-button (click)="loadImage()">{{ 'loadImage' | translate }}</button> - <button mat-raised-button (click)="imgSrc = null">{{ 'removeImage' | translate }}</button> - <img [src]="imgSrc" - [style.object-fit]="'scale-down'" - [width]="200"> - <mat-form-field appearance="fill"> - <mat-label>{{'position' | translate }}</mat-label> - <mat-select #positionSelect [value]="data.column.position"> - <mat-option *ngFor="let option of ['above', 'below']" - [value]="option"> - {{ option | translate }} - </mat-option> - </mat-select> - </mat-form-field> - </mat-dialog-content> - <mat-dialog-actions> - <button mat-button [mat-dialog-close]="{ - text: textContent, - imgSrc: imgSrc, - position: positionSelect.value }"> - {{'save' | translate }} - </button> - <button mat-button mat-dialog-close>{{'cancel' | translate }}</button> - </mat-dialog-actions> - `, - styles: [ - 'aspect-rich-text-editor-simple {margin-bottom: 20px;}' - ] -}) -export class ColumnHeaderEditDialogComponent { - constructor(@Inject(MAT_DIALOG_DATA) public data: { column: TextImageLabel, defaultFontSize: number }) { } - textContent: string = this.data.column.text; - imgSrc: string | null = this.data.column.imgSrc; - - async loadImage(): Promise<void> { - this.imgSrc = await FileService.loadImage(); - } -} diff --git a/projects/editor/src/app/components/dialogs/drop-list-option-edit-dialog.component.ts b/projects/editor/src/app/components/dialogs/drop-list-option-edit-dialog.component.ts index 6e06a9c596dfec8a21a556196265219859fe3719..387774c772833190f57333ac013f321def10fcf6 100644 --- a/projects/editor/src/app/components/dialogs/drop-list-option-edit-dialog.component.ts +++ b/projects/editor/src/app/components/dialogs/drop-list-option-edit-dialog.component.ts @@ -9,10 +9,10 @@ import { DragNDropValueObject } from 'common/models/elements/element'; <mat-dialog-content fxLayout="column"> <mat-form-field> <mat-label>{{'text' | translate }}</mat-label> - <input #textField matInput type="text" [value]="data.value.stringValue"> + <input #textField matInput type="text" [value]="data.value.text"> </mat-form-field> <button mat-raised-button (click)="loadImage()">{{ 'loadImage' | translate }}</button> - <button mat-raised-button (click)="imgSrc = undefined">{{ 'removeImage' | translate }}</button> + <button mat-raised-button (click)="imgSrc = null">{{ 'removeImage' | translate }}</button> <img [src]="imgSrc" [style.object-fit]="'scale-down'" [width]="200"> @@ -23,8 +23,8 @@ import { DragNDropValueObject } from 'common/models/elements/element'; </mat-dialog-content> <mat-dialog-actions> <button mat-button [mat-dialog-close]="{ - stringValue: textField.value, - imgSrcValue: imgSrc, + text: textField.value, + imgSrc: imgSrc, id: idField.value } "> {{'save' | translate }} @@ -35,7 +35,7 @@ import { DragNDropValueObject } from 'common/models/elements/element'; }) export class DropListOptionEditDialogComponent { constructor(@Inject(MAT_DIALOG_DATA) public data: { value: DragNDropValueObject }) { } - imgSrc: string | undefined = this.data.value.imgSrcValue; + imgSrc: string | null = this.data.value.imgSrc; async loadImage(): Promise<void> { this.imgSrc = await FileService.loadImage(); diff --git a/projects/editor/src/app/components/dialogs/label-edit-dialog.component.ts b/projects/editor/src/app/components/dialogs/label-edit-dialog.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..7aec0d6e220ba1230c9e9409d556e9244c08831b --- /dev/null +++ b/projects/editor/src/app/components/dialogs/label-edit-dialog.component.ts @@ -0,0 +1,51 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { TextImageLabel } from 'common/models/elements/element'; +import { FileService } from 'common/services/file.service'; + +@Component({ + selector: 'aspect-label-edit-dialog', + template: ` + <mat-dialog-content fxLayout="column" fxLayoutGap="20px"> + <aspect-rich-text-editor-simple [(content)]="newLabel.text"> + </aspect-rich-text-editor-simple> + + <div *ngIf="newLabel.imgSrc !== undefined" fxLayout="row" fxLayoutAlign="space-between center"> + <div fxLayout="column" fxLayoutGap="10px"> + <button mat-raised-button (click)="loadImage()">{{ 'loadImage' | translate }}</button> + <button mat-raised-button (click)="newLabel.imgSrc = null">{{ 'removeImage' | translate }}</button> + <mat-form-field> + <mat-label>{{'imagePosition' | translate }}</mat-label> + <mat-select [(ngModel)]="newLabel.imgPosition" + [disabled]="newLabel.imgSrc == null"> + <mat-option *ngFor="let option of ['above', 'below', 'left', 'right']" + [value]="option"> + {{ option | translate }} + </mat-option> + </mat-select> + </mat-form-field> + </div> + <img [src]="newLabel.imgSrc" + [style.object-fit]="'scale-down'" + [width]="200"> + </div> + + </mat-dialog-content> + <mat-dialog-actions> + <button mat-button [mat-dialog-close]="newLabel">{{'save' | translate }}</button> + <button mat-button mat-dialog-close>{{'cancel' | translate }}</button> + </mat-dialog-actions> + `, + styles: [ + 'aspect-rich-text-editor-simple {margin-bottom: 20px;}' + ] +}) +export class LabelEditDialogComponent { + newLabel = { ...this.data.label }; + + constructor(@Inject(MAT_DIALOG_DATA) public data: { label: TextImageLabel }) { } + + async loadImage(): Promise<void> { + this.newLabel.imgSrc = await FileService.loadImage(); + } +} diff --git a/projects/editor/src/app/components/dialogs/likert-row-edit-dialog.component.ts b/projects/editor/src/app/components/dialogs/likert-row-edit-dialog.component.ts index 86097006f19b3445197797fe7aaabba33fed729b..038fb8f3d0ddd73d22b9c3beb96adac6597cb925 100644 --- a/projects/editor/src/app/components/dialogs/likert-row-edit-dialog.component.ts +++ b/projects/editor/src/app/components/dialogs/likert-row-edit-dialog.component.ts @@ -2,41 +2,37 @@ import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { FileService } from 'common/services/file.service'; import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row'; -import { TextImageLabel } from 'common/models/elements/element'; +import { TextLabel } from 'common/models/elements/element'; @Component({ selector: 'aspect-likert-row-edit-dialog', template: ` <mat-dialog-content fxLayout="column"> - <mat-form-field> - <mat-label>{{'text' | translate }}</mat-label> - <input #textField matInput type="text" [value]="data.row.rowLabel.text"> - </mat-form-field> + <aspect-rich-text-editor-simple [(content)]="newLikertRow.rowLabel.text"> + </aspect-rich-text-editor-simple> - <mat-form-field> + <mat-form-field [style.margin-top.px]="15"> <mat-label>{{'id' | translate }}</mat-label> - <input #idField matInput type="text" [value]="data.row.id"> + <input matInput type="text" [(ngModel)]="newLikertRow.id"> </mat-form-field> - <button mat-raised-button (click)="loadImage()">{{ 'loadImage' | translate }}</button> - <button mat-raised-button (click)="imgSrc = null">{{ 'removeImage' | translate }}</button> - <img [src]="imgSrc" - [style.object-fit]="'scale-down'" - [width]="200"> + <mat-checkbox [(ngModel)]="newLikertRow.readOnly"> + {{'propertiesPanel.readOnly' | translate }} + </mat-checkbox> <mat-form-field appearance="fill"> - <mat-label>{{'imagePosition' | translate }}</mat-label> - <mat-select #positionSelect [value]="data.row.rowLabel.position"> - <mat-option *ngFor="let option of ['above', 'below', 'left', 'right']" - [value]="option"> - {{ option | translate }} + <mat-label>{{'preset' | translate }}</mat-label> + <mat-select [(ngModel)]="newLikertRow.value"> + <mat-option [value]="null">{{'propertiesPanel.undefined' | translate }}</mat-option> + <mat-option *ngFor="let column of data.options; let i = index" [value]="i"> + {{column.text}} (Index: {{i}}) </mat-option> </mat-select> </mat-form-field> <mat-form-field appearance="fill"> <mat-label>{{'verticalButtonAlignment' | translate }}</mat-label> - <mat-select #verticalButtonAlignmentSelect [value]="data.row.verticalButtonAlignment"> + <mat-select [(ngModel)]="newLikertRow.verticalButtonAlignment"> <mat-option *ngFor="let option of ['auto', 'center']" [value]="option"> {{ option | translate }} @@ -44,40 +40,49 @@ import { TextImageLabel } from 'common/models/elements/element'; </mat-select> </mat-form-field> - <mat-form-field appearance="fill"> - <mat-label>{{'preset' | translate }}</mat-label> - <mat-select #valueField [value]="data.row.value"> - <mat-option [value]="null">{{'propertiesPanel.undefined' | translate }}</mat-option> - <mat-option *ngFor="let column of data.columns; let i = index" [value]="i + 1"> - {{column.text}} (Index: {{i + 1}}) - </mat-option> - </mat-select> - </mat-form-field> + <div fxLayout="row" fxLayoutAlign="space-between center"> + <div fxLayout="column" fxLayoutGap="10px"> + <button mat-raised-button (click)="loadImage()"> + {{ 'loadImage' | translate }}</button> + <button mat-raised-button (click)="newLikertRow.rowLabel.imgSrc = null"> + {{ 'removeImage' | translate }}</button> + <mat-form-field> + <mat-label>{{'imagePosition' | translate }}</mat-label> + <mat-select [(ngModel)]="newLikertRow.rowLabel.imgPosition"> + <mat-option *ngFor="let option of ['above', 'below', 'left', 'right']" + [value]="option"> + {{ option | translate }} + </mat-option> + </mat-select> + </mat-form-field> + </div> + + <img [src]="newLikertRow.rowLabel.imgSrc" + [style.object-fit]="'scale-down'" [width]="200"> + </div> + </mat-dialog-content> <mat-dialog-actions> - <button mat-button - [mat-dialog-close]="{ - rowLabel: { - text: textField.value, - imgSrc: imgSrc, - position: positionSelect.value - }, - id: idField.value, - value: valueField.value, - verticalButtonAlignment: verticalButtonAlignmentSelect.value - }"> + <button mat-button [mat-dialog-close]="newLikertRow"> {{'save' | translate }} </button> <button mat-button mat-dialog-close>{{'cancel' | translate }}</button> </mat-dialog-actions> - ` + `, + styles: [ + 'mat-checkbox {margin-bottom: 15px;}' + ] }) export class LikertRowEditDialogComponent { - constructor(@Inject(MAT_DIALOG_DATA) public data: { row: LikertRowElement, columns: TextImageLabel[] }) { } - imgSrc: string | null = this.data.row.rowLabel.imgSrc; + constructor(@Inject(MAT_DIALOG_DATA) public data: { row: LikertRowElement, options: TextLabel[] }) { } + + newLikertRow = { + ...this.data.row, + rowLabel: { ...this.data.row.rowLabel } + }; async loadImage(): Promise<void> { - this.imgSrc = await FileService.loadImage(); + this.newLikertRow.rowLabel.imgSrc = await FileService.loadImage(); } } diff --git a/projects/editor/src/app/components/dialogs/rich-text-simple-edit-dialog.component.ts b/projects/editor/src/app/components/dialogs/rich-text-simple-edit-dialog.component.ts index 04769292107e3fccacf83741e4c94dc25850126b..7e2c2a3cd053afcf8eff7ed128530889030cd644 100644 --- a/projects/editor/src/app/components/dialogs/rich-text-simple-edit-dialog.component.ts +++ b/projects/editor/src/app/components/dialogs/rich-text-simple-edit-dialog.component.ts @@ -1,18 +1,20 @@ import { Component, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { TextLabel } from 'common/models/elements/element'; @Component({ selector: 'aspect-rich-text-simple-edit-dialog', template: ` - <aspect-rich-text-editor-simple [(content)]="data.content" + <aspect-rich-text-editor-simple [(content)]="data.option.text" [defaultFontSize]="data.defaultFontSize"> </aspect-rich-text-editor-simple> <mat-dialog-actions> - <button mat-button [mat-dialog-close]="data.content">{{'save' | translate }}</button> + <button mat-button [mat-dialog-close]="data.option">{{'save' | translate }}</button> <button mat-button mat-dialog-close>{{'cancel' | translate }}</button> </mat-dialog-actions> ` }) export class RichTextSimpleEditDialogComponent { - constructor(@Inject(MAT_DIALOG_DATA) public data: { content: string, defaultFontSize: number }) { } + constructor(public dialogRef: MatDialogRef<RichTextSimpleEditDialogComponent>, + @Inject(MAT_DIALOG_DATA) public data: { option: TextLabel, defaultFontSize: number }) { } } diff --git a/projects/editor/src/app/components/new-ui-element-panel/ui-element-toolbox.component.ts b/projects/editor/src/app/components/new-ui-element-panel/ui-element-toolbox.component.ts index baa034c751a2b6c792658788f91d61bf7dfc167d..5b9c08243c4aa2af123ae566aac5c7b58ff5aac8 100644 --- a/projects/editor/src/app/components/new-ui-element-panel/ui-element-toolbox.component.ts +++ b/projects/editor/src/app/components/new-ui-element-panel/ui-element-toolbox.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; +import { UIElementType } from 'common/models/elements/element'; import { UnitService } from '../../services/unit.service'; import { SelectionService } from '../../services/selection.service'; -import { UIElementType } from 'common/models/elements/element'; @Component({ selector: 'aspect-ui-element-toolbox', diff --git a/projects/editor/src/app/components/properties-panel/element-properties-panel.component.css b/projects/editor/src/app/components/properties-panel/element-properties-panel.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e6fe1a8d940edbfc3618d89712e9261b49559509 --- /dev/null +++ b/projects/editor/src/app/components/properties-panel/element-properties-panel.component.css @@ -0,0 +1,32 @@ +/*Make tabs not take more width than needed*/ +:host ::ng-deep .mat-tab-label { + min-width: 0 !important; +} + +mat-tab-group { + overflow: auto; + padding: 0 10px; +} + +.panel-title { + font-size: x-large; + text-align: center; +} +.panel-title span { + font-size: medium; +} + +/*prevents small scrolling space around tab content*/ +:host ::ng-deep .mat-tab-body-wrapper {height: 100%} + +:host ::ng-deep .mat-form-field { + margin-bottom: -10px; +} + +:host ::ng-deep mat-checkbox { + margin-bottom: 5px; +} + +:host ::ng-deep .wide-form-field { + width: 100%; +} diff --git a/projects/editor/src/app/components/properties-panel/element-properties-panel.component.html b/projects/editor/src/app/components/properties-panel/element-properties-panel.component.html index 5ea2dde8715b441e54844f0a0fceeae7c0b2b080..d7a996b467a72d2ed9fba2559822f0c216f2ecac 100644 --- a/projects/editor/src/app/components/properties-panel/element-properties-panel.component.html +++ b/projects/editor/src/app/components/properties-panel/element-properties-panel.component.html @@ -1,6 +1,6 @@ -<div *ngIf="selectedElements.length > 0" - class="properties-panel" - fxLayout="column" fxLayoutAlign="space-between stretch"> +<div *ngIf="selectedElements.length > 0 && combinedProperties" + class="properties-panel" fxFlexFill + fxLayout="column" fxLayoutAlign="start stretch"> <div class="panel-title" fxLayout="column"> <ng-container *ngIf="combinedProperties.type"> {{'toolbox.' + combinedProperties.type | translate}} @@ -10,8 +10,10 @@ <i>Mehrfachauswahl</i> </ng-container> </div> - <mat-divider [style.margin-bottom]="0"></mat-divider> - <mat-tab-group mat-stretch-tabs> + + <mat-divider></mat-divider> + + <mat-tab-group mat-stretch-tabs fxFlex> <mat-tab> <ng-template mat-tab-label> <mat-icon class="example-tab-icon">build</mat-icon> @@ -28,7 +30,7 @@ </ng-template> <aspect-element-postion-properties [dimensions]="{ width: combinedProperties.width, height: combinedProperties.height, - dynamicWidth: $any(combinedProperties.dynamicWidth)}" + dynamicWidth: $any(combinedProperties).dynamicWidth}" [positionProperties]="combinedProperties.position"> </aspect-element-postion-properties> </mat-tab> @@ -37,18 +39,23 @@ <ng-template mat-tab-label> <mat-icon class="example-tab-icon">palette</mat-icon> </ng-template> - <aspect-element-style-properties [styles]="$any(combinedProperties.styling)"> - </aspect-element-style-properties> + <aspect-element-style-properties [styles]="combinedProperties.styling"> + </aspect-element-style-properties> </mat-tab> </mat-tab-group> <div fxLayout="column" class="button-group"> <mat-divider></mat-divider> - <button mat-raised-button + <button mat-raised-button [disabled]="selectedElements.length > 1 || + combinedProperties.type == 'toggle-button' || + combinedProperties.type == 'drop-list-simple' || + combinedProperties.type == 'text-field-simple'" (click)="duplicateElement()"> {{'propertiesPanel.duplicateElement' | translate }} </button> - <button mat-raised-button color="warn" + <button mat-raised-button color="warn" [disabled]="combinedProperties.type == 'toggle-button' || + combinedProperties.type == 'drop-list-simple' || + combinedProperties.type == 'text-field-simple'" (click)="deleteElement()"> {{'propertiesPanel.deleteElement' | translate }} </button> diff --git a/projects/editor/src/app/components/properties-panel/element-properties-panel.component.ts b/projects/editor/src/app/components/properties-panel/element-properties-panel.component.ts index 03c9afae47427e9e1cd3889f020204a79b05c908..323a3c0ac2c2f22e26633f5a8812b5d430163856 100644 --- a/projects/editor/src/app/components/properties-panel/element-properties-panel.component.ts +++ b/projects/editor/src/app/components/properties-panel/element-properties-panel.component.ts @@ -5,27 +5,22 @@ import { DomSanitizer } from '@angular/platform-browser'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; +import { MessageService } from 'common/services/message.service'; +import { TextLabel, UIElement } from 'common/models/elements/element'; +import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row'; import { UnitService } from '../../services/unit.service'; import { SelectionService } from '../../services/selection.service'; -import { MessageService } from 'common/services/message.service'; -import { DragNDropValueObject, TextImageLabel, UIElement } from 'common/models/elements/element'; + +export type CombinedProperties = UIElement & { idList?: string[] }; @Component({ selector: 'aspect-element-properties', templateUrl: './element-properties-panel.component.html', - styles: [ - '.button-group button {margin: 5px 10px;}', - 'mat-divider {margin: 20px; border-top-width: 9px; border-top-style: dotted;}', - '.properties-panel {height: 100%; padding-bottom: 20px}', - '.properties-panel .mat-tab-group {height: 100%; overflow: auto; padding: 0 15px;}', - ':host ::ng-deep .mat-tab-body-wrapper {height: 100%}', - '.panel-title {font-size: x-large; text-align: center;}', - '.panel-title span {font-size: medium;}' - ] + styleUrls: ['./element-properties-panel.component.css'] }) export class ElementPropertiesPanelComponent implements OnInit, OnDestroy { - selectedElements!: UIElement[]; - combinedProperties: UIElement = {} as UIElement; + selectedElements: UIElement[] = []; + combinedProperties: CombinedProperties | undefined; private ngUnsubscribe = new Subject<void>(); constructor(private selectionService: SelectionService, public unitService: UnitService, @@ -39,7 +34,7 @@ export class ElementPropertiesPanelComponent implements OnInit, OnDestroy { .subscribe( () => { this.combinedProperties = - ElementPropertiesPanelComponent.createCombinedProperties(this.selectedElements) as UIElement; + ElementPropertiesPanelComponent.createCombinedProperties(this.selectedElements); } ); this.selectionService.selectedElements @@ -48,14 +43,14 @@ export class ElementPropertiesPanelComponent implements OnInit, OnDestroy { (selectedElements: UIElement[]) => { this.selectedElements = selectedElements; this.combinedProperties = - ElementPropertiesPanelComponent.createCombinedProperties(this.selectedElements) as UIElement; + ElementPropertiesPanelComponent.createCombinedProperties(this.selectedElements); } ); } - static createCombinedProperties(elements: UIElement[]): Partial<UIElement> { + static createCombinedProperties(elements: UIElement[]): CombinedProperties | undefined { if (elements.length > 0) { - const combinedProperties: Partial<UIElement> & { id: string | string[] } = { ...elements[0], id: elements[0].id }; + const combinedProperties = { ...elements[0], idList: [elements[0].id] } as CombinedProperties; for (let elementCounter = 1; elementCounter < elements.length; elementCounter++) { const elementToMerge = elements[elementCounter]; @@ -64,14 +59,14 @@ export class ElementPropertiesPanelComponent implements OnInit, OnDestroy { if (typeof combinedProperties[property] === 'object' && !Array.isArray(combinedProperties[property]) && combinedProperties[property] !== null) { - (combinedProperties[property] as UIElement) = + combinedProperties[property] = ElementPropertiesPanelComponent.createCombinedProperties( [(combinedProperties[property] as UIElement), (elementToMerge[property] as UIElement)] - ) as UIElement; + ); } else if (JSON.stringify(combinedProperties[property]) !== JSON.stringify(elementToMerge[property])) { if (property === 'id') { - (combinedProperties.id as string[]).push(elementToMerge.id as string); + combinedProperties.idList?.push(elementToMerge.id as string); } else { combinedProperties[property] = null; } @@ -81,14 +76,17 @@ export class ElementPropertiesPanelComponent implements OnInit, OnDestroy { } }); } + // replace rows array to trigger change detection for options panel + combinedProperties.rows = combinedProperties.rows ? [...combinedProperties.rows as LikertRowElement[]] : undefined; + // console.log('combi', combinedProperties); return combinedProperties; } - return {}; + return undefined; } updateModel(property: string, value: string | number | boolean | string[] | - TextImageLabel[] | DragNDropValueObject[] | null, + TextLabel | TextLabel[] | LikertRowElement[] | null, isInputValid: boolean | null = true): void { if (isInputValid) { this.unitService.updateElementsProperty(this.selectedElements, property, value); diff --git a/projects/editor/src/app/components/properties-panel/likert-row-label.pipe.ts b/projects/editor/src/app/components/properties-panel/likert-row-label.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..7aa2a01a352724b69a642f3c52d22391d7d4a540 --- /dev/null +++ b/projects/editor/src/app/components/properties-panel/likert-row-label.pipe.ts @@ -0,0 +1,13 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row'; +import { TextImageLabel } from 'common/models/elements/element'; + +@Pipe({ + name: 'LikertRowLabel' +}) +export class LikertRowLabelPipe implements PipeTransform { + // eslint-disable-next-line class-methods-use-this + transform(rows: LikertRowElement[]): TextImageLabel[] { + return rows.map(row => row.rowLabel); + } +} diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.css b/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.css index 1a581131d4f533a46cd6b2e8a289a5da07d9b83f..7b3c282c87ba1cae9103c55f390efb423f915b10 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.css +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.css @@ -1,25 +1,34 @@ -.list-items { - padding: 5px 10px; - border-bottom: solid 1px #ccc; - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between +aspect-input-element-properties { + margin-bottom: 10px; } -::ng-deep aspect-element-properties .mat-tab-label { - min-width: 0 !important; +button:not(.media-src-button):not(.mat-mini-fab):not(.mat-icon-button) { + width: 95% } -::ng-deep aspect-element-properties .mat-tab-group { - padding: 15px; +.mat-fab, .mat-raised-button { + margin-bottom: 10px; } -:host ::ng-deep mat-button-toggle { - width: 100%; - height: 100% +:host ::ng-deep fieldset { + margin-bottom: 15px; } -button { - margin-bottom: 15px; +:host ::ng-deep .drop-list { + border: solid 1px #ccc; + border-radius: 4px; +} + +:host ::ng-deep .drop-list:empty { + border: none; +} + +:host ::ng-deep .option-draggable { + border-bottom: solid 1px #ccc; + cursor: move; + padding: 3px 5px; +} + +:host ::ng-deep .option-draggable:last-child { + border: none; } diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.html b/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.html index 8765619a0905bc3708326556a1ba6af993c2ce43..f68424a4fce91ee5c1050860fbfa9f36fa2f0386 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.html +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.html @@ -6,60 +6,56 @@ <input matInput type="text" disabled *ngIf="selectedElements.length > 1" [value]="'Muss eindeutig sein'"> </mat-form-field> - <mat-form-field *ngIf="combinedProperties.label !== undefined" appearance="fill"> + <aspect-input-element-properties [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-input-element-properties> + + <aspect-preset-value-properties [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-preset-value-properties> + + <mat-form-field *ngIf="combinedProperties.label !== undefined && combinedProperties.required === undefined" + appearance="fill"> <mat-label>{{'propertiesPanel.label' | translate }}</mat-label> <input matInput type="text" [value]="$any(combinedProperties.label)" (input)="updateModel.emit({property: 'label', value: $any($event.target).value })"> </mat-form-field> - <ng-container *ngIf="combinedProperties.document"> - <button (click)="unitService.showDefaultEditDialog(selectedElements[0])"> - <mat-icon>build_circle</mat-icon> - </button> - </ng-container> - - <ng-container *ngIf="combinedProperties.player"> - <button (click)="unitService.showDefaultEditDialog(selectedElements[0])"> - <mat-icon>build_circle</mat-icon> - </button> - </ng-container> - - <aspect-text-properties-field-set [combinedProperties]="combinedProperties" - (updateModel)="updateModel.emit($event)"> - </aspect-text-properties-field-set> - - <mat-checkbox *ngIf="combinedProperties.strikeOtherOptions !== undefined" - [checked]="$any(combinedProperties.strikeOtherOptions)" - (change)="updateModel.emit({ property: 'strikeOtherOptions', value: $event.checked })"> - {{'propertiesPanel.strikeOtherOptions' | translate }} - </mat-checkbox> - <mat-checkbox *ngIf="combinedProperties.allowUnset !== undefined" - [checked]="$any(combinedProperties.allowUnset)" - (change)="updateModel.emit({ property: 'allowUnset', value: $event.checked })"> - {{'propertiesPanel.allowUnset' | translate }} - </mat-checkbox> - - <mat-checkbox *ngIf="combinedProperties.readOnly !== undefined" - [checked]="$any(combinedProperties.readOnly)" - (change)="updateModel.emit({ property: 'readOnly', value: $event.checked })"> - {{'propertiesPanel.readOnly' | translate }} - </mat-checkbox> - <mat-checkbox *ngIf="combinedProperties.required !== undefined" - [checked]="$any(combinedProperties.required)" - (change)="updateModel.emit({ property: 'required', value: $event.checked })"> - {{'propertiesPanel.requiredField' | translate }} - </mat-checkbox> - <mat-form-field *ngIf="combinedProperties.required" - appearance="fill"> - <mat-label>{{'propertiesPanel.requiredWarnMessage' | translate }}</mat-label> - <input matInput type="text" [value]="$any(combinedProperties.requiredWarnMessage)" - (input)="updateModel.emit({ property: 'requiredWarnMessage', value: $any($event.target).value })"> + <mat-form-field *ngIf="combinedProperties.label2 !== undefined" appearance="fill"> + <mat-label>{{'propertiesPanel.label2' | translate }}</mat-label> + <input matInput type="text" [value]="$any(combinedProperties.label2)" + (input)="updateModel.emit({property: 'label2', value: $any($event.target).value })"> </mat-form-field> <aspect-options-field-set [combinedProperties]="combinedProperties" (updateModel)="updateModel.emit($event)"> </aspect-options-field-set> + <aspect-border-properties [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"></aspect-border-properties> + + <button *ngIf="combinedProperties.document" + mat-raised-button fxFlexAlign="center" + (click)="unitService.showDefaultEditDialog(selectedElements[0])"> + Text editieren + </button> + + <button *ngIf="combinedProperties.src" + mat-fab color="primary" fxFlexAlign="center" class="media-src-button" + [matTooltip]="'Medienquelle ändern'" [matTooltipPosition]="'right'" + (click)="changeMediaSrc(combinedProperties.type)"> + <mat-icon>upload_file</mat-icon> + </button> + <button *ngIf="combinedProperties.player" + mat-raised-button fxFlexAlign="center" + (click)="unitService.showDefaultEditDialog(selectedElements[0])"> + Medienoptionen + </button> + + <aspect-text-properties-field-set [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-text-properties-field-set> + <mat-form-field *ngIf="combinedProperties.alignment" appearance="fill"> <mat-label>{{'propertiesPanel.alignment' | translate }}</mat-label> <mat-select [value]="combinedProperties.alignment" @@ -71,6 +67,10 @@ </mat-select> </mat-form-field> + <aspect-select-properties [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-select-properties> + <mat-checkbox *ngIf="combinedProperties.resizeEnabled !== undefined" [checked]="$any(combinedProperties.resizeEnabled)" (change)="updateModel.emit({ property: 'resizeEnabled', value: $event.checked })"> @@ -88,50 +88,13 @@ (updateModel)="updateModel.emit($event)"> </aspect-button-properties> - <mat-form-field *ngIf="combinedProperties.options !== undefined && !combinedProperties.connectedTo" - appearance="fill"> - <mat-label>{{'preset' | translate }}</mat-label> - <mat-select [value]="combinedProperties.value" - (selectionChange)="updateModel.emit({ property: 'value', value: $event.value })"> - <mat-option [value]="null">{{'propertiesPanel.undefined' | translate }}</mat-option> - <mat-option *ngFor="let option of $any(combinedProperties.options); let i = index" [value]="i"> - {{option}} (Index: {{i}}) - </mat-option> - </mat-select> - </mat-form-field> - - <mat-form-field *ngIf="combinedProperties.richTextOptions !== undefined && !combinedProperties.connectedTo" - appearance="fill"> - <mat-label>{{'preset' | translate }}</mat-label> - <mat-select [value]="combinedProperties.value" - (selectionChange)="updateModel.emit({ property: 'value', value: $event.value })"> - <mat-option [value]="null">{{'propertiesPanel.undefined' | translate }}</mat-option> - <mat-option *ngFor="let option of $any(combinedProperties.richTextOptions); let i = index" [value]="i" - [innerHTML]="option + ' (Index: ' + i + ')'"> - </mat-option> - </mat-select> - </mat-form-field> - - <!-- This is for radio with images--> - <mat-form-field *ngIf="combinedProperties.columns !== undefined && combinedProperties.rows === undefined" - appearance="fill"> - <mat-label>{{'preset' | translate }}</mat-label> - <mat-select [value]="combinedProperties.value" - (selectionChange)="updateModel.emit({ property: 'value', value: $event.value })"> - <mat-option [value]="null">{{'propertiesPanel.undefined' | translate }}</mat-option> - <mat-option *ngFor="let column of $any(combinedProperties.columns); let i = index" [value]="i"> - {{column.name}} (Index: {{i}}) - </mat-option> - </mat-select> - </mat-form-field> - <aspect-slider-properties [combinedProperties]="combinedProperties" (updateModel)="updateModel.emit($event)"> </aspect-slider-properties> - <ng-container *ngIf="combinedProperties.value === true || combinedProperties.value === false"> + <ng-container *ngIf="combinedProperties.type === 'checkbox'"> {{'preset' | translate }} - <mat-button-toggle-group [value]="combinedProperties.value" + <mat-button-toggle-group [value]="combinedProperties.value" fxFlexAlign="start" (change)="updateModel.emit({ property: 'value', value: $event.value })"> <mat-button-toggle [value]="true">{{'propertiesPanel.true' | translate }}</mat-button-toggle> <mat-button-toggle [value]="false">{{'propertiesPanel.false' | translate }}</mat-button-toggle> @@ -147,96 +110,21 @@ isInputValid: presetValue.valid})"> </mat-form-field> - <mat-form-field *ngIf="combinedProperties.type === 'text-area'" - appearance="fill"> - <mat-label>{{'preset' | translate }}</mat-label> - <textarea matInput type="text" - [value]="$any(combinedProperties.value)" - (input)="updateModel.emit({ property: 'value', value: $any($event.target).value })"> - </textarea> - </mat-form-field> + <aspect-text-field-element-properties [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-text-field-element-properties> - <mat-form-field *ngIf="combinedProperties.type === 'text-field'" + <mat-form-field *ngIf="combinedProperties.firstColumnSizeRatio != null" + matTooltip="{{'propertiesPanel.firstColumnSizeRatioExplanation' | translate }}" appearance="fill"> - <mat-label>{{'preset' | translate }}</mat-label> - <input matInput type="text" [value]="$any(combinedProperties.value)" - (input)="updateModel.emit({property: 'value', value: $any($event.target).value })"> - </mat-form-field> - - <aspect-input-element-properties [combinedProperties]="combinedProperties" - (updateModel)="updateModel.emit($event)"> - </aspect-input-element-properties> - - <mat-form-field disabled="true" *ngIf="combinedProperties.rows !== undefined"> - <ng-container> - <mat-label>{{'rows' | translate }}</mat-label> - <div class="drop-list" cdkDropList [cdkDropListData]="combinedProperties.rows" - (cdkDropListDropped)="moveListValue('rows', $any($event))"> - <div *ngFor="let row of $any(combinedProperties.rows); let i = index" cdkDrag - class="list-items" fxLayout="row" fxLayoutAlign="end center"> - <div fxFlex="70"> - {{row.rowLabel.text}} - </div> - <img [src]="row.rowLabel.imgSrc" - [style.object-fit]="'scale-down'" - [style.height.px]="40"> - <button mat-icon-button color="primary" - (click)="editLikertRow(i)"> - <mat-icon>build</mat-icon> - </button> - <button mat-icon-button color="primary" - (click)="removeListValue('rows', row)"> - <mat-icon>clear</mat-icon> - </button> - </div> - </div> - </ng-container> - <div fxLayout="row" fxLayoutAlign="center center"> - <button mat-icon-button matPrefix - (click)="addLikertRow(newRow.value); newRow.select()"> - <mat-icon>add</mat-icon> - </button> - <input #newRow matInput type="text" placeholder="Fragetext" - (keyup.enter)="addLikertRow(newRow.value); newRow.select()"> - </div> - </mat-form-field> - - <mat-form-field disabled="true" *ngIf="combinedProperties.columns !== undefined"> - <ng-container> - <mat-label>{{'columns' | translate }}</mat-label> - <div class="drop-list" cdkDropList [cdkDropListData]="combinedProperties.columns" - (cdkDropListDropped)="moveListValue('columns', $any($event))"> - <div *ngFor="let column of $any(combinedProperties.columns); let i = index" cdkDrag - class="list-items" fxLayout="row" fxLayoutAlign="end center"> - <div fxFlex="70" [innerHTML]="sanitizer.bypassSecurityTrustHtml(column.text)"> - </div> - <img [src]="column.imgSrc" - [style.object-fit]="'scale-down'" - [style.height.px]="40"> - <button mat-icon-button color="primary" - (click)="editColumnOption(i)"> - <mat-icon>build</mat-icon> - </button> - <button mat-icon-button color="primary" - (click)="removeListValue('columns', column)"> - <mat-icon>clear</mat-icon> - </button> - </div> - </div> - </ng-container> - <div fxLayout="row" fxLayoutAlign="center center"> - <button mat-icon-button matPrefix - (click)="addColumn(newColumn.value); newColumn.select()"> - <mat-icon>add</mat-icon> - </button> - <input #newColumn matInput type="text" placeholder="Antworttext" - (keyup.enter)="addColumn(newColumn.value); newColumn.select()"> - </div> + <mat-label>{{'propertiesPanel.firstColumnSizeRatio' | translate }}</mat-label> + <input matInput type="number" [value]="$any(combinedProperties.firstColumnSizeRatio)" + (input)="updateModel.emit({ property: 'firstColumnSizeRatio', value: $any($event.target).value })"> </mat-form-field> - <aspect-image-properties [combinedProperties]="combinedProperties" - (updateModel)="updateModel.emit($event)"> - </aspect-image-properties> + <aspect-scale-and-zoom-properties [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-scale-and-zoom-properties> <mat-checkbox *ngIf="combinedProperties.verticalOrientation !== undefined" [checked]="$any(combinedProperties.verticalOrientation)" @@ -247,12 +135,4 @@ <aspect-drop-list-properties [combinedProperties]="combinedProperties" (updateModel)="updateModel.emit($event)"> </aspect-drop-list-properties> - - <mat-form-field *ngIf="combinedProperties.firstColumnSizeRatio != null" - matTooltip="{{'propertiesPanel.firstColumnSizeRatioExplanation' | translate }}" - appearance="fill" class="mdInput textsingleline"> - <mat-label>{{'propertiesPanel.firstColumnSizeRatio' | translate }}</mat-label> - <input matInput type="number" [value]="$any(combinedProperties.firstColumnSizeRatio)" - (input)="updateModel.emit({ property: 'firstColumnSizeRatio', value: $any($event.target).value })"> - </mat-form-field> </div> diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.ts index e176e705013d2438636f64bc98d772c40b1ba16b..2e3c11fe6b26b66dc4296181ef3bab43a0ff9992 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.ts @@ -2,16 +2,18 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; import { CdkDragDrop } from '@angular/cdk/drag-drop/drag-events'; import { moveItemInArray } from '@angular/cdk/drag-drop'; +import { + InputElementValue, TextLabel, TextImageLabel, UIElement +} from 'common/models/elements/element'; +import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row'; +import { FileService } from 'common/services/file.service'; +import { CombinedProperties } from 'editor/src/app/components/properties-panel/element-properties-panel.component'; import { UnitService } from '../../../services/unit.service'; import { SelectionService } from '../../../services/selection.service'; import { DialogService } from '../../../services/dialog.service'; -import { DomSanitizer } from '@angular/platform-browser'; -import { DragNDropValueObject, InputElementValue, TextImageLabel, UIElement } from 'common/models/elements/element'; -import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row'; -import { LikertElement } from 'common/models/elements/compound-elements/likert/likert'; -import { IDManager } from 'common/util/id-manager'; @Component({ selector: 'aspect-element-model-properties-component', @@ -19,11 +21,11 @@ import { IDManager } from 'common/util/id-manager'; styleUrls: ['./element-model-properties.component.css'] }) export class ElementModelPropertiesComponent { - @Input() combinedProperties: UIElement = {} as UIElement; + @Input() combinedProperties!: CombinedProperties; @Input() selectedElements: UIElement[] = []; @Output() updateModel = new EventEmitter<{ property: string; - value: InputElementValue | TextImageLabel[] | DragNDropValueObject[], + value: InputElementValue | TextImageLabel[] | LikertRowElement[] | TextLabel[], isInputValid?: boolean | null }>(); @@ -44,12 +46,6 @@ export class ElementModelPropertiesComponent { this.updateModel.emit({ property: property, value: event.container.data }); } - removeListValue(property: string, option: any): void { - const valueList = this.combinedProperties[property] as string[] | LikertRowElement[] | TextImageLabel[]; - valueList.splice(valueList.indexOf(option), 1); - this.updateModel.emit({ property: property, value: valueList }); - } - async editTextOption(property: string, optionIndex: number): Promise<void> { const oldOptions = this.selectionService.getSelectedElements()[0][property] as string[]; await this.dialogService.showTextEditDialog(oldOptions[optionIndex]) @@ -61,75 +57,20 @@ export class ElementModelPropertiesComponent { }); } - async editColumnOption(optionIndex: number): Promise<void> { - const firstElement = (this.selectedElements as LikertElement[])[0]; - await this.dialogService - .showLikertColumnEditDialog(firstElement.columns[optionIndex], - (this.combinedProperties as LikertElement).styling.fontSize) - .subscribe((result: TextImageLabel) => { - if (result) { - firstElement.columns[optionIndex] = result; - this.updateModel.emit({ property: 'columns', value: firstElement.columns }); - } - }); - } - - addColumn(value: string): void { - const column: TextImageLabel = { - text: value, - imgSrc: null, - position: 'above' - }; - (this.combinedProperties.columns as TextImageLabel[]).push(column); - this.updateModel.emit({ property: 'columns', value: this.combinedProperties.columns as TextImageLabel[] }); - } - - addLikertRow(rowLabelText: string): void { - const newRow = new LikertRowElement({ - type: 'likert-row', - rowLabel: { - text: rowLabelText, - imgSrc: null, - position: 'above' - }, - columnCount: (this.combinedProperties.columns as TextImageLabel[]).length - }, IDManager.getInstance()); - (this.combinedProperties.rows as LikertRowElement[]).push(newRow); - this.updateModel.emit({ property: 'rows', value: this.combinedProperties.rows as LikertRowElement[] }); - } - - async editLikertRow(rowIndex: number): Promise<void> { - const row = (this.combinedProperties.rows as LikertRowElement[])[rowIndex] as LikertRowElement; - const columns = this.combinedProperties.columns as TextImageLabel[]; - - await this.dialogService.showLikertRowEditDialog(row, columns) - .subscribe((result: LikertRowElement) => { - if (result) { - if (result.id !== row.id) { - this.unitService.updateElementsProperty( - [row], - 'id', - result.id - ); - } - if (result.rowLabel !== row.rowLabel) { - this.unitService.updateElementsProperty([row], 'rowLabel', result.rowLabel); - } - if (result.value !== row.value) { - this.unitService.updateElementsProperty( - [row], - 'value', - result.value - ); - } - if (result.verticalButtonAlignment !== row.verticalButtonAlignment) { - this.unitService.updateElementsProperty( - [row], - 'verticalButtonAlignment', - result.verticalButtonAlignment - ); - } - } - }); + async changeMediaSrc(elementType: string) { + let mediaSrc = ''; + switch (elementType) { + case 'image': + mediaSrc = await FileService.loadImage(); + break; + case 'audio': + mediaSrc = await FileService.loadAudio(); + break; + case 'video': + mediaSrc = await FileService.loadVideo(); + break; + // no default + } + this.updateModel.emit({ property: 'src', value: mediaSrc }); } } diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/border-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/border-properties.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..19c59bc7d7d3b4f461944b19ed351e4c2eef4269 --- /dev/null +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/border-properties.component.ts @@ -0,0 +1,37 @@ +import { + Component, EventEmitter, Input, Output +} from '@angular/core'; +import { UIElement } from 'common/models/elements/element'; + +@Component({ + selector: 'aspect-border-properties', + template: ` + <div fxLayout="column"> + <mat-checkbox *ngIf="combinedProperties.hasBorderTop !== undefined" + [checked]="$any(combinedProperties).hasBorderTop" + (change)="updateModel.emit({ property: 'hasBorderTop', value: $event.checked })"> + {{'propertiesPanel.hasBorderTop' | translate }} + </mat-checkbox> + <mat-checkbox *ngIf="combinedProperties.hasBorderBottom !== undefined" + [checked]="$any(combinedProperties).hasBorderBottom" + (change)="updateModel.emit({ property: 'hasBorderBottom', value: $event.checked })"> + {{'propertiesPanel.hasBorderBottom' | translate }} + </mat-checkbox> + <mat-checkbox *ngIf="combinedProperties.hasBorderLeft !== undefined" + [checked]="$any(combinedProperties).hasBorderLeft" + (change)="updateModel.emit({ property: 'hasBorderLeft', value: $event.checked })"> + {{'propertiesPanel.hasBorderLeft' | translate }} + </mat-checkbox> + <mat-checkbox *ngIf="combinedProperties.hasBorderRight !== undefined" + [checked]="$any(combinedProperties).hasBorderRight" + (change)="updateModel.emit({ property: 'hasBorderRight', value: $event.checked })"> + {{'propertiesPanel.hasBorderRight' | translate }} + </mat-checkbox> + </div> + ` +}) +export class BorderPropertiesComponent { + @Input() combinedProperties!: UIElement; + @Output() updateModel = + new EventEmitter<{ property: string; value: string | number | boolean | null }>(); +} diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/button-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/button-properties.component.ts index 8db4f5d8a51aa0b2db10488b30e95a7b7826aa3d..8f97aba55f496d5e8e66b83a28b1028894fa0bb1 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/button-properties.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/button-properties.component.ts @@ -1,11 +1,10 @@ import { - Component, EventEmitter, Input, OnInit, Output + Component, EventEmitter, Input, Output } from '@angular/core'; -import { UnitService } from '../../../../services/unit.service'; -import { SelectionService } from '../../../../services/selection.service'; import { FileService } from 'common/services/file.service'; import { UIElement } from 'common/models/elements/element'; -import { ButtonElement } from 'common/models/elements/button/button'; +import { UnitService } from '../../../../services/unit.service'; +import { SelectionService } from '../../../../services/selection.service'; @Component({ selector: 'aspect-button-properties', @@ -14,14 +13,14 @@ import { ButtonElement } from 'common/models/elements/button/button'; <legend>Knopf</legend> <mat-checkbox *ngIf="combinedProperties.asLink !== undefined" - [checked]="combinedButtonElement.asLink" + [checked]="$any(combinedProperties).asLink" (change)="updateModel.emit({ property: 'asLink', value: $event.checked })"> {{'propertiesPanel.asLink' | translate }} </mat-checkbox> <mat-form-field *ngIf="combinedProperties.action !== undefined" appearance="fill"> <mat-label>{{'propertiesPanel.action' | translate }}</mat-label> - <mat-select [value]="combinedButtonElement.action" + <mat-select [value]="combinedProperties.action" (selectionChange)="updateModel.emit({ property: 'action', value: $event.value })"> <mat-option [value]="null"> {{ 'propertiesPanel.none' | translate }} @@ -35,13 +34,13 @@ import { ButtonElement } from 'common/models/elements/button/button'; <mat-form-field appearance="fill"> <mat-label>{{'propertiesPanel.actionParam' | translate }}</mat-label> - <mat-select [disabled]="combinedButtonElement.action === null" - [value]="combinedButtonElement.actionParam" - [matTooltipDisabled]="combinedButtonElement.action !== 'pageNav'" + <mat-select [disabled]="combinedProperties.action === null" + [value]="combinedProperties.actionParam" + [matTooltipDisabled]="combinedProperties.action !== 'pageNav'" [matTooltip]="'propertiesPanel.pageNavSelectionHint' | translate" (selectionChange)="updateModel.emit({ property: 'actionParam', value: $event.value })"> - <ng-container *ngIf="combinedButtonElement.action === 'pageNav'"> + <ng-container *ngIf="combinedProperties.action === 'pageNav'"> <ng-container *ngFor="let page of unitService.unit.pages; index as i"> <mat-option *ngIf="!page.alwaysVisible && selectionService.selectedPageIndex !== i" [value]="i"> @@ -50,7 +49,7 @@ import { ButtonElement } from 'common/models/elements/button/button'; </ng-container> </ng-container> - <ng-container *ngIf="combinedButtonElement.action === 'unitNav'"> + <ng-container *ngIf="combinedProperties.action === 'unitNav'"> <mat-option *ngFor="let option of [undefined, 'previous', 'next', 'first', 'last', 'end']" [value]="option"> {{ 'propertiesPanel.' + option | translate }} @@ -60,40 +59,27 @@ import { ButtonElement } from 'common/models/elements/button/button'; </mat-form-field> <div class="image-panel" (mouseenter)="hoveringImage = true" (mouseleave)="hoveringImage = false"> - <button *ngIf="combinedButtonElement.imageSrc === null || hoveringImage" + <button *ngIf="combinedProperties.imageSrc === null || hoveringImage" class="add-image-button" mat-raised-button (click)="loadImage()">{{'loadImage' | translate }}</button> - <button *ngIf="combinedButtonElement.imageSrc !== null && hoveringImage" + <button *ngIf="combinedProperties.imageSrc !== null && hoveringImage" class="remove-image-button" mat-raised-button (click)="removeImage()">{{'removeImage' | translate }}</button> - <img *ngIf="combinedButtonElement.imageSrc" - [src]="combinedButtonElement.imageSrc"> + <img *ngIf="combinedProperties.imageSrc" + [src]="combinedProperties.imageSrc"> </div> </fieldset> - `, - styles: [ - 'mat-checkbox {margin-bottom: 10px;}', - 'mat-form-field {width: 100%;}', - '.image-panel {width: 250px; height: 150px; border: 1px solid; text-align: center; position: relative;}', - '.image-panel .add-image-button {position: absolute; left: 50%; top: 35%; transform: translate(-50%, -35%);}', - '.image-panel .remove-image-button {position: absolute; left: 50%; top: 70%; transform: translate(-50%, -70%);}', - '.image-panel img {width:100%; height:100%;}' - ] + ` }) -export class ButtonPropertiesComponent implements OnInit { +export class ButtonPropertiesComponent { @Input() combinedProperties!: UIElement; @Output() updateModel = new EventEmitter<{ property: string; value: string | number | boolean | null, isInputValid?: boolean | null }>(); - combinedButtonElement: ButtonElement = {} as ButtonElement; hoveringImage = false; constructor(public unitService: UnitService, public selectionService: SelectionService) { } - ngOnInit(): void { - this.combinedButtonElement = this.combinedProperties as ButtonElement; - } - async loadImage(): Promise<void> { this.updateModel.emit({ property: 'imageSrc', value: await FileService.loadImage() }); } diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/drop-list-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/drop-list-properties.component.ts index dcf7b37f87d41e71e3e2d40237c3dcc0ffe9ff3e..b0ada6e195600aecb61a6111716339eaa5400cb3 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/drop-list-properties.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/drop-list-properties.component.ts @@ -1,63 +1,38 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { UnitService } from '../../../../services/unit.service'; -import { SelectionService } from '../../../../services/selection.service'; -import { DialogService } from '../../../../services/dialog.service'; -import { MessageService } from 'common/services/message.service'; import { TranslateService } from '@ngx-translate/core'; -import { CdkDragDrop } from '@angular/cdk/drag-drop/drag-events'; import { moveItemInArray } from '@angular/cdk/drag-drop'; -import { DragNDropValueObject } from 'common/models/elements/element'; +import { MessageService } from 'common/services/message.service'; +import { DragNDropValueObject, TextImageLabel } from 'common/models/elements/element'; import { IDManager } from 'common/util/id-manager'; +import { CombinedProperties } from 'editor/src/app/components/properties-panel/element-properties-panel.component'; +import { UnitService } from '../../../../services/unit.service'; +import { SelectionService } from '../../../../services/selection.service'; +import { DialogService } from '../../../../services/dialog.service'; @Component({ selector: 'aspect-drop-list-properties', template: ` - <fieldset *ngIf="combinedProperties.type === 'drop-list' || - combinedProperties.type === 'drop-list-simple'"> - <legend>Ablegeliste</legend> - - <div class="value-list-container"> - <mat-label>{{'preset' | translate }}</mat-label> - <div class="drop-list" cdkDropList [cdkDropListData]="combinedProperties.value" - (cdkDropListDropped)="moveListValue($any($event))"> - <div *ngFor="let value of $any(combinedProperties.value); let i = index" cdkDrag - class="list-items" fxLayout="row" fxLayoutAlign="end center"> - <div fxFlex="70" class="draggable-element-label"> - {{value.stringValue}} ({{value.id}}) - </div> - <img [src]="value.imgSrcValue" - [style.object-fit]="'scale-down'" - [style.height.px]="40"> - <button mat-icon-button color="primary" - (click)="editDropListOption(i)"> - <mat-icon>build</mat-icon> - </button> - <button mat-icon-button color="primary" - (click)="removeListValue('value', i)"> - <mat-icon>clear</mat-icon> - </button> - </div> - </div> - <div fxLayout="row" fxLayoutAlign="center center" class="text-area-container"> - <textarea matInput type="text" - #newValue rows="2" - (keyup.enter)="addDropListOption(newValue.value); newValue.select()"></textarea> - <button mat-icon-button - (click)="addDropListOption(newValue.value); newValue.select()"> - <mat-icon>add</mat-icon> - </button> - </div> - </div> - - <mat-form-field appearance="fill" *ngIf="combinedProperties.connectedTo !== null" + <div *ngIf="combinedProperties.type === 'drop-list' || + combinedProperties.type === 'drop-list-simple'" + fxLayout="column"> + <aspect-option-list-panel [title]="'preset'" [textFieldLabel]="'Neue Option'" + [itemList]="$any(combinedProperties.value)" + (addItem)="addOption($event)" + (removeItem)="removeOption($event)" + (changedItemOrder)="moveOption('value', $event)" + (editItem)="editOption($event)"> + </aspect-option-list-panel> + + <mat-form-field *ngIf="combinedProperties.connectedTo !== null" + class="wide-form-field" appearance="fill" (click)="generateValidDropLists()"> <mat-label>{{'propertiesPanel.connectedDropLists' | translate }}</mat-label> <mat-select multiple [ngModel]="combinedProperties.connectedTo" (ngModelChange)="toggleConnectedDropList($event)"> <mat-select-trigger> - {{'propertiesPanel.connectedDropLists' | translate }} ({{combinedProperties.connectedTo.length}}) + {{'propertiesPanel.connectedDropLists' | translate }} ({{$any(combinedProperties.connectedTo).length}}) </mat-select-trigger> <mat-option *ngFor="let id of dropListIDs" [value]="id"> {{id}} @@ -94,28 +69,20 @@ import { IDManager } from 'common/util/id-manager'; (change)="updateModel.emit({ property: 'highlightReceivingDropList', value: $event.checked })"> {{'propertiesPanel.highlightReceivingDropList' | translate }} </mat-checkbox> - <mat-form-field *ngIf="combinedProperties.highlightReceivingDropList" - appearance="fill" class="mdInput textsingleline"> + <mat-form-field appearance="fill" class="mdInput textsingleline"> <mat-label>{{'propertiesPanel.highlightReceivingDropListColor' | translate }}</mat-label> - <input matInput type="text" [value]="$any(combinedProperties.highlightReceivingDropListColor)" + <input matInput type="text" + [disabled]="!combinedProperties.highlightReceivingDropList" + [value]="$any(combinedProperties.highlightReceivingDropListColor)" (input)="updateModel.emit({ property: 'highlightReceivingDropListColor', value: $any($event.target).value })"> </mat-form-field> - </fieldset> - `, - styles: [ - 'mat-form-field {width: 100%;}', - '.draggable-element-label {overflow-wrap: anywhere;}', - 'mat-select {height: 100%;}', - '.text-area-container {background-color: lightgray; margin-bottom: 15px;}', - '.value-list-container {background-color: rgba(0,0,0,.04);}', - '.text-area-container button {border: 1px solid gray;}', - '.value-list-container mat-label {font-size: large;}' - ] + </div> + ` }) export class DropListPropertiesComponent { - @Input() combinedProperties!: any; + @Input() combinedProperties!: CombinedProperties; @Output() updateModel = new EventEmitter<{ property: string; value: string | number | boolean | string[] | DragNDropValueObject[], @@ -130,18 +97,34 @@ export class DropListPropertiesComponent { private messageService: MessageService, private translateService: TranslateService) { } - addDropListOption(value: string): void { + notifyListChange(changedList: DragNDropValueObject[]): void { + this.updateModel.emit({ property: 'value', value: changedList }); + } + + addOption(value: string): void { this.updateModel.emit({ property: 'value', value: [ ...this.combinedProperties.value as DragNDropValueObject[], - { stringValue: value, id: this.unitService.getNewValueID() } // TODO direkt IDService + { + text: value, + imgSrc: null, + imgPosition: 'above', + id: this.unitService.getNewValueID() + } ] }); } - async editDropListOption(optionIndex: number): Promise<void> { - const oldOptions = this.combinedProperties.value; + moveOption(property: string, indices: { previousIndex: number, currentIndex: number }): void { + moveItemInArray(this.combinedProperties[property] as TextImageLabel[], + indices.previousIndex, + indices.currentIndex); + this.updateModel.emit({ property: property, value: this.combinedProperties[property] as DragNDropValueObject[] }); + } + + async editOption(optionIndex: number): Promise<void> { + const oldOptions: DragNDropValueObject[] = this.combinedProperties.value as DragNDropValueObject[]; await this.dialogService.showDropListOptionEditDialog(oldOptions[optionIndex]) .subscribe((result: DragNDropValueObject) => { @@ -156,15 +139,10 @@ export class DropListPropertiesComponent { }); } - moveListValue(event: CdkDragDrop<string[]>): void { - moveItemInArray(event.container.data, event.previousIndex, event.currentIndex); - this.updateModel.emit({ property: 'value', value: event.container.data }); - } - - removeListValue(property: string, optionIndex: number): void { - const valueList = this.combinedProperties[property] as DragNDropValueObject[]; + removeOption(optionIndex: number): void { + const valueList = this.combinedProperties.value as DragNDropValueObject[]; valueList.splice(optionIndex, 1); - this.updateModel.emit({ property: property, value: valueList }); + this.updateModel.emit({ property: 'value', value: valueList }); } toggleConnectedDropList(connectedDropListList: string[]) { @@ -176,6 +154,6 @@ export class DropListPropertiesComponent { generateValidDropLists() { this.dropListIDs = this.unitService.getDropListElementIDs() - .filter(dropListID => !this.combinedProperties.id.includes(dropListID)); + .filter(dropListID => !this.combinedProperties.idList!.includes(dropListID)); } } diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/image-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/image-properties.component.ts deleted file mode 100644 index d5840779fb1199d2c83b4fb43ab34c355ed5f410..0000000000000000000000000000000000000000 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/image-properties.component.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { - Component, EventEmitter, Input, Output -} from '@angular/core'; - -@Component({ - selector: 'aspect-image-properties', - template: ` - <mat-checkbox *ngIf="combinedProperties.scale !== undefined" - [checked]="$any(combinedProperties.scale)" - (change)="updateModel.emit({ property: 'scale', value: $event.checked })"> - {{'propertiesPanel.scale' | translate }} - </mat-checkbox> - - <mat-checkbox *ngIf="combinedProperties.magnifier !== undefined" - [checked]="$any(combinedProperties.magnifier)" - (change)="updateModel.emit({ property: 'magnifier', value: $event.checked })"> - {{'propertiesPanel.magnifier' | translate }} - </mat-checkbox> - <mat-form-field *ngIf="combinedProperties.magnifier" appearance="fill"> - <mat-label>{{'propertiesPanel.magnifierSize' | translate }} in px</mat-label> - <input matInput type="number" #magnifierSize="ngModel" min="0" - [ngModel]="combinedProperties.magnifierSize" - (ngModelChange)="updateModel.emit({ - property: 'magnifierSize', - value: $event, - isInputValid: magnifierSize.valid})"> - </mat-form-field> - - <ng-container *ngIf="combinedProperties.magnifier"> - {{'propertiesPanel.magnifierZoom' | translate }} - <mat-slider min="1" max="3" step="0.1" [ngModel]="combinedProperties.magnifierZoom" - (change)="updateModel.emit({ property: 'magnifierZoom', value: $event.value })"> - </mat-slider> - <div *ngIf="combinedProperties.magnifier"> - {{combinedProperties.magnifierZoom}} - </div> - </ng-container> - `, - styles: [ - ] -}) -export class ImagePropertiesComponent { - @Input() combinedProperties!: any; - @Output() updateModel = - new EventEmitter<{ property: string; value: string | number | boolean | null, isInputValid?: boolean | null }>(); -} diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/input-element-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/input-element-properties.component.ts index aa0616e85263b7a9967ab5e40b8c8f48cef522de..ad5edf1c3e334ff4bf0617595faad15ceeaa69fd 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/input-element-properties.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/input-element-properties.component.ts @@ -1,122 +1,42 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { + Component, EventEmitter, Input, Output +} from '@angular/core'; +import { UIElement } from 'common/models/elements/element'; @Component({ selector: 'aspect-input-element-properties', template: ` - <mat-form-field *ngIf="combinedProperties.appearance !== undefined" appearance="fill"> - <mat-label>{{'propertiesPanel.appearance' | translate }}</mat-label> - <mat-select [value]="combinedProperties.appearance" - (selectionChange)="updateModel.emit({ property: 'appearance', value: $event.value })"> - <mat-option *ngFor="let option of [{displayValue: 'fill', value: 'fill'}, - {displayValue: 'outline', value: 'outline'}]" - [value]="option.value"> - {{'propertiesPanel.' + option.displayValue | translate}} - </mat-option> - </mat-select> - </mat-form-field> - - <mat-form-field *ngIf="combinedProperties.minLength !== undefined" appearance="fill"> - <mat-label>{{'propertiesPanel.minLength' | translate }}</mat-label> - <input matInput type="number" #minLength="ngModel" min="0" - [ngModel]="combinedProperties.minLength" - (ngModelChange)="updateModel.emit({ - property: 'minLength', - value: $event, - isInputValid: minLength.valid })"> - </mat-form-field> - <mat-form-field *ngIf="combinedProperties.minLength && - $any(combinedProperties.minLength) > 0" - appearance="fill"> - <mat-label>{{'propertiesPanel.minLengthWarnMessage' | translate }}</mat-label> - <input matInput type="text" [value]="$any(combinedProperties.minLengthWarnMessage)" - (input)="updateModel.emit({ property: 'minLengthWarnMessage', value: $any($event.target).value })"> - </mat-form-field> - - <mat-form-field *ngIf="combinedProperties.maxLength !== undefined" appearance="fill"> - <mat-label>{{'propertiesPanel.maxLength' | translate }}</mat-label> - <input matInput type="number" #maxLength="ngModel" min="0" - [ngModel]="combinedProperties.maxLength" - (ngModelChange)="updateModel.emit({ - property: 'maxLength', - value: $event, - isInputValid: maxLength.valid })"> - </mat-form-field> - <mat-form-field *ngIf="combinedProperties.maxLength && - $any(combinedProperties.maxLength) > 0" - appearance="fill"> - <mat-label>{{'propertiesPanel.maxLengthWarnMessage' | translate }}</mat-label> - <input matInput type="text" [value]="$any(combinedProperties.maxLengthWarnMessage)" - (input)="updateModel.emit({ property: 'maxLengthWarnMessage', value: $any($event.target).value })"> - </mat-form-field> - - <mat-form-field *ngIf="combinedProperties.pattern !== undefined" appearance="fill"> - <mat-label>{{'propertiesPanel.pattern' | translate }}</mat-label> - <input matInput [value]="$any(combinedProperties.pattern)" - (input)="updateModel.emit({ property: 'pattern', value: $any($event.target).value })"> - </mat-form-field> - <mat-form-field *ngIf="combinedProperties.pattern && $any(combinedProperties.pattern) !== ''" - appearance="fill" - matTooltip="Angabe als regulärer Ausdruck."> - <mat-label>{{'propertiesPanel.patternWarnMessage' | translate }}</mat-label> - <input matInput type="text" [value]="$any(combinedProperties.patternWarnMessage)" - (input)="updateModel.emit({ property: 'patternWarnMessage', value: $any($event.target).value })"> - </mat-form-field> - - <mat-checkbox *ngIf="combinedProperties.clearable !== undefined" - [checked]="$any(combinedProperties.clearable)" - (change)="updateModel.emit({ property: 'clearable', value: $event.checked })"> - {{'propertiesPanel.clearable' | translate }} - </mat-checkbox> - - <mat-form-field *ngIf="combinedProperties.inputAssistancePreset !== undefined" appearance="fill"> - <mat-label>{{'propertiesPanel.inputAssistance' | translate }}</mat-label> - <mat-select [value]="combinedProperties.inputAssistancePreset" - (selectionChange)="updateModel.emit({ property: 'inputAssistancePreset', value: $event.value })"> - <mat-option *ngFor="let option of [null, 'french', 'numbers', 'numbersAndOperators', 'numbersAndBasicOperators', - 'comparisonOperators', 'squareDashDot', 'placeValue']" - [value]="option"> - {{ option === null ? ('propertiesPanel.none' | translate) : ('propertiesPanel.' + option | translate) }} - </mat-option> - </mat-select> - </mat-form-field> - <mat-form-field *ngIf="combinedProperties.inputAssistancePreset !== null && - combinedProperties.inputAssistancePosition !== undefined" - appearance="fill"> - <mat-label>{{'propertiesPanel.inputAssistancePosition' | translate }}</mat-label> - <mat-select [value]="combinedProperties.inputAssistancePosition" - (selectionChange)="updateModel.emit({ property: 'inputAssistancePosition', value: $event.value })"> - <mat-option *ngFor="let option of ['floating', 'right']" - [value]="option"> - {{ 'propertiesPanel.' + option | translate }} - </mat-option> - </mat-select> - </mat-form-field> - - <mat-checkbox *ngIf="combinedProperties.inputAssistancePreset !== null && - combinedProperties.restrictedToInputAssistanceChars !== undefined" - [checked]="$any(combinedProperties.restrictedToInputAssistanceChars)" - (change)="updateModel.emit({ property: 'restrictedToInputAssistanceChars', value: $event.checked })"> - {{'propertiesPanel.restrictedToInputAssistanceChars' | translate }} - </mat-checkbox> - - <mat-checkbox *ngIf="combinedProperties.showSoftwareKeyboard !== undefined" - [checked]="$any(combinedProperties.showSoftwareKeyboard)" - (change)="updateModel.emit({ property: 'showSoftwareKeyboard', value: $event.checked })"> - {{'propertiesPanel.showSoftwareKeyboard' | translate }} - </mat-checkbox> - - <mat-checkbox *ngIf="combinedProperties.showSoftwareKeyboard" - [checked]="$any(combinedProperties.softwareKeyboardShowFrench)" - (change)="updateModel.emit({ property: 'softwareKeyboardShowFrench', value: $event.checked })"> - {{'propertiesPanel.softwareKeyboardShowFrench' | translate }} - </mat-checkbox> - `, - styles: [ - 'mat-form-field {width: 100%;}' - ] + <fieldset *ngIf="combinedProperties.required !== undefined" fxLayout="column"> + <legend>Eingabeelement</legend> + <mat-form-field *ngIf="combinedProperties.label !== undefined" appearance="fill"> + <mat-label>{{'propertiesPanel.label' | translate }}</mat-label> + <input matInput type="text" [value]="$any(combinedProperties.label)" + (input)="updateModel.emit({property: 'label', value: $any($event.target).value })"> + </mat-form-field> + + <mat-checkbox *ngIf="combinedProperties.readOnly !== undefined" + [checked]="$any(combinedProperties.readOnly)" + (change)="updateModel.emit({ property: 'readOnly', value: $event.checked })"> + {{'propertiesPanel.readOnly' | translate }} + </mat-checkbox> + + <mat-checkbox *ngIf="combinedProperties.required !== undefined" + [checked]="$any(combinedProperties.required)" + (change)="updateModel.emit({ property: 'required', value: $event.checked })"> + {{'propertiesPanel.requiredField' | translate }} + </mat-checkbox> + + <mat-form-field appearance="fill"> + <mat-label>{{'propertiesPanel.requiredWarnMessage' | translate }}</mat-label> + <input matInput type="text" [disabled]="!combinedProperties.required" + [value]="$any(combinedProperties.requiredWarnMessage)" + (input)="updateModel.emit({ property: 'requiredWarnMessage', value: $any($event.target).value })"> + </mat-form-field> + </fieldset> + ` }) export class InputElementPropertiesComponent { - @Input() combinedProperties!: any; + @Input() combinedProperties!: UIElement; @Output() updateModel = - new EventEmitter<{ property: string; value: string | number | boolean | string[], isInputValid?: boolean | null }>(); + new EventEmitter<{ property: string; value: string | number | boolean | string[], isInputValid?: boolean | null }>(); } diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts index c519c472bdd3dbea8f66db5c99470128de36f176..c3a290ba1e1c98576659575ac4f2463f940cf298 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts @@ -1,113 +1,162 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { CdkDragDrop } from '@angular/cdk/drag-drop/drag-events'; +import { + TextLabel, TextImageLabel, OptionElement, Label +} from 'common/models/elements/element'; +import { CombinedProperties } from 'editor/src/app/components/properties-panel/element-properties-panel.component'; +import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row'; +import { IDManager } from 'common/util/id-manager'; +import { UnitService } from 'editor/src/app/services/unit.service'; +import { DialogService } from 'editor/src/app/services/dialog.service'; import { moveItemInArray } from '@angular/cdk/drag-drop'; -import { DialogService } from '../../../../services/dialog.service'; -import { DomSanitizer } from '@angular/platform-browser'; +import { SelectionService } from 'editor/src/app/services/selection.service'; @Component({ selector: 'aspect-options-field-set', template: ` - <mat-form-field disabled="true" *ngIf="combinedProperties.options !== undefined"> - <ng-container> - <mat-label>{{'propertiesPanel.options' | translate }}</mat-label> - <div class="drop-list" cdkDropList [cdkDropListData]="combinedProperties.options" - (cdkDropListDropped)="reorderOptions('options', $any($event))"> - <div *ngFor="let option of $any(combinedProperties.options); let i = index" cdkDrag - fxLayout="row" fxLayoutAlign="end center"> - <div fxFlex="70"> - {{option}} - </div> - <button mat-icon-button color="primary" - (click)="editTextOption('options', i)"> - <mat-icon>build</mat-icon> - </button> - <button mat-icon-button color="primary" - (click)="removeOption('options', option)"> - <mat-icon>clear</mat-icon> - </button> - </div> - </div> - </ng-container> - <div fxLayout="row" fxLayoutAlign="center center"> - <button mat-icon-button matPrefix - (click)="addOption('options', newOption.value); newOption.select()"> - <mat-icon>add</mat-icon> - </button> - <input #newOption matInput type="text" placeholder="Optionstext" - (keyup.enter)="addOption('options', newOption.value); newOption.select()"> - </div> - </mat-form-field> - - <mat-form-field disabled="true" *ngIf="combinedProperties.richTextOptions !== undefined"> - <ng-container> - <mat-label>{{'propertiesPanel.options' | translate }}</mat-label> - <div class="drop-list" cdkDropList [cdkDropListData]="combinedProperties.richTextOptions" - (cdkDropListDropped)="reorderOptions('richTextOptions', $any($event))"> - <div *ngFor="let option of $any(combinedProperties.richTextOptions); let i = index" cdkDrag - fxLayout="row" fxLayoutAlign="end center"> - <div fxFlex="70" [innerHTML]="sanitizer.bypassSecurityTrustHtml(option)"> - </div> - <button mat-icon-button color="primary" - (click)="editTextOption('richTextOptions', i)"> - <mat-icon>build</mat-icon> - </button> - <button mat-icon-button color="primary" - (click)="removeOption('richTextOptions', option)"> - <mat-icon>clear</mat-icon> - </button> - </div> - </div> - </ng-container> - <div fxLayout="row" fxLayoutAlign="center center"> - <button mat-icon-button matPrefix - (click)="addOption('richTextOptions', newOption.value); newOption.select()"> - <mat-icon>add</mat-icon> - </button> - <input #newOption matInput type="text" placeholder="Optionstext" - (keyup.enter)="addOption('richTextOptions', newOption.value); newOption.select()"> - </div> - </mat-form-field> - `, - styles: [ - '.mat-form-field {width: 100%;}' - ] + <!--dropdown--> +<!-- [useRichText]="combinedProperties.type === 'radio'"--> + <aspect-option-list-panel *ngIf="combinedProperties.options !== undefined" + [title]="'propertiesPanel.options'" [textFieldLabel]="'Neue Option'" + [itemList]="$any(combinedProperties.options)" + (addItem)="addOption('options', $event)" + (removeItem)="removeOption('options', $event)" + (changedItemOrder)="moveOption('options', $event)" + (editItem)="editOption('options', $event)"> + </aspect-option-list-panel> + + <!--likert--> + <aspect-option-list-panel *ngIf="combinedProperties.rows !== undefined" + [itemList]="$any(combinedProperties).rows | LikertRowLabel" + [title]="'rows'" + [textFieldLabel]="'Neue Zeile'" + + (changedItemOrder)="moveLikertRow($event)" + + (addItem)="addLikertRow($event)" + (removeItem)="removeLikertRow($event)" + (editItem)="editLikertRow($event)"> + </aspect-option-list-panel> + ` }) export class OptionsFieldSetComponent { - @Input() combinedProperties!: any; + @Input() combinedProperties!: CombinedProperties; @Output() updateModel = - new EventEmitter<{ property: string; value: string | number | boolean | string[], isInputValid?: boolean | null }>(); + new EventEmitter<{ + property: string; + value: string | number | boolean | string[] | Label[] | LikertRowElement[] + }>(); + + constructor(private unitService: UnitService, + private selectionService: SelectionService, + public dialogService: DialogService) { } - constructor(public dialogService: DialogService, public sanitizer: DomSanitizer) { } + addOption(property: string, option: string): void { + const selectedElements = this.selectionService.getSelectedElements() as OptionElement[]; - addOption(property: string, value: string): void { + selectedElements.forEach(element => { + const newValue = [...this.combinedProperties[property] as Label[], element.getNewOptionLabel(option)]; + this.unitService.updateElementsProperty([element], property, newValue); + }); + } + + removeOption(property: string, optionIndex: number): void { + (this.combinedProperties[property] as Label[]).splice(optionIndex, 1); this.updateModel.emit({ property: property, - value: [...(this.combinedProperties[property] as string[]), value] + value: this.combinedProperties[property] as Label[] }); } - removeOption(property: string, option: any): void { - const valueList = this.combinedProperties[property] as string[]; - valueList.splice(valueList.indexOf(option), 1); - this.updateModel.emit({ property: property, value: valueList }); + moveOption(property: string, indices: { previousIndex: number, currentIndex: number }): void { + moveItemInArray(this.combinedProperties[property] as Label[], + indices.previousIndex, + indices.currentIndex); + this.updateModel.emit({ property: property, value: this.combinedProperties[property] as Label[] }); + } + + async editOption(property: string, optionIndex: number): Promise<void> { + const selectedOption = (this.combinedProperties[property] as Label[])[optionIndex]; + await this.dialogService.showLabelEditDialog(selectedOption) + .subscribe((result: Label) => { + if (result) { + (this.combinedProperties[property] as Label[])[optionIndex] = result; + this.updateModel.emit({ property, value: (this.combinedProperties[property] as Label[]) }); + } + }); + } + + notifyListChange(property: string, changedList: TextLabel[]): void { + this.updateModel.emit({ property: property, value: changedList }); } - async editTextOption(property: string, optionIndex: number): Promise<void> { - const oldOptions = this.combinedProperties[property] as string[]; - await this.dialogService - .showRichTextSimpleEditDialog(oldOptions[optionIndex], this.combinedProperties.styling.fontSize) - .subscribe((result: string) => { + addLikertRow(rowLabelText: string): void { + const newRow = new LikertRowElement({ + type: 'likert-row', + rowLabel: { + text: rowLabelText, + imgSrc: null, + imgPosition: 'above' + }, + columnCount: (this.combinedProperties.options as unknown[]).length + }, IDManager.getInstance()); + (this.combinedProperties.rows as LikertRowElement[]).push(newRow); + this.updateModel.emit({ property: 'rows', value: this.combinedProperties.rows as LikertRowElement[] }); + } + + async editLikertRow(rowIndex: number): Promise<void> { + const row = (this.combinedProperties.rows as LikertRowElement[])[rowIndex] as LikertRowElement; + const columns = this.combinedProperties.options as TextImageLabel[]; + + await this.dialogService.showLikertRowEditDialog(row, columns) + .subscribe((result: LikertRowElement) => { if (result) { - oldOptions[optionIndex] = result; - this.updateModel.emit({ property, value: oldOptions }); + if (result.id !== row.id) { + this.unitService.updateElementsProperty( + [row], + 'id', + result.id + ); + } + if (result.rowLabel !== row.rowLabel) { + this.unitService.updateElementsProperty([row], 'rowLabel', result.rowLabel); + } + if (result.value !== row.value) { + this.unitService.updateElementsProperty( + [row], + 'value', + result.value + ); + } + if (result.verticalButtonAlignment !== row.verticalButtonAlignment) { + this.unitService.updateElementsProperty( + [row], + 'verticalButtonAlignment', + result.verticalButtonAlignment + ); + } + if (result.readOnly !== row.readOnly) { + this.unitService.updateElementsProperty( + [row], + 'readOnly', + result.readOnly + ); + } } }); } - reorderOptions(property: string, event: CdkDragDrop<string[]>): void { - moveItemInArray(event.container.data, event.previousIndex, event.currentIndex); - this.updateModel.emit({ property: property, value: event.container.data }); + removeLikertRow(index: number): void { + const valueList = this.combinedProperties.rows as LikertRowElement[]; + valueList.splice(index, 1); + this.updateModel.emit({ property: 'rows', value: valueList }); + } + + moveLikertRow(indices: { previousIndex: number, currentIndex: number }): void { + moveItemInArray(this.combinedProperties.rows as LikertRowElement[], + indices.previousIndex, + indices.currentIndex); + this.updateModel.emit({ property: 'rows', value: this.combinedProperties.rows as LikertRowElement[] }); } } diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/preset-value-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/preset-value-properties.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..0c2461c6a3f6d7027f03092cb9d0c08a0b69e411 --- /dev/null +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/preset-value-properties.component.ts @@ -0,0 +1,42 @@ +import { + Component, EventEmitter, Input, Output +} from '@angular/core'; +import { CombinedProperties } from 'editor/src/app/components/properties-panel/element-properties-panel.component'; + +@Component({ + selector: 'aspect-preset-value-properties', + template: ` + <mat-form-field *ngIf="combinedProperties.type === 'text-area'" + class="wide-form-field" appearance="fill"> + <mat-label>{{'preset' | translate }}</mat-label> + <textarea matInput type="text" + [value]="$any(combinedProperties.value)" + (input)="updateModel.emit({ property: 'value', value: $any($event.target).value })"> + </textarea> + </mat-form-field> + + <mat-form-field *ngIf="combinedProperties.type === 'text-field' || combinedProperties.type === 'text-field-simple'" + class="wide-form-field" appearance="fill"> + <mat-label>{{'preset' | translate }}</mat-label> + <input matInput type="text" [value]="$any(combinedProperties).value" + (input)="updateModel.emit({property: 'value', value: $any($event.target).value })"> + </mat-form-field> + + <mat-form-field *ngIf="combinedProperties.options !== undefined && combinedProperties.rows === undefined" + appearance="fill" class="wide-form-field"> + <mat-label>{{'preset' | translate }}</mat-label> + <mat-select [value]="combinedProperties.value" + (selectionChange)="updateModel.emit({ property: 'value', value: $event.value })"> + <mat-option [value]="null">{{'propertiesPanel.undefined' | translate }}</mat-option> + <mat-option *ngFor="let option of $any(combinedProperties.options); let i = index" [value]="i"> + <div fxFlex fxFlexAlign="center" [innerHTML]="option.text + ' (Index: ' + i + ')'"></div> + </mat-option> + </mat-select> + </mat-form-field> + ` +}) +export class PresetValuePropertiesComponent { + @Input() combinedProperties!: CombinedProperties; + @Output() updateModel = + new EventEmitter<{ property: string; value: string | number | boolean | string[], isInputValid?: boolean | null }>(); +} diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/scale-and-zoom-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/scale-and-zoom-properties.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..6d44b9535b8b60cba1ecb8efb4c1f3deee60c8c3 --- /dev/null +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/scale-and-zoom-properties.component.ts @@ -0,0 +1,55 @@ +import { + Component, EventEmitter, Input, Output +} from '@angular/core'; +import { UIElement } from 'common/models/elements/element'; + +@Component({ + selector: 'aspect-scale-and-zoom-properties', + template: ` + <div fxLayout="column" fxLayoutGap="5px"> + <mat-checkbox *ngIf="combinedProperties.scale !== undefined" + [checked]="$any(combinedProperties.scale)" + (change)="updateModel.emit({ property: 'scale', value: $event.checked })"> + {{'propertiesPanel.scale' | translate }} + </mat-checkbox> + + <mat-checkbox *ngIf="combinedProperties.magnifier !== undefined" + [checked]="$any(combinedProperties.magnifier)" + (change)="updateModel.emit({ property: 'magnifier', value: $event.checked })"> + {{'propertiesPanel.magnifier' | translate }} + </mat-checkbox> + + <mat-form-field appearance="fill" *ngIf="combinedProperties.magnifierSize !== undefined"> + <mat-label>{{'propertiesPanel.magnifierSize' | translate }} in px</mat-label> + <input matInput type="number" #magnifierSize="ngModel" min="0" + [disabled]="!combinedProperties.magnifier" + [ngModel]="combinedProperties.magnifierSize" + (ngModelChange)="updateModel.emit({ + property: 'magnifierSize', + value: $event, + isInputValid: magnifierSize.valid})"> + </mat-form-field> + + <div *ngIf="combinedProperties.magnifierZoom !== undefined" + [class.disabled-label]="!combinedProperties.magnifier"> + {{'propertiesPanel.magnifierZoom' | translate }} + </div> + <mat-slider *ngIf="combinedProperties.magnifierZoom !== undefined" + min="1" max="3" step="0.1" [disabled]="!combinedProperties.magnifier" + [ngModel]="combinedProperties.magnifierZoom" + (change)="updateModel.emit({ property: 'magnifierZoom', value: $event.value })"> + </mat-slider> + <div *ngIf="combinedProperties.magnifier"> + {{combinedProperties.magnifierZoom}} + </div> + </div> + `, + styles: [ + '.disabled-label {color: rgba(0, 0, 0, 0.26)}' + ] +}) +export class ScaleAndZoomPropertiesComponent { + @Input() combinedProperties!: UIElement; + @Output() updateModel = + new EventEmitter<{ property: string; value: string | number | boolean | null, isInputValid?: boolean | null }>(); +} diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/select-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/select-properties.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..3e642370374a18cf0100d45a92d3ab6cff1c37a9 --- /dev/null +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/select-properties.component.ts @@ -0,0 +1,62 @@ +import { + Component, EventEmitter, Input, Output +} from '@angular/core'; +import { CombinedProperties } from 'editor/src/app/components/properties-panel/element-properties-panel.component'; +import { MatCheckboxChange } from '@angular/material/checkbox'; + +@Component({ + selector: 'aspect-select-properties', + template: ` + <mat-checkbox *ngIf="combinedProperties.strikeOtherOptions !== undefined" + [checked]="$any(combinedProperties.strikeOtherOptions)" + (change)="updateModel.emit({ property: 'strikeOtherOptions', value: $event.checked })"> + {{'propertiesPanel.strikeOtherOptions' | translate }} + </mat-checkbox> + <mat-checkbox *ngIf="combinedProperties.strikeSelectedOption !== undefined" + [checked]="$any(combinedProperties.strikeSelectedOption)" + (change)="updateModel.emit({ property: 'strikeSelectedOption', value: $event.checked })"> + {{'propertiesPanel.strikeSelectedOption' | translate }} + </mat-checkbox> + <mat-checkbox *ngIf="combinedProperties.allowUnset !== undefined" + [checked]="$any(combinedProperties.allowUnset)" + (change)="updateModel.emit({ property: 'allowUnset', value: $event.checked })"> + {{'propertiesPanel.allowUnset' | translate }} + </mat-checkbox> + + <mat-checkbox *ngIf="combinedProperties.itemsPerRow !== undefined" + (change)="setItemsPerRow($event)" + [checked]="$any(combinedProperties.itemsPerRow)"> + {{'limitItemPerRow' | translate}} + </mat-checkbox> + + <mat-form-field *ngIf="combinedProperties.itemsPerRow !== undefined" + appearance="fill" class="mdInput textsingleline"> + <mat-label>{{'itemsPerRow' | translate }}</mat-label> + <input matInput type="number" [min]="1" [pattern]="'[1-9]'" #itemsPerRow="ngModel" required + [disabled]="!isItemsPerRowSet" + [ngModel]="$any(combinedProperties.itemsPerRow)" + (input)="itemsPerRow.valid && + updateModel.emit({ property: 'itemsPerRow', value: $any($event.target).value })"> + <mat-error *ngIf="itemsPerRow.errors?.['pattern']"> + {{'numberGreater0' | translate}} + </mat-error> + </mat-form-field> + ` +}) +export class SelectPropertiesComponent { + @Input() combinedProperties!: CombinedProperties; + @Output() updateModel = + new EventEmitter<{ + property: string, + value: string | number | boolean | string[] | null + }>(); + + isItemsPerRowSet = false; + + setItemsPerRow(event: MatCheckboxChange) { + this.isItemsPerRowSet = event.checked; + if (!event.checked) { + this.updateModel.emit({ property: 'itemsPerRow', value: null }); + } + } +} diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/slider-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/slider-properties.component.ts index d53f8639404b9976c8e02e311e1b74c8cbda9e4b..444793379d5f3aafd189668df2dd74c2aef393c7 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/slider-properties.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/slider-properties.component.ts @@ -1,4 +1,6 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { + Component, EventEmitter, Input, Output +} from '@angular/core'; @Component({ selector: 'aspect-slider-properties', @@ -36,12 +38,10 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; (change)="updateModel.emit({ property: 'thumbLabel', value: $event.checked })"> {{'propertiesPanel.thumbLabel' | translate }} </mat-checkbox> - `, - styles: [ - ] + ` }) export class SliderPropertiesComponent { @Input() combinedProperties!: any; @Output() updateModel = - new EventEmitter<{ property: string; value: string | number | boolean | string[], isInputValid?: boolean | null }>(); + new EventEmitter<{ property: string; value: string | number | boolean | string[], isInputValid?: boolean | null }>(); } diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/text-field-element-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/text-field-element-properties.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..d50060ab72b6a6f13fb21073b7426107f353e4d8 --- /dev/null +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/text-field-element-properties.component.ts @@ -0,0 +1,128 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { CombinedProperties } from 'editor/src/app/components/properties-panel/element-properties-panel.component'; + +@Component({ + selector: 'aspect-text-field-element-properties', + template: ` + <mat-form-field *ngIf="combinedProperties.appearance !== undefined" appearance="fill"> + <mat-label>{{'propertiesPanel.appearance' | translate }}</mat-label> + <mat-select [value]="combinedProperties.appearance" + (selectionChange)="updateModel.emit({ property: 'appearance', value: $event.value })"> + <mat-option *ngFor="let option of [{displayValue: 'fill', value: 'fill'}, + {displayValue: 'outline', value: 'outline'}]" + [value]="option.value"> + {{'propertiesPanel.' + option.displayValue | translate}} + </mat-option> + </mat-select> + </mat-form-field> + + <ng-container *ngIf="combinedProperties.minLength !== undefined"> + <mat-form-field class="wide-form-field" appearance="fill"> + <mat-label>{{'propertiesPanel.minLength' | translate }}</mat-label> + <input matInput type="number" #minLength="ngModel" min="0" + [ngModel]="combinedProperties.minLength" + (ngModelChange)="updateModel.emit({ + property: 'minLength', + value: $event, + isInputValid: minLength.valid })"> + </mat-form-field> + <mat-form-field class="wide-form-field" appearance="fill"> + <mat-label>{{'propertiesPanel.minLengthWarnMessage' | translate }}</mat-label> + <input matInput type="text" + [disabled]="!combinedProperties.minLength" + [value]="$any(combinedProperties.minLengthWarnMessage)" + (input)="updateModel.emit({ property: 'minLengthWarnMessage', value: $any($event.target).value })"> + </mat-form-field> + </ng-container> + + <ng-container *ngIf="combinedProperties.maxLength !== undefined"> + <mat-form-field class="wide-form-field" appearance="fill"> + <mat-label>{{'propertiesPanel.maxLength' | translate }}</mat-label> + <input matInput type="number" #maxLength="ngModel" min="0" + [ngModel]="combinedProperties.maxLength" + (ngModelChange)="updateModel.emit({ + property: 'maxLength', + value: $event, + isInputValid: maxLength.valid })"> + </mat-form-field> + <mat-form-field class="wide-form-field" appearance="fill"> + <mat-label>{{'propertiesPanel.maxLengthWarnMessage' | translate }}</mat-label> + <input matInput type="text" + [disabled]="!combinedProperties.maxLength" + [value]="$any(combinedProperties.maxLengthWarnMessage)" + (input)="updateModel.emit({ property: 'maxLengthWarnMessage', value: $any($event.target).value })"> + </mat-form-field> + </ng-container> + + <ng-container *ngIf="combinedProperties.pattern !== undefined"> + <mat-form-field class="wide-form-field" appearance="fill" + matTooltip="Angabe als regulärer Ausdruck."> + <mat-label>{{'propertiesPanel.pattern' | translate }}</mat-label> + <input matInput [value]="$any(combinedProperties.pattern)" + (input)="updateModel.emit({ property: 'pattern', value: $any($event.target).value })"> + </mat-form-field> + <mat-form-field class="wide-form-field" appearance="fill"> + <mat-label>{{'propertiesPanel.patternWarnMessage' | translate }}</mat-label> + <input matInput type="text" + [disabled]="!combinedProperties.pattern" + [value]="$any(combinedProperties.patternWarnMessage)" + (input)="updateModel.emit({ property: 'patternWarnMessage', value: $any($event.target).value })"> + </mat-form-field> + </ng-container> + + <mat-checkbox *ngIf="combinedProperties.clearable !== undefined" + [checked]="$any(combinedProperties.clearable)" + (change)="updateModel.emit({ property: 'clearable', value: $event.checked })"> + {{'propertiesPanel.clearable' | translate }} + </mat-checkbox> + + <mat-checkbox *ngIf="combinedProperties.showSoftwareKeyboard !== undefined" + [checked]="$any(combinedProperties.showSoftwareKeyboard)" + (change)="updateModel.emit({ property: 'showSoftwareKeyboard', value: $event.checked })"> + {{'propertiesPanel.showSoftwareKeyboard' | translate }} + </mat-checkbox> + <mat-checkbox *ngIf="combinedProperties.showSoftwareKeyboard !== undefined" + [disabled]="!combinedProperties.showSoftwareKeyboard" + [checked]="$any(combinedProperties.softwareKeyboardShowFrench)" + (change)="updateModel.emit({ property: 'softwareKeyboardShowFrench', value: $event.checked })"> + {{'propertiesPanel.softwareKeyboardShowFrench' | translate }} + </mat-checkbox> + + <mat-form-field *ngIf="combinedProperties.inputAssistancePreset !== undefined" appearance="fill" + class="wide-form-field"> + <mat-label>{{'propertiesPanel.inputAssistance' | translate }}</mat-label> + <mat-select [value]="combinedProperties.inputAssistancePreset" + (selectionChange)="updateModel.emit({ property: 'inputAssistancePreset', value: $event.value })"> + <mat-option *ngFor="let option of [null, 'french', 'numbers', 'numbersAndOperators', 'numbersAndBasicOperators', + 'comparisonOperators', 'squareDashDot', 'placeValue']" + [value]="option"> + {{ option === null ? ('propertiesPanel.none' | translate) : ('propertiesPanel.' + option | translate) }} + </mat-option> + </mat-select> + </mat-form-field> + <mat-form-field *ngIf="combinedProperties.inputAssistancePreset !== null && + combinedProperties.inputAssistancePosition !== undefined" + appearance="fill"> + <mat-label>{{'propertiesPanel.inputAssistancePosition' | translate }}</mat-label> + <mat-select [value]="combinedProperties.inputAssistancePosition" + (selectionChange)="updateModel.emit({ property: 'inputAssistancePosition', value: $event.value })"> + <mat-option *ngFor="let option of ['floating', 'right']" + [value]="option"> + {{ 'propertiesPanel.' + option | translate }} + </mat-option> + </mat-select> + </mat-form-field> + + <mat-checkbox *ngIf="combinedProperties.inputAssistancePreset !== null && + combinedProperties.restrictedToInputAssistanceChars !== undefined" + [checked]="$any(combinedProperties.restrictedToInputAssistanceChars)" + (change)="updateModel.emit({ property: 'restrictedToInputAssistanceChars', value: $event.checked })"> + {{'propertiesPanel.restrictedToInputAssistanceChars' | translate }} + </mat-checkbox> + ` +}) +export class TextFieldElementPropertiesComponent { + @Input() combinedProperties!: CombinedProperties; + @Output() updateModel = + new EventEmitter<{ property: string; value: string | number | boolean | string[], isInputValid?: boolean | null }>(); +} diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/text-properties-field-set.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/text-properties-field-set.component.ts index 8b6b3022ce3d624979a5431f0fdb233b3d5dec96..27bd9d6d601687c8545eff6e3bf19ac052bd0e31 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/text-properties-field-set.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/text-properties-field-set.component.ts @@ -1,15 +1,14 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { TextElement } from 'common/models/elements/text/text'; import { DialogService } from '../../../../services/dialog.service'; import { SelectionService } from '../../../../services/selection.service'; -import { TextElement } from 'common/models/elements/text/text'; @Component({ selector: 'aspect-text-properties-field-set', template: ` - <fieldset *ngIf="combinedProperties.text"> - <legend>Textelement</legend> + <div *ngIf="combinedProperties.text" fxLayout="column"> <ng-container> <div class="text-text" [innerHTML]="$any(combinedProperties.text) | safeResourceHTML" @@ -41,24 +40,17 @@ import { TextElement } from 'common/models/elements/text/text'; (change)="updateModel.emit({ property: 'highlightableOrange', value: $event.checked })"> {{'propertiesPanel.highlightableOrange' | translate }} </mat-checkbox> - </fieldset> + </div> `, styles: [ - 'mat-checkbox {margin-left: 15px;}', - '.text-text {min-height: 125px; max-height: 500px; overflow: auto; margin-bottom: 10px;}', - '.text-text {background-color: rgba(0,0,0,.04); cursor: pointer;}', - '::ng-deep .text-text p:empty::after {content: "\\00A0";}', - '::ng-deep .text-text h1 {font-weight: bold; font-size: 20px;}', - '::ng-deep .text-text h2 {font-weight: bold; font-size: 18px;}', - '::ng-deep .text-text h3 {font-weight: bold; font-size: 16px;}', - '::ng-deep .text-text h4 {font-weight: normal; font-size: 16px;}', - '::ng-deep .text-text mark {color: inherit;}' + '.text-text {min-height: 125px; max-height: 400px; overflow: auto;}', + '.text-text {background-color: rgba(0,0,0,.04); cursor: pointer; margin-bottom: 10px;}' ] }) export class TextPropertiesFieldSetComponent { @Input() combinedProperties!: any; @Output() updateModel = - new EventEmitter<{ property: string; value: string | number | boolean | string[], isInputValid?: boolean | null }>(); + new EventEmitter<{ property: string; value: string | number | boolean | string[], isInputValid?: boolean | null }>(); constructor(public dialogService: DialogService, public selectionService: SelectionService) {} diff --git a/projects/editor/src/app/components/properties-panel/option-list-panel.component.ts b/projects/editor/src/app/components/properties-panel/option-list-panel.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..ef3e289c484333f7c5fd7019c0dd9fda7a7e6996 --- /dev/null +++ b/projects/editor/src/app/components/properties-panel/option-list-panel.component.ts @@ -0,0 +1,62 @@ +import { + Component, EventEmitter, Input, Output +} from '@angular/core'; +import { CdkDragDrop } from '@angular/cdk/drag-drop/drag-events'; +import { Label } from 'common/models/elements/element'; + +@Component({ + selector: 'aspect-option-list-panel', + template: ` + <fieldset fxLayout="column"> + <legend>{{title | translate }}</legend> + <mat-form-field appearance="outline"> + <mat-label>{{textFieldLabel}}</mat-label> + <input #newItem matInput type="text" placeholder="Fragetext" + (keyup.enter)="addListItem(newItem.value); newItem.select()"> + <button mat-mini-fab matSuffix color="primary" [style.bottom.px]="3" + (click)="addListItem(newItem.value); newItem.select()"> + <mat-icon>add</mat-icon> + </button> + </mat-form-field> + + <div class="drop-list" cdkDropList [cdkDropListData]="itemList" + (cdkDropListDropped)="moveListValue($event)"> + <div *ngFor="let item of itemList; let i = index" cdkDrag + class="option-draggable" fxLayout="row"> + <div fxFlex fxFlexAlign="center" [innerHTML]="item.text"></div> + <img [src]="$any(item).imgSrc" + [style.object-fit]="'scale-down'" [style.height.px]="40"> + <button mat-icon-button color="primary" + (click)="editItem.emit(i)"> + <mat-icon>build</mat-icon> + </button> + <button mat-icon-button color="primary" + (click)="removeListItem(i)"> + <mat-icon>clear</mat-icon> + </button> + </div> + </div> + </fieldset> + ` +}) +export class OptionListPanelComponent { + @Input() title!: string; + @Input() textFieldLabel!: string; + @Input() itemList!: Label[]; + @Output() addItem = new EventEmitter<string>(); + @Output() removeItem = new EventEmitter<number>(); + @Output() editItem = new EventEmitter<number>(); + @Output() changedItemOrder = new EventEmitter<{ previousIndex: number, currentIndex: number }>(); + + addListItem(text: string): void { + this.addItem.emit(text); + } + + removeListItem(itemIndex: number): void { + this.removeItem.emit(itemIndex); + } + + moveListValue(event: CdkDragDrop<Label[]>): void { + this.changedItemOrder.emit({ previousIndex: event.previousIndex, currentIndex: event.currentIndex }); + } +} diff --git a/projects/editor/src/app/components/properties-panel/position-properties-tab/element-position-properties.component.ts b/projects/editor/src/app/components/properties-panel/position-properties-tab/element-position-properties.component.ts index 88aedbbdd7eacbc07dd678ab91c8b13cd8d5fca1..045cbe78688b290e6e2c956c6fcfe74007f5c3ac 100644 --- a/projects/editor/src/app/components/properties-panel/position-properties-tab/element-position-properties.component.ts +++ b/projects/editor/src/app/components/properties-panel/position-properties-tab/element-position-properties.component.ts @@ -1,9 +1,9 @@ import { Component, Input } from '@angular/core'; +import { PositionedUIElement, PositionProperties } from 'common/models/elements/element'; import { UnitService } from '../../../services/unit.service'; import { SelectionService } from '../../../services/selection.service'; -import { PositionedUIElement, PositionProperties } from 'common/models/elements/element'; @Component({ selector: 'aspect-element-postion-properties', @@ -39,14 +39,10 @@ import { PositionedUIElement, PositionProperties } from 'common/models/elements/ </div> </ng-container> </div> - `, - styles: [ - 'aspect-position-field-set {margin-bottom: 20px;}', - ':host ::ng-deep fieldset {padding-bottom: 0;}' - ] + ` }) export class ElementPositionPropertiesComponent { - @Input() dimensions!: { width: number; height: number; dynamicWidth: boolean; }; + @Input() dimensions!: { width?: number; height?: number; dynamicWidth: boolean; }; @Input() positionProperties: PositionProperties | undefined; constructor(public unitService: UnitService, public selectionService: SelectionService) { } diff --git a/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/dimension-field-set.component.ts b/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/dimension-field-set.component.ts index de37bc75777aa0ce60f9eefdedd497090a129d11..cfe65d38df4f4d7744c7a9bf1c5241262098d15e 100644 --- a/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/dimension-field-set.component.ts +++ b/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/dimension-field-set.component.ts @@ -1,6 +1,4 @@ -import { - Component, EventEmitter, Input, Output -} from '@angular/core'; +import { Component, Input } from '@angular/core'; import { PositionProperties } from 'common/models/elements/element'; import { UnitService } from 'editor/src/app/services/unit.service'; import { SelectionService } from 'editor/src/app/services/selection.service'; @@ -31,7 +29,7 @@ import { SelectionService } from 'editor/src/app/services/selection.service'; (positionProperties?.dynamicPositioning && positionProperties?.fixedSize)"> {{'propertiesPanel.width' | translate }} </mat-label> - <input matInput type="number" #width="ngModel" min="0" + <input matInput type="number" min="0" [disabled]="$any(dimensions.dynamicWidth)" [ngModel]="dimensions.width" (ngModelChange)="updateDimensionProperty('width', $event)"> @@ -54,19 +52,16 @@ import { SelectionService } from 'editor/src/app/services/selection.service'; (positionProperties?.fixedSize || positionProperties?.useMinHeight))"> {{'propertiesPanel.height' | translate }} </mat-label> - <input matInput type="number" #height="ngModel" min="0" + <input matInput type="number" min="0" [ngModel]="dimensions.height" (ngModelChange)="updateDimensionProperty('height', $event)"> </mat-form-field> </fieldset> - `, - styles: [ - '.mat-form-field {display: inline;}' - ] + ` }) export class DimensionFieldSetComponent { @Input() positionProperties: PositionProperties | undefined; - @Input() dimensions!: { width: number; height: number; dynamicWidth?: boolean }; + @Input() dimensions!: { width?: number; height?: number; dynamicWidth?: boolean }; constructor(public unitService: UnitService, public selectionService: SelectionService) { } diff --git a/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/position-field-set.component.ts b/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/position-field-set.component.ts index 04dcee575a01cbddf9023103ac43f25fef14c673..011de1f564fbcb882e3fadc6caa46b5b2bfa0495 100644 --- a/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/position-field-set.component.ts +++ b/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/position-field-set.component.ts @@ -8,9 +8,11 @@ import { PositionProperties } from 'common/models/elements/element'; template: ` <fieldset> <legend>Position</legend> - <ng-container *ngIf="!positionProperties.dynamicPositioning; else elseBlock"> + <div *ngIf="!positionProperties.dynamicPositioning; else elseBlock" + fxLayout="row" fxLayoutGap="10px"> <mat-form-field *ngIf="!positionProperties.dynamicPositioning && - positionProperties.xPosition !== undefined" appearance="fill"> + positionProperties.xPosition !== undefined" + fxFlex> <mat-label>{{'propertiesPanel.xPosition' | translate }}</mat-label> <input matInput type="number" #xPosition="ngModel" min="0" [ngModel]="positionProperties.xPosition" @@ -18,36 +20,38 @@ import { PositionProperties } from 'common/models/elements/element'; { property: 'xPosition', value: $event, isInputValid: xPosition.valid && $event !== null })"> </mat-form-field> <mat-form-field *ngIf="!positionProperties.dynamicPositioning && - positionProperties.yPosition !== undefined" appearance="fill"> + positionProperties.yPosition !== undefined" + fxFlex> <mat-label>{{'propertiesPanel.yPosition' | translate }}</mat-label> <input matInput type="number" #yPosition="ngModel" min="0" [ngModel]="positionProperties.yPosition" (ngModelChange)="updateModel.emit( { property: 'yPosition', value: $event, isInputValid: yPosition.valid && $event !== null })"> </mat-form-field> - </ng-container> + </div> <ng-template #elseBlock> {{'propertiesPanel.grid' | translate }} - <div class="input-group"> - <mat-form-field> - <mat-label>{{'propertiesPanel.column' | translate }}</mat-label> + <div fxLayout="row" fxLayoutGap="10px"> + <mat-form-field fxFlex> + <mat-label>{{'column' | translate }}</mat-label> <input matInput type="number" [ngModel]="positionProperties.gridColumn" (ngModelChange)="updateModel.emit({ property: 'gridColumn', value: $event })"> </mat-form-field> - <mat-form-field> - <mat-label>{{'propertiesPanel.row' | translate }}</mat-label> - <input matInput type="number" [ngModel]="positionProperties.gridRow" - (ngModelChange)="updateModel.emit({ property: 'gridRow', value: $event })"> - </mat-form-field> - - <mat-form-field> + <mat-form-field fxFlex="40"> <mat-label>{{'propertiesPanel.columnRange' | translate }}</mat-label> <input matInput type="number" [ngModel]="positionProperties.gridColumnRange" (ngModelChange)="updateModel.emit({ property: 'gridColumnRange', value: $event })"> </mat-form-field> - <mat-form-field> + </div> + <div fxLayout="row" fxLayoutGap="10px"> + <mat-form-field fxFlex> + <mat-label>{{'row' | translate }}</mat-label> + <input matInput type="number" [ngModel]="positionProperties.gridRow" + (ngModelChange)="updateModel.emit({ property: 'gridRow', value: $event })"> + </mat-form-field> + <mat-form-field fxFlex="40"> <mat-label>{{'propertiesPanel.rowRange' | translate }}</mat-label> <input matInput type="number" [ngModel]="positionProperties.gridRowRange" @@ -56,16 +60,16 @@ import { PositionProperties } from 'common/models/elements/element'; </div> {{'propertiesPanel.margin' | translate }} - <div class="input-group"> - <mat-form-field class="centered-form-field small-input"> + <div fxLayout="column" class="margin-controls"> + <mat-form-field fxFlexAlign="center"> <mat-label>{{'propertiesPanel.top' | translate }}</mat-label> <input matInput type="number" #marginTop="ngModel" [ngModel]="positionProperties.marginTop" (ngModelChange)="updateModel.emit( { property: 'marginTop', value: $event, isInputValid: marginTop.valid && $event !== null })"> </mat-form-field> - <div fxLayoutAlign="row"> - <mat-form-field class="small-input"> + <div fxLayout="row" fxLayoutAlign="space-around center"> + <mat-form-field> <mat-label>{{'propertiesPanel.left' | translate }}</mat-label> <input matInput type="number" #marginLeft="ngModel" [ngModel]="positionProperties.marginLeft" @@ -75,7 +79,7 @@ import { PositionProperties } from 'common/models/elements/element'; isInputValid: marginLeft.valid && $event !== null })"> </mat-form-field> - <mat-form-field class="right-form-field small-input"> + <mat-form-field> <mat-label>{{'propertiesPanel.right' | translate }}</mat-label> <input matInput type="number" #marginRight="ngModel" [ngModel]="positionProperties.marginRight" @@ -85,7 +89,7 @@ import { PositionProperties } from 'common/models/elements/element'; isInputValid: marginRight .valid && $event !== null })"> </mat-form-field> </div> - <mat-form-field class="centered-form-field small-input"> + <mat-form-field fxFlexAlign="center"> <mat-label>{{'propertiesPanel.bottom' | translate }}</mat-label> <input matInput type="number" #marginBottom="ngModel" [ngModel]="positionProperties.marginBottom" @@ -110,12 +114,9 @@ import { PositionProperties } from 'common/models/elements/element'; </fieldset> `, styles: [ - '.centered-form-field {margin-left: 25%;}', - '.right-form-field {margin-left: 15px;}', - '.input-group {background-color: rgba(0,0,0,.04); margin-bottom: 10px;}', - '.mat-form-field {display: inline;}', - '.small-input {display: inline-block;}', - '::ng-deep aspect-position-field-set .small-input .mat-form-field-infix {width: 100px; margin: 0 5px;}' + '.margin-controls mat-form-field {width: 100px;}', + '.margin-controls {margin-bottom: 10px;}', + 'mat-form-field {width: 110px;}' ] }) export class PositionFieldSetComponent { diff --git a/projects/editor/src/app/components/properties-panel/style-properties-tab/element-style-properties.component.ts b/projects/editor/src/app/components/properties-panel/style-properties-tab/element-style-properties.component.ts index 27daacccc22347d200e3fd909a310e713b5ab72b..a88f7c86e04fb39011e64c528d17c2606dbfaccb 100644 --- a/projects/editor/src/app/components/properties-panel/style-properties-tab/element-style-properties.component.ts +++ b/projects/editor/src/app/components/properties-panel/style-properties-tab/element-style-properties.component.ts @@ -1,13 +1,11 @@ -import { - Component, EventEmitter, Input, Output -} from '@angular/core'; +import { Component, Input } from '@angular/core'; import { BasicStyles, ExtendedStyles } from 'common/models/elements/element'; import { UnitService } from 'editor/src/app/services/unit.service'; @Component({ selector: 'aspect-element-style-properties', template: ` - <div fxLayout="column"> + <div fxLayout="column" *ngIf="styles"> <mat-checkbox *ngIf="styles.lineColoring !== undefined" [checked]="$any(styles.lineColoring)" (change)="unitService.updateSelectedElementsStyleProperty('lineColoring', $event.checked)"> @@ -39,12 +37,6 @@ import { UnitService } from 'editor/src/app/services/unit.service'; [value]="styles.selectionColor" (input)="unitService.updateSelectedElementsStyleProperty('selectionColor', $any($event.target).value)"> - <mat-form-field *ngIf="styles.borderRadius !== undefined" appearance="fill"> - <mat-label>{{'propertiesPanel.borderRadius' | translate }}</mat-label> - <input matInput type="number" [ngModel]="styles.borderRadius" - (input)="unitService.updateSelectedElementsStyleProperty('borderRadius', $any($event.target).value)"> - </mat-form-field> - <mat-form-field *ngIf="styles.itemBackgroundColor !== undefined" appearance="fill" class="mdInput textsingleline"> <mat-label>{{'propertiesPanel.itemBackgroundColor' | translate }}</mat-label> @@ -71,19 +63,6 @@ import { UnitService } from 'editor/src/app/services/unit.service'; [value]="styles.backgroundColor" (input)="unitService.updateSelectedElementsStyleProperty('backgroundColor', $any($event.target).value)"> - <mat-form-field *ngIf="styles.borderColor !== undefined" - appearance="fill" class="mdInput textsingleline"> - <mat-label>{{'propertiesPanel.borderColor' | translate }}</mat-label> - <input matInput type="text" [value]="styles.borderColor" - (input)="unitService.updateSelectedElementsStyleProperty('borderColor', $any($event.target).value)"> - <button mat-icon-button matSuffix (click)="borderColorInput.click()"> - <mat-icon>edit</mat-icon> - </button> - </mat-form-field> - <input matInput type="color" hidden #borderColorInput - [value]="styles.borderColor" - (input)="unitService.updateSelectedElementsStyleProperty('borderColor', $any($event.target).value)"> - <mat-form-field *ngIf="styles.fontColor !== undefined" appearance="fill" class="mdInput textsingleline"> <mat-label>{{'propertiesPanel.fontColor' | translate }}</mat-label> @@ -134,6 +113,30 @@ import { UnitService } from 'editor/src/app/services/unit.service'; {{'propertiesPanel.underline' | translate }} </mat-checkbox> + </div> + + <fieldset *ngIf="styles && styles.borderRadius !== undefined"> + <legend>Rahmen</legend> + + <mat-form-field *ngIf="styles.borderRadius !== undefined" appearance="fill"> + <mat-label>{{'propertiesPanel.borderRadius' | translate }}</mat-label> + <input matInput type="number" [ngModel]="styles.borderRadius" + (input)="unitService.updateSelectedElementsStyleProperty('borderRadius', $any($event.target).value)"> + </mat-form-field> + + <mat-form-field *ngIf="styles.borderColor !== undefined" + appearance="fill" class="mdInput textsingleline"> + <mat-label>{{'propertiesPanel.borderColor' | translate }}</mat-label> + <input matInput type="text" [value]="styles.borderColor" + (input)="unitService.updateSelectedElementsStyleProperty('borderColor', $any($event.target).value)"> + <button mat-icon-button matSuffix (click)="borderColorInput.click()"> + <mat-icon>edit</mat-icon> + </button> + </mat-form-field> + <input matInput type="color" hidden #borderColorInput + [value]="styles.borderColor" + (input)="unitService.updateSelectedElementsStyleProperty('borderColor', $any($event.target).value)"> + <mat-form-field *ngIf="styles.borderStyle !== undefined" appearance="fill"> <mat-label>{{'propertiesPanel.borderStyle' | translate }}</mat-label> @@ -146,7 +149,6 @@ import { UnitService } from 'editor/src/app/services/unit.service'; </mat-option> </mat-select> </mat-form-field> - <mat-form-field *ngIf="styles.borderWidth !== undefined" appearance="fill" class="mdInput textsingleline"> <mat-label>{{'propertiesPanel.borderWidth' | translate }}</mat-label> @@ -154,11 +156,11 @@ import { UnitService } from 'editor/src/app/services/unit.service'; [ngModel]="styles.borderWidth" (ngModelChange)="unitService.updateSelectedElementsStyleProperty('borderWidth', $event)"> </mat-form-field> - </div> + </fieldset> ` }) export class ElementStylePropertiesComponent { - @Input() styles!: BasicStyles & ExtendedStyles; + @Input() styles!: BasicStyles & ExtendedStyles | undefined; constructor(public unitService: UnitService) { } } diff --git a/projects/editor/src/app/components/unit-view/unit-view.component.ts b/projects/editor/src/app/components/unit-view/unit-view.component.ts index 64d2869a47ece0ceea8182a50a7f948bf739324a..0a6e36624fe98188234f5ee7b8141bc4b84a4070 100644 --- a/projects/editor/src/app/components/unit-view/unit-view.component.ts +++ b/projects/editor/src/app/components/unit-view/unit-view.component.ts @@ -1,12 +1,12 @@ import { Component, OnDestroy } from '@angular/core'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; -import { UnitService } from '../../services/unit.service'; -import { DialogService } from '../../services/dialog.service'; -import { SelectionService } from '../../services/selection.service'; import { MessageService } from 'common/services/message.service'; import { ArrayUtils } from 'common/util/array'; import { Page } from 'common/models/page'; +import { UnitService } from '../../services/unit.service'; +import { DialogService } from '../../services/dialog.service'; +import { SelectionService } from '../../services/selection.service'; @Component({ selector: 'aspect-unit-view', diff --git a/projects/editor/src/app/services/dialog.service.ts b/projects/editor/src/app/services/dialog.service.ts index 86f6863805a82d8287a9e5e7224da278ae976c6d..65b1f9f37a73164c0cc21c35309cf081234dede3 100644 --- a/projects/editor/src/app/services/dialog.service.ts +++ b/projects/editor/src/app/services/dialog.service.ts @@ -1,20 +1,23 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { MatDialog } from '@angular/material/dialog'; +import { + DragNDropValueObject, + PlayerProperties, + TextImageLabel, Label +} from 'common/models/elements/element'; +import { ClozeDocument } from 'common/models/elements/compound-elements/cloze/cloze'; +import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row'; +import { Section } from 'common/models/section'; +import { SectionInsertDialogComponent } from 'editor/src/app/components/dialogs/section-insert-dialog.component'; +import { LabelEditDialogComponent } from 'editor/src/app/components/dialogs/label-edit-dialog.component'; import { ConfirmationDialogComponent } from '../components/dialogs/confirmation-dialog.component'; import { TextEditDialogComponent } from '../components/dialogs/text-edit-dialog.component'; import { TextEditMultilineDialogComponent } from '../components/dialogs/text-edit-multiline-dialog.component'; import { RichTextEditDialogComponent } from '../components/dialogs/rich-text-edit-dialog.component'; import { PlayerEditDialogComponent } from '../components/dialogs/player-edit-dialog.component'; -import { ColumnHeaderEditDialogComponent } from '../components/dialogs/column-header-edit-dialog.component'; import { LikertRowEditDialogComponent } from '../components/dialogs/likert-row-edit-dialog.component'; import { DropListOptionEditDialogComponent } from '../components/dialogs/drop-list-option-edit-dialog.component'; -import { RichTextSimpleEditDialogComponent } from '../components/dialogs/rich-text-simple-edit-dialog.component'; -import { DragNDropValueObject, PlayerProperties, TextImageLabel } from 'common/models/elements/element'; -import { ClozeDocument } from 'common/models/elements/compound-elements/cloze/cloze'; -import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row'; -import { SectionInsertDialogComponent } from 'editor/src/app/components/dialogs/section-insert-dialog.component'; -import { Section } from 'common/models/section'; @Injectable({ providedIn: 'root' @@ -22,6 +25,13 @@ import { Section } from 'common/models/section'; export class DialogService { constructor(private dialog: MatDialog) { } + showLabelEditDialog(label: Label): Observable<Label> { + const dialogRef = this.dialog.open(LabelEditDialogComponent, { + data: { label } + }); + return dialogRef.afterClosed(); + } + showConfirmDialog(text: string): Observable<boolean> { const dialogRef = this.dialog.open(ConfirmationDialogComponent, { data: { text } @@ -57,17 +67,7 @@ export class DialogService { defaultFontSize, clozeMode: false }, - autoFocus: false - }); - return dialogRef.afterClosed(); - } - - showRichTextSimpleEditDialog(text: string, defaultFontSize: number): Observable<string> { - const dialogRef = this.dialog.open(RichTextSimpleEditDialogComponent, { - data: { - content: text, - defaultFontSize - }, + height: '600px', autoFocus: false }); return dialogRef.afterClosed(); @@ -75,7 +75,12 @@ export class DialogService { showClozeTextEditDialog(document: ClozeDocument, defaultFontSize: number): Observable<string> { const dialogRef = this.dialog.open(RichTextEditDialogComponent, { - data: { content: document, defaultFontSize, clozeMode: true }, + data: { + content: document, + defaultFontSize, + clozeMode: true + }, + height: '700px', autoFocus: false }); return dialogRef.afterClosed(); @@ -88,16 +93,9 @@ export class DialogService { return dialogRef.afterClosed(); } - showLikertColumnEditDialog(column: TextImageLabel, defaultFontSize: number): Observable<TextImageLabel> { - const dialogRef = this.dialog.open(ColumnHeaderEditDialogComponent, { - data: { column, defaultFontSize } - }); - return dialogRef.afterClosed(); - } - - showLikertRowEditDialog(row: LikertRowElement, columns: TextImageLabel[]): Observable<LikertRowElement> { + showLikertRowEditDialog(row: LikertRowElement, options: TextImageLabel[]): Observable<LikertRowElement> { const dialogRef = this.dialog.open(LikertRowEditDialogComponent, { - data: { row, columns } + data: { row, options } }); return dialogRef.afterClosed(); } diff --git a/projects/editor/src/app/services/unit.service.ts b/projects/editor/src/app/services/unit.service.ts index 0545daf1028fe77324ce126dd52ac6855a03eade..b030dbed91a53b2c8b22a481f146d40626ca5c57 100644 --- a/projects/editor/src/app/services/unit.service.ts +++ b/projects/editor/src/app/services/unit.service.ts @@ -4,16 +4,14 @@ import { Subject } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; import { FileService } from 'common/services/file.service'; import { MessageService } from 'common/services/message.service'; -import { DialogService } from './dialog.service'; -import { VeronaAPIService } from './verona-api.service'; -import { SelectionService } from './selection.service'; import { ArrayUtils } from 'common/util/array'; import { SanitizationService } from 'common/services/sanitization.service'; import { Unit } from 'common/models/unit'; import { + CompoundElement, DragNDropValueObject, InputElement, - InputElementValue, PlayerElement, PlayerProperties, PositionedUIElement, TextImageLabel, - UIElement, UIElementType + InputElementValue, TextLabel, PlayerElement, PlayerProperties, PositionedUIElement, TextImageLabel, + UIElement, UIElementType, UIElementValue } from 'common/models/elements/element'; import { LikertElement } from 'common/models/elements/compound-elements/likert/likert'; import { ClozeDocument, ClozeElement } from 'common/models/elements/compound-elements/cloze/cloze'; @@ -24,12 +22,16 @@ import { Page } from 'common/models/page'; import { Section } from 'common/models/section'; import { ElementFactory } from 'common/util/element.factory'; import { IDManager } from 'common/util/id-manager'; +import { DialogService } from './dialog.service'; +import { VeronaAPIService } from './verona-api.service'; +import { SelectionService } from './selection.service'; @Injectable({ providedIn: 'root' }) export class UnitService { unit: Unit; + idManager = IDManager.getInstance(); elementPropertyUpdated: Subject<void> = new Subject<void>(); @@ -44,13 +46,13 @@ export class UnitService { } loadUnitDefinition(unitDefinition: string): void { - IDManager.getInstance().reset(); + this.idManager.reset(); const unitDef = JSON.parse(unitDefinition); if (SanitizationService.isUnitDefinitionOutdated(unitDef)) { this.unit = new Unit(this.sanitizationService.sanitizeUnitDefinition(unitDef)); this.messageService.showMessage(this.translateService.instant('outdatedUnit')); } else { - this.unit = new Unit(unitDef, IDManager.getInstance()); + this.unit = new Unit(unitDef, this.idManager); } } @@ -139,14 +141,19 @@ export class UnitService { this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit); } - private freeUpIds(elements: UIElement[]): void { // TODO free up child and value IDs + private freeUpIds(elements: UIElement[]): void { elements.forEach(element => { if (element.type === 'drop-list') { ((element as DropListElement).value as DragNDropValueObject[]).forEach((value: DragNDropValueObject) => { - IDManager.getInstance().removeId(value.id); + this.idManager.removeId(value.id); + }); + } + if (element instanceof CompoundElement) { + element.getChildElements().forEach((childElement: UIElement) => { + this.idManager.removeId(childElement.id); }); } - IDManager.getInstance().removeId(element.id); + this.idManager.removeId(element.id); }); } @@ -177,25 +184,13 @@ export class UnitService { if ('value' in newElement && newElement.value instanceof Object) { // replace value Ids with fresh ones (dropList) (newElement.value as DragNDropValueObject[]).forEach((valueObject: { id: string }) => { - valueObject.id = IDManager.getInstance().getNewID('value'); + valueObject.id = this.idManager.getNewID('value'); }); } if ('row' in newElement && newElement.rows instanceof Object) { // replace row Ids with fresh ones (likert) (newElement.rows as LikertRowElement[]).forEach((rowObject: { id: string }) => { - rowObject.id = IDManager.getInstance().getNewID('likert_row'); - }); - } - - - if (newElement instanceof ClozeElement) { - element.getChildElements().forEach((childElement: UIElement) => { - childElement.id = IDManager.getInstance().getNewID(childElement.type); - if (childElement.type === 'drop-list-simple') { // replace value Ids with fresh ones (dropList) - (childElement.value as DragNDropValueObject[]).forEach((valueObject: DragNDropValueObject) => { - valueObject.id = IDManager.getInstance().getNewID('value'); - }); - } + rowObject.id = this.idManager.getNewID('likert_row'); }); } return newElement; @@ -216,26 +211,18 @@ export class UnitService { updateElementsProperty(elements: UIElement[], property: string, - value: InputElementValue | TextImageLabel | TextImageLabel[] | ClozeDocument | - DragNDropValueObject[] | null): void { + value: InputElementValue | LikertRowElement[] | + TextLabel | TextLabel[] | ClozeDocument | null): void { console.log('updateElementProperty', elements, property, value); elements.forEach(element => { if (property === 'id') { - if (!IDManager.getInstance().isIdAvailable((value as string))) { // prohibit existing IDs + if (!this.idManager.isIdAvailable((value as string))) { // prohibit existing IDs this.messageService.showError(this.translateService.instant('idTaken')); } else { - IDManager.getInstance().removeId(element.id); - IDManager.getInstance().addID(value as string); + this.idManager.removeId(element.id); + this.idManager.addID(value as string); element.id = value as string; } - } else if (element.type === 'likert' && property === 'columns') { - (element as LikertElement).rows.forEach(row => { - row.columnCount = (element as LikertElement).columns.length; - }); - } else if (element.type === 'likert' && property === 'readOnly') { - (element as LikertElement).rows.forEach(row => { - row.readOnly = value as boolean; - }); } else { element.setProperty(property, value); } @@ -244,11 +231,11 @@ export class UnitService { this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit); } - updateSelectedElementsPositionProperty(property: string, value: any): void { + updateSelectedElementsPositionProperty(property: string, value: UIElementValue): void { this.updateElementsPositionProperty(this.selectionService.getSelectedElements(), property, value); } - updateElementsPositionProperty(elements: UIElement[], property: string, value: any): void { + updateElementsPositionProperty(elements: UIElement[], property: string, value: UIElementValue): void { elements.forEach(element => { element.setPositionProperty(property, value); }); @@ -256,7 +243,7 @@ export class UnitService { this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit); } - updateSelectedElementsStyleProperty(property: string, value: any): void { + updateSelectedElementsStyleProperty(property: string, value: UIElementValue): void { const elements = this.selectionService.getSelectedElements(); elements.forEach(element => { element.setStyleProperty(property, value); @@ -265,7 +252,7 @@ export class UnitService { this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit); } - updateElementsPlayerProperty(elements: UIElement[], property: string, value: any): void { + updateElementsPlayerProperty(elements: UIElement[], property: string, value: UIElementValue): void { elements.forEach(element => { element.setPlayerProperty(property, value); }); @@ -379,7 +366,9 @@ export class UnitService { case 'video': this.dialogService.showPlayerEditDialog((element as PlayerElement).player) .subscribe((result: PlayerProperties) => { - Object.keys(result).forEach(key => this.updateElementsPlayerProperty([element], key, result[key])); + Object.keys(result).forEach( + key => this.updateElementsPlayerProperty([element], key, result[key] as UIElementValue) + ); }); break; // no default @@ -387,7 +376,7 @@ export class UnitService { } getNewValueID(): string { - return IDManager.getInstance().getNewID('value'); + return this.idManager.getNewID('value'); } /* Used by props panel to show available dropLists to connect */ diff --git a/projects/editor/src/app/services/verona-api.service.ts b/projects/editor/src/app/services/verona-api.service.ts index 79e97ce8bf9739f768de7d2bb0cf809dca5ed2ba..d35bd174b79745e472eeebb64524210ef8ab2dbc 100644 --- a/projects/editor/src/app/services/verona-api.service.ts +++ b/projects/editor/src/app/services/verona-api.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@angular/core'; import { fromEvent, Observable, Subject } from 'rxjs'; -import packageJSON from '../../../../../package.json'; import { Unit } from 'common/models/unit'; import { AnswerScheme } from 'common/models/elements/element'; +import packageJSON from '../../../../../package.json'; @Injectable({ providedIn: 'root' @@ -12,7 +12,7 @@ export class VeronaAPIService { private _voeStartCommand = new Subject<VoeStartCommand>(); // TODO proper interfaces private _voeGetDefinitionRequest = new Subject<VoeGetDefinitionRequest>(); - private isStandalone = (): boolean => window === window.parent; + private isStandalone = window === window.parent; constructor() { fromEvent(window, 'message') @@ -37,7 +37,7 @@ export class VeronaAPIService { private send(message: Record<string, string | AnswerScheme[]>): void { // prevent posts in local (dev) mode - if (!this.isStandalone()) { + if (!this.isStandalone) { window.parent.postMessage(message, '*'); } else { // console.log(`player: ${message.type}`); diff --git a/projects/editor/src/app/text-editor-simple/rich-text-editor-simple.component.css b/projects/editor/src/app/text-editor-simple/rich-text-editor-simple.component.css index cd115f951937d19dd772736efabbf899ac634a48..4790c9456292741f6bca04defdcf2cc69566a8f1 100644 --- a/projects/editor/src/app/text-editor-simple/rich-text-editor-simple.component.css +++ b/projects/editor/src/app/text-editor-simple/rich-text-editor-simple.component.css @@ -1,5 +1,5 @@ :host ::ng-deep div.ProseMirror { - min-height: 100px; + min-height: 60px; border: 1px solid; } @@ -9,7 +9,6 @@ } .editor-control-panel { - margin-bottom: 20px; background: linear-gradient(to top right, #FFF5F8, #FAFAFA); font: unset; max-width: 1000px; diff --git a/projects/editor/src/app/text-editor-simple/rich-text-editor-simple.component.html b/projects/editor/src/app/text-editor-simple/rich-text-editor-simple.component.html index 2f0badcbef9f8a8d5fc34b3e93b3058d883a441b..163e3a2d3604b71f9d784df1ae6f8ce44fc23644 100644 --- a/projects/editor/src/app/text-editor-simple/rich-text-editor-simple.component.html +++ b/projects/editor/src/app/text-editor-simple/rich-text-editor-simple.component.html @@ -1,4 +1,4 @@ -<div fxLayout="row" class="editor-control-panel" mat-dialog-title> +<div fxLayout="row" class="editor-control-panel"> <div fxLayout="row"> <fieldset fxLayout="row"> <legend>Schriftauszeichnung</legend> @@ -112,7 +112,7 @@ </fieldset> </div> -<tiptap-editor [editor]="editor" [ngModel]="content" mat-dialog-content +<tiptap-editor [editor]="editor" [ngModel]="content" [style.font-size.px]="defaultFontSize" (ngModelChange)="contentChange.emit($event)"> </tiptap-editor> diff --git a/projects/editor/src/app/text-editor-simple/rich-text-editor-simple.component.ts b/projects/editor/src/app/text-editor-simple/rich-text-editor-simple.component.ts index 4ceacaf227a6704188247a62455115c75e89ab9d..fafc7488ebf02b572a0c0de4aada30836ca5be1b 100644 --- a/projects/editor/src/app/text-editor-simple/rich-text-editor-simple.component.ts +++ b/projects/editor/src/app/text-editor-simple/rich-text-editor-simple.component.ts @@ -2,7 +2,6 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Editor, mergeAttributes } from '@tiptap/core'; -import StarterKit from '@tiptap/starter-kit'; import { Underline } from '@tiptap/extension-underline'; import { Superscript } from '@tiptap/extension-superscript'; import { Subscript } from '@tiptap/extension-subscript'; @@ -10,6 +9,12 @@ import { Paragraph } from '@tiptap/extension-paragraph'; import { TextStyle } from '@tiptap/extension-text-style'; import { Color } from '@tiptap/extension-color'; import { Highlight } from '@tiptap/extension-highlight'; +import { Document } from '@tiptap/extension-document'; +import { Text } from '@tiptap/extension-text'; +import { ListItem } from '@tiptap/extension-list-item'; +import { Strike } from '@tiptap/extension-strike'; +import { Bold } from '@tiptap/extension-bold'; +import { Italic } from '@tiptap/extension-italic'; @Component({ selector: 'aspect-rich-text-editor-simple', @@ -25,8 +30,10 @@ export class RichTextEditorSimpleComponent { selectedHighlightColor: string = 'lightgrey'; editor = new Editor({ - extensions: [StarterKit, Underline, Superscript, Subscript, + extensions: [Document, Text, ListItem, + Underline, Superscript, Subscript, TextStyle, Color, + Bold, Italic, Strike, Highlight.configure({ multicolor: true }), diff --git a/projects/editor/src/app/text-editor/extensions/hanging-indent.ts b/projects/editor/src/app/text-editor/extensions/hanging-indent.ts index 7442817b58266280d3a3abfc4150bd69b6f7ca01..581d00878945b5c266e534a5d9521b9212aac5b7 100644 --- a/projects/editor/src/app/text-editor/extensions/hanging-indent.ts +++ b/projects/editor/src/app/text-editor/extensions/hanging-indent.ts @@ -57,7 +57,7 @@ export const HangingIndent = Extension.create({ const updateIndentLevel = (tr: Transaction, hangingIndent: boolean, indentSize: number): Transaction => { const { doc, selection } = tr; - if (doc && selection && (selection instanceof TextSelection || selection instanceof AllSelection)) { + if (doc && selection && (selection instanceof TextSelection)) { const { from, to } = selection; doc.nodesBetween(from, to, (node, pos) => { setNodeIndentMarkup(tr, pos, hangingIndent, indentSize); diff --git a/projects/editor/src/app/text-editor/rich-text-editor.component.css b/projects/editor/src/app/text-editor/rich-text-editor.component.css index 2add2430f5e55778e6cfc197a2e3aa95fb87bec4..fad268e1740fe23c3db635fde262c9df648c5c3c 100644 --- a/projects/editor/src/app/text-editor/rich-text-editor.component.css +++ b/projects/editor/src/app/text-editor/rich-text-editor.component.css @@ -1,5 +1,8 @@ +tiptap-editor { + height: 57%; +} :host ::ng-deep div.ProseMirror { - min-height: 300px; + height: 99%; border: 1px solid; } diff --git a/projects/editor/src/app/text-editor/rich-text-editor.component.html b/projects/editor/src/app/text-editor/rich-text-editor.component.html index 4991d39c035fae146d57bc84d5e6e36f35ab6d5a..07efd514eb517757bedf3ed4abbc48b4e4a56aa8 100644 --- a/projects/editor/src/app/text-editor/rich-text-editor.component.html +++ b/projects/editor/src/app/text-editor/rich-text-editor.component.html @@ -208,6 +208,8 @@ <mat-menu #specialCharsMenu="matMenu" yPosition="above"> <button mat-button (click)="insertSpecialChar(' ')" [matTooltip]="'Geschütztes Leerzeichen'">␣</button> + <button mat-button (click)="insertSpecialChar(' ')" + [matTooltip]="'Geschütztes Leerzeichen (schmal)'">␣<sub>2</sub></button> <button mat-button (click)="insertSpecialChar('–')">–</button> <button mat-button (click)="insertSpecialChar('♀')">♀</button> <button mat-button (click)="insertSpecialChar('♂')">♂</button> @@ -252,7 +254,7 @@ <button mat-button (click)="insertSpecialChar('♦')">♦</button> </mat-menu> <button mat-icon-button matTooltip="Bild" [matTooltipShowDelay]="300" - (click)="addImage()"> + (click)="insertImage()"> <mat-icon>image</mat-icon> </button> <button mat-icon-button matTooltip="Zitat" [matTooltipShowDelay]="300" diff --git a/projects/editor/src/app/text-editor/rich-text-editor.component.ts b/projects/editor/src/app/text-editor/rich-text-editor.component.ts index c5f4162f03ca467773a32603a05e8a041c675f55..26eda925dee280bd9efc0b9a07b7f9fabc724142 100644 --- a/projects/editor/src/app/text-editor/rich-text-editor.component.ts +++ b/projects/editor/src/app/text-editor/rich-text-editor.component.ts @@ -3,7 +3,6 @@ import { AfterViewInit, Injector, OnInit } from '@angular/core'; import { Editor } from '@tiptap/core'; -import StarterKit from '@tiptap/starter-kit'; import { Underline } from '@tiptap/extension-underline'; import { Superscript } from '@tiptap/extension-superscript'; import { Subscript } from '@tiptap/extension-subscript'; @@ -14,13 +13,19 @@ import { TextAlign } from '@tiptap/extension-text-align'; import { Heading } from '@tiptap/extension-heading'; import { Image } from '@tiptap/extension-image'; import { Blockquote } from '@tiptap/extension-blockquote'; +import { Document } from '@tiptap/extension-document'; +import { Text } from '@tiptap/extension-text'; +import { ListItem } from '@tiptap/extension-list-item'; +import { Bold } from '@tiptap/extension-bold'; +import { Italic } from '@tiptap/extension-italic'; +import { Strike } from '@tiptap/extension-strike'; +import { FileService } from 'common/services/file.service'; import { Indent } from './extensions/indent'; import { HangingIndent } from './extensions/hanging-indent'; import { ParagraphExtension } from './extensions/paragraph-extension'; import { FontSize } from './extensions/font-size'; import { BulletListExtension } from './extensions/bullet-list'; import { OrderedListExtension } from './extensions/ordered-list'; -import { FileService } from 'common/services/file.service'; import ToggleButtonComponentExtension from './angular-node-views/toggle-button-component-extension'; import DropListComponentExtension from './angular-node-views/drop-list-component-extension'; import TextFieldComponentExtension from './angular-node-views/text-field-component-extension'; @@ -43,8 +48,11 @@ export class RichTextEditorComponent implements OnInit, AfterViewInit { bulletListStyle: string = 'disc'; orderedListStyle: string = 'decimal'; - defaultExtensions = [StarterKit, Underline, Superscript, Subscript, + defaultExtensions = [ + Document, Text, ListItem, + Underline, Superscript, Subscript, TextStyle, Color, + Bold, Italic, Strike, Highlight.configure({ multicolor: true }), @@ -66,6 +74,7 @@ export class RichTextEditorComponent implements OnInit, AfterViewInit { HangingIndent, Image.configure({ inline: true, + allowBase64: true, HTMLAttributes: { style: 'display: inline-block; height: 1em; vertical-align: middle' } @@ -202,7 +211,7 @@ export class RichTextEditorComponent implements OnInit, AfterViewInit { this.editor.commands.unhangIndent(this.selectedIndentSize); } - async addImage(): Promise<void> { + async insertImage(): Promise<void> { const mediaSrc = await FileService.loadImage(); this.editor.commands.setImage({ src: mediaSrc }); } diff --git a/projects/editor/src/assets/i18n/de.json b/projects/editor/src/assets/i18n/de.json index a11c716c717a6be55ba8acdd171a28708db712e8..3a06ca2f90bbc34d9bd6897f606df41162f3c551 100644 --- a/projects/editor/src/assets/i18n/de.json +++ b/projects/editor/src/assets/i18n/de.json @@ -4,6 +4,8 @@ "page": "Seite", "rows": "Zeilen", "columns": "Spalten", + "column": "Spalte", + "row": "Zeile", "forward": "nach vorn", "backward": "nach hinten", "delete": "Löschen", @@ -60,8 +62,8 @@ "useMinHeight": "Mindesthöhe setzen", "minHeight": "Mindesthöhe", "grid": "Raster", - "column": "Spalte", - "row": "Zeile", + "column": "vertikal", + "row": "horizontal", "columnRange": "Spaltenspanne", "rowRange": "Zeilenspanne", "margin": "Abstand", @@ -74,7 +76,7 @@ "id": "ID", "label": "Beschriftung", "text": "Text", - "borderRadius": "Kantenradius", + "borderRadius": "Radius", "highlightable": "Markieren erlauben", "highlightableYellow": "Gelb", "highlightableTurquoise": "Türkis", @@ -100,11 +102,12 @@ "true": "wahr", "false": "falsch", "appearance": "Aussehen", - "strikeOtherOptions": "Andere Optionen durchstreichen", + "strikeOtherOptions": "Nicht gewählte Optionen durchstreichen", + "strikeSelectedOption": "Gewählte Option durchstreichen", "minLength": "Minimallänge", "minValue": "Minimalwert", "minLengthWarnMessage": "Minimalwert Warnmeldung", - "maxLength": "Maximalwert", + "maxLength": "Maximallänge", "maxValue": "Maximalwert", "showValues": "Zeige Start- und Endwert", "barStyle": "Zahlenstrahl-Modus", @@ -126,7 +129,7 @@ "inputAssistancePosition": "Eingabehilfeposition", "floating": "schwebend", "showSoftwareKeyboard": "Tastatur einblenden", - "softwareKeyboardShowFrench": "Französische Sonderzeichen", + "softwareKeyboardShowFrench": "Tastatur: Französische Sonderzeichen", "lineColoring": "Zeilenfärbung", "lineColoringColor": "Zeilenfarbe", "scale": "Skalieren", @@ -144,9 +147,9 @@ "deleteElement": "Element löschen", "noElementSelected": "Kein Element ausgewählt", "spellCorrectButtonLabel": "Wort zum Korrigieren", - "borderColor": "Randfarbe", - "borderWidth": "Randstärke", - "borderStyle": "Linienstil", + "borderColor": "Farbe", + "borderWidth": "Stärke", + "borderStyle": "Stil", "selectionColor": "Auswahlfarbe", "flex": "Dynamisch zentriert", "fixedSize": "Feste Abmessungen", @@ -157,7 +160,11 @@ "outline": "Umrandet", "dynamicWidth": "Dynamische Breite", "verticalOrientation": "vertikale Ausrichtung", - "copyOnDrop": "Elemente kopieren" + "copyOnDrop": "Elemente kopieren", + "hasBorderBottom": "Kante oben", + "hasBorderTop": "Kante unten", + "hasBorderLeft": "Kante links", + "hasBorderRight": "Kante rechts" }, "player": { "autoStart": "Autostart (nicht für Tablets)", @@ -187,20 +194,24 @@ "button": "Navigationsknopf", "frame": "Rahmen", "text-field": "Eingabefeld", + "text-field-simple": "Eingabefeld", "text-area": "Eingabebereich", "checkbox": "Kontrollkästchen", "dropdown": "Klappliste", "radio": "Optionsfelder", + "radio-group-images": "Optionsfelder mit Bild", "simple": "mit Text", "complex": "mit Bild", "drop-list": "Ablegeliste", + "drop-list-simple": "Ablegeliste", "image": "Bild", "audio": "Audio", "video": "Video", "likert": "Optionentabelle", "cloze": "Lückentext", "slider": "Schieberegler", - "spell-correct": "Wort korrigieren" + "spell-correct": "Wort korrigieren", + "toggle-button": "Optionsfeld" }, "section-menu": { "height": "Höhe", @@ -219,5 +230,8 @@ "Bitte kopierten Abschnitt einfügen": "Bitte kopierten Abschnitt einfügen", "Doppelte IDs festgestellt. Weiter mit neu generierten IDs?": "Doppelte IDs festgestellt. Weiter mit neu generierten IDs?", "Abschnitt wurde erfolgreich gelesen.": "Abschnitt wurde erfolgreich gelesen.", - "Fehler beim Lesen des Abschnitts!": "Fehler beim Lesen des Abschnitts!" + "Fehler beim Lesen des Abschnitts!": "Fehler beim Lesen des Abschnitts!", + "limitItemPerRow": "Elemente je Zeile begrenzen", + "numberGreater0": "Zahlenwert größer 0", + "itemsPerRow": "Anzahl der Elemente" } diff --git a/projects/editor/src/styles.css b/projects/editor/src/styles.css index a6e109e2280b070a58c412ab458211ee452bf4ab..096a900cbfd93d3ef6f5039ccad6e69f54bc1e9a 100644 --- a/projects/editor/src/styles.css +++ b/projects/editor/src/styles.css @@ -2,17 +2,7 @@ html, body { height: 100%; } .mat-expansion-panel-content {font: unset !important;} -.snackbar-warning {border: 3px double #ff4d4d} -.snackbar-error {background-color: #ff4d4d} -.snackbar-success {background-color: green} - -.cdk-drag { - cursor: grab; -} -.cdk-drag-dragging { - cursor: grabbing; -} - +/* TODO needed? */ .mat-dialog-content { border: 1px solid lightgray; margin: -13px !important; diff --git a/projects/player/modules/logging/services/log.service.ts b/projects/player/modules/logging/services/log.service.ts index 5083b13705036a12703bcde1cca9979c6e260324..22aefe204cd96fd62139b629677f2a3c3460fb3d 100644 --- a/projects/player/modules/logging/services/log.service.ts +++ b/projects/player/modules/logging/services/log.service.ts @@ -11,28 +11,28 @@ export class LogService { // eslint-disable-next-line @typescript-eslint/no-explicit-any static error(...args: any[]): void { if (LogService.level <= LogLevel.ERROR) { - window.console.error.apply( console, args ); + window.console.error.apply(console, args); } } // eslint-disable-next-line @typescript-eslint/no-explicit-any static warn(...args: any[]): void { if (LogService.level <= LogLevel.WARN) { - window.console.warn.apply( console, args ); + window.console.warn.apply(console, args); } } // eslint-disable-next-line @typescript-eslint/no-explicit-any static info(...args: any[]): void { if (LogService.level <= LogLevel.INFO) { - window.console.info.apply( console, args ); + window.console.info.apply(console, args); } } // eslint-disable-next-line @typescript-eslint/no-explicit-any static log(...args: any[]): void { if (LogService.level <= LogLevel.LOG) { - window.console.log.apply( console, args ); + window.console.log.apply(console, args); } } } diff --git a/projects/player/src/app/app.module.ts b/projects/player/src/app/app.module.ts index ddc8d997896048ce69a5d70b94e27ac03ca7c8b9..2a97aad7adc05cdb4173d258e422f615dd903101 100644 --- a/projects/player/src/app/app.module.ts +++ b/projects/player/src/app/app.module.ts @@ -5,10 +5,12 @@ import { CommonModule } from '@angular/common'; import { createCustomElement } from '@angular/elements'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { OverlayModule } from '@angular/cdk/overlay'; +import { SharedModule } from 'common/shared.module'; +import { KeyInputModule } from 'player/modules/key-input/key-input.module'; +import { UnitMenuModule } from 'player/modules/unit-menu/unit-menu.module'; import { AppComponent } from './app.component'; import { PageComponent } from './components/page/page.component'; import { SectionComponent } from './components/section/section.component'; -import { SharedModule } from 'common/shared.module'; import { PlayerTranslateLoader } from './classes/player-translate-loader'; import { PagesLayoutComponent } from './components/layouts/pages-layout/pages-layout.component'; import { PageLabelDirective } from './directives/page-label.directive'; @@ -36,9 +38,7 @@ import { InteractiveGroupElementComponent } from './components/elements/interactive-group-element/interactive-group-element.component'; import { PlayerLayoutComponent } from './components/layouts/player-layout/player-layout.component'; -import { KeyInputModule } from 'player/modules/key-input/key-input.module'; import { UnitStateDirective } from './directives/unit-state.directive'; -import { UnitMenuModule } from 'player/modules/unit-menu/unit-menu.module'; import { ValidPagesPipe } from './pipes/valid-pages.pipe'; import { ScrollPagesPipe } from './pipes/scroll-pages.pipe'; import { AlwaysVisiblePagePipe } from './pipes/always-visible-page.pipe'; @@ -46,6 +46,7 @@ import { PageIndexPipe } from './pipes/page-index.pipe'; import { PlayerStateDirective } from './directives/player-state.directive'; import { SectionVisibilityHandlingDirective } from './directives/section-visibility-handling.directive'; import { UnitComponent } from './components/unit/unit.component'; +import { PageScrollButtonComponent } from './components/page-scroll-button/page-scroll-button.component'; @NgModule({ declarations: [ @@ -75,7 +76,8 @@ import { UnitComponent } from './components/unit/unit.component'; PageIndexPipe, PlayerStateDirective, SectionVisibilityHandlingDirective, - UnitComponent + UnitComponent, + PageScrollButtonComponent ], imports: [ BrowserModule, diff --git a/projects/player/src/app/components/elements/base-group-element/base-group-element.component.ts b/projects/player/src/app/components/elements/base-group-element/base-group-element.component.ts index 81fccb9f14aaaefa96315c390d4516d85040675e..80df78cd5ccf5d42be19286b364e1bf075313150 100644 --- a/projects/player/src/app/components/elements/base-group-element/base-group-element.component.ts +++ b/projects/player/src/app/components/elements/base-group-element/base-group-element.component.ts @@ -22,7 +22,7 @@ export class BaseGroupElementComponent extends ElementGroupDirective implements ngOnInit(): void { this.baseElementComponent = - this.elementComponentContainer.createComponent(this.elementModel.getComponentFactory()).instance; + this.elementComponentContainer.createComponent(this.elementModel.getElementComponent()).instance; this.baseElementComponent.elementModel = this.elementModel; } diff --git a/projects/player/src/app/components/layouts/pages-layout/pages-layout.component.css b/projects/player/src/app/components/layouts/pages-layout/pages-layout.component.css index 24c90f70688c6a3ebca611da04ac4a1ac8c7ea8c..f711044e92da311fed65016c8edc0583c449d118 100644 --- a/projects/player/src/app/components/layouts/pages-layout/pages-layout.component.css +++ b/projects/player/src/app/components/layouts/pages-layout/pages-layout.component.css @@ -3,8 +3,8 @@ } .concat-scroll-snap { - scroll-snap-type: both mandatory; - scroll-padding: 10px; + scroll-snap-type: y mandatory; + scroll-padding: 0; } .page-container{ diff --git a/projects/player/src/app/components/layouts/pages-layout/pages-layout.component.html b/projects/player/src/app/components/layouts/pages-layout/pages-layout.component.html index b306807775f34cc3ad3ad9ca5a49dde9ac8014ef..626bd0d543ff6a7ffb376419b4923ec9abeafc69 100644 --- a/projects/player/src/app/components/layouts/pages-layout/pages-layout.component.html +++ b/projects/player/src/app/components/layouts/pages-layout/pages-layout.component.html @@ -9,8 +9,10 @@ </div> <ng-template #alwaysVisiblePageView> - <div *ngIf="alwaysVisiblePage" + <aspect-page-scroll-button + *ngIf="alwaysVisiblePage" class="page-container" + [isSnapMode]="false" [style.max-height.%]="aspectRatioColumn.alwaysVisiblePage" [style.max-width.%]="aspectRatioRow.alwaysVisiblePage"> <div #alwaysVisiblePageContainer @@ -33,15 +35,18 @@ </aspect-page> </div> </div> - </div> + </aspect-page-scroll-button> </ng-template> <ng-template #scrollPagesView> <ng-container *ngIf="hasScrollPages"> - <div class="page-container" + <aspect-page-scroll-button + class="page-container" + [isSnapMode]="scrollPageMode === 'concat-scroll-snap'" [class.concat-scroll-snap]="scrollPageMode === 'concat-scroll-snap'" [style.max-height.%]="aspectRatioColumn.scrollPages" - [style.max-width.%]="aspectRatioRow.scrollPages"> + [style.max-width.%]="aspectRatioRow.scrollPages" + (scrollToNextPage)="scrollToNextPage()"> <div #pagesScrolledContainer [class.center-pages]="layoutAlignment === 'column' || !alwaysVisiblePage" [class.left-container]="alwaysVisiblePage && alwaysVisiblePagePosition === 'right'" @@ -51,7 +56,7 @@ [ngTemplateOutletContext]="{pagesContainer: pagesScrolledContainer}"> </ng-container> </div> - </div> + </aspect-page-scroll-button> </ng-container> </ng-template> @@ -83,7 +88,9 @@ <ng-template #scrollPagesScrolledView let-pagesContainer> <ng-container *ngFor="let page of scrollPages; let i = index; let last = last"> - <div [style.min-height]="'calc(100vh - ' + (page.margin * 2) + 'px)'" + <div [style.min-height]="scrollPageMode === 'concat-scroll-snap' ? + 'calc(100vh - ' + (page.margin * 2) + 'px)' : + 'unset'" [style.background-color]="page.backgroundColor" [class.concat-scroll-snap-align]="scrollPageMode === 'concat-scroll-snap'" [style.max-width]="page.hasMaxWidth ? page.maxWidth + 'px' : '100%'" @@ -97,6 +104,7 @@ </div> <aspect-page [pageIndex]="pages | pageIndex: page" + [scrollPageIndex]="i" [pagesContainer]="pagesContainer" [page]="page" [isLastPage]="last" diff --git a/projects/player/src/app/components/layouts/pages-layout/pages-layout.component.ts b/projects/player/src/app/components/layouts/pages-layout/pages-layout.component.ts index bd413325b70689ca9cc37561d5a18534d517de7d..cf2ae2f82cf88f34c1a44e3a2126cb69af13d89c 100644 --- a/projects/player/src/app/components/layouts/pages-layout/pages-layout.component.ts +++ b/projects/player/src/app/components/layouts/pages-layout/pages-layout.component.ts @@ -3,12 +3,12 @@ import { } from '@angular/core'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; -import { NativeEventService } from '../../../services/native-event.service'; import { Page } from 'common/models/page'; import { VeronaPostService } from 'player/modules/verona/services/verona-post.service'; import { NavigationService } from 'player/src/app/services/navigation.service'; import { VopPageNavigationCommand } from 'player/modules/verona/models/verona'; import { VeronaSubscriptionService } from 'player/modules/verona/services/verona-subscription.service'; +import { NativeEventService } from '../../../services/native-event.service'; @Component({ selector: 'aspect-pages-layout', @@ -134,7 +134,7 @@ export class PagesLayoutComponent implements OnInit, AfterViewInit, OnDestroy { } private calculatePagesMaxWidth(): void { - this.maxWidth.alwaysVisiblePage = this.getAbsolutePageWidth(this.alwaysVisiblePage); + this.maxWidth.alwaysVisiblePage = PagesLayoutComponent.getAbsolutePageWidth(this.alwaysVisiblePage); this.maxWidth.scrollPages = this.getScrollPagesWidth(); this.maxWidth.allPages = Math.max(this.maxWidth.alwaysVisiblePage, this.maxWidth.scrollPages); } @@ -153,13 +153,19 @@ export class PagesLayoutComponent implements OnInit, AfterViewInit, OnDestroy { private getScrollPagesWidth(): number { return this.hasScrollPages ? - Math.max(...this.scrollPages.map((page: Page): number => this.getAbsolutePageWidth(page))) : 0; + Math.max(...this.scrollPages.map((page: Page): number => PagesLayoutComponent.getAbsolutePageWidth(page))) : 0; } - private getAbsolutePageWidth = (page: Page | null): number => ((page) ? 2 * page.margin + page.maxWidth : 0); + private static getAbsolutePageWidth = (page: Page | null): number => ((page) ? 2 * page.margin + page.maxWidth : 0); ngOnDestroy(): void { this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); } + + scrollToNextPage() { + if (this.selectedIndex < this.scrollPages.length - 1) { + this.selectIndex.next(this.selectedIndex + 1); + } + } } diff --git a/projects/player/src/app/components/page-scroll-button/page-scroll-button.component.html b/projects/player/src/app/components/page-scroll-button/page-scroll-button.component.html new file mode 100644 index 0000000000000000000000000000000000000000..ba9a935a287c536326fdb06da560d4f08b23210b --- /dev/null +++ b/projects/player/src/app/components/page-scroll-button/page-scroll-button.component.html @@ -0,0 +1,17 @@ +<ng-content></ng-content> +<div fxLayout="column" fxLayoutAlign="center end" + class="scroll-button-container"> + <button *ngIf="isVisible.value" + mat-fab + class="scroll-button" + (contextmenu)="$event.preventDefault()" + (pointercancel)="toggleScrolling(false)" + (pointerout)="toggleScrolling(false)" + (pointerdown)="toggleScrolling(true)" + (pointerup)="toggleScrolling(false)" + (pointerleave)="toggleScrolling(false)"> + <mat-icon>arrow_downward</mat-icon> + </button> +</div> + + diff --git a/projects/player/src/app/components/page-scroll-button/page-scroll-button.component.scss b/projects/player/src/app/components/page-scroll-button/page-scroll-button.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..616da2a411d0f764e084b13b46bfe26a6cb68e56 --- /dev/null +++ b/projects/player/src/app/components/page-scroll-button/page-scroll-button.component.scss @@ -0,0 +1,16 @@ +.scroll-button { + background-color: #cccccc; + opacity: 0.6; + pointer-events: all; + &:hover { + opacity: 0.8; + } +} + +.scroll-button-container { + height: 0; + padding-right: 15px; + position: sticky; + bottom: 40px; + pointer-events: none; +} diff --git a/projects/player/src/app/components/page-scroll-button/page-scroll-button.component.spec.ts b/projects/player/src/app/components/page-scroll-button/page-scroll-button.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..0c79ac29126e149256e13a8e60ef11bb97ae9e64 --- /dev/null +++ b/projects/player/src/app/components/page-scroll-button/page-scroll-button.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PageScrollButtonComponent } from './page-scroll-button.component'; + +describe('PageScrollButtonComponent', () => { + let component: PageScrollButtonComponent; + let fixture: ComponentFixture<PageScrollButtonComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [PageScrollButtonComponent] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PageScrollButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/player/src/app/components/page-scroll-button/page-scroll-button.component.ts b/projects/player/src/app/components/page-scroll-button/page-scroll-button.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..97c65dba4d0739de83a17b159d46d95ce6bf09e4 --- /dev/null +++ b/projects/player/src/app/components/page-scroll-button/page-scroll-button.component.ts @@ -0,0 +1,77 @@ +import { + AfterViewInit, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, OnDestroy, Output +} from '@angular/core'; +import { BehaviorSubject, Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + +@Component({ + selector: 'aspect-page-scroll-button', + templateUrl: './page-scroll-button.component.html', + styleUrls: ['./page-scroll-button.component.scss'] +}) +export class PageScrollButtonComponent implements AfterViewInit, OnDestroy { + @HostListener('scroll', ['$event.target']) + onScroll(element: HTMLElement) { + this.checkScrollPosition(element); + } + + @Input() isSnapMode!: boolean; + + @Output() scrollToNextPage: EventEmitter<void> = new EventEmitter<void>(); + + isVisible: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); + scrollIntervalId!: number; + + private ngUnsubscribe = new Subject<void>(); + + constructor(private elementRef: ElementRef, + private changeDetectorRef: ChangeDetectorRef) { + this.isVisible + .pipe(takeUntil(this.ngUnsubscribe)) + .subscribe(value => { + if (!value) { + this.clearScrollIng(); + } + }); + } + + ngAfterViewInit(): void { + this.checkScrollPosition(this.elementRef.nativeElement); + this.changeDetectorRef.detectChanges(); + } + + private checkScrollPosition(element: HTMLElement): void { + this.isVisible.next(element.scrollHeight - element.offsetHeight > element.scrollTop); + } + + toggleScrolling(scrolling: boolean) { + if (scrolling) { + this.scrollIntervalId = setInterval(() => { + this.scrollDown(); + }); + } else { + setTimeout(() => this.clearScrollIng(), 0); + } + } + + scrollDown(): void { + const lastScrollTop = this.elementRef.nativeElement.scrollTop; + this.elementRef.nativeElement.scrollTop = lastScrollTop + 2; + if (this.isSnapMode && (this.elementRef.nativeElement.scrollTop !== lastScrollTop + 2)) { + this.clearScrollIng(); + // FF needs time to finish concat scroll anmations before setting a new page index + setTimeout(() => this.scrollToNextPage.emit(), 100); + } + } + + private clearScrollIng(): void { + if (this.scrollIntervalId) { + clearInterval(this.scrollIntervalId); + } + } + + ngOnDestroy(): void { + this.ngUnsubscribe.next(); + this.ngUnsubscribe.complete(); + } +} diff --git a/projects/player/src/app/components/page/page.component.html b/projects/player/src/app/components/page/page.component.html index 2a646e071d4f7adaf2ea39d69b1f537726d8e27b..961be3cc6e22d70d1798fdcef9142a5343a12fdb 100644 --- a/projects/player/src/app/components/page/page.component.html +++ b/projects/player/src/app/components/page/page.component.html @@ -1,7 +1,7 @@ <div aspectInViewDetection detectionType="top" [intersectionContainer]="pagesContainer" - (intersecting)="selectedIndexChange.emit(pageIndex)"> + (intersecting)="selectedIndexChange.emit(scrollPageIndex)"> <aspect-section *ngFor="let section of page.sections" class="section" @@ -18,5 +18,5 @@ <div aspectInViewDetection detectionType="bottom" [intersectionContainer]="pagesContainer" - (intersecting)="selectedIndexChange.emit(pageIndex)"> + (intersecting)="selectedIndexChange.emit(scrollPageIndex)"> </div> diff --git a/projects/player/src/app/components/page/page.component.ts b/projects/player/src/app/components/page/page.component.ts index 566cf711e58f64966c76741d64049c733ed8990b..eb08b27574d33d06466e6d0c2e31d447ce83e2fd 100644 --- a/projects/player/src/app/components/page/page.component.ts +++ b/projects/player/src/app/components/page/page.component.ts @@ -1,8 +1,8 @@ import { Component, Input, Output, EventEmitter } from '@angular/core'; -import { MediaPlayerService } from '../../services/media-player.service'; import { Page } from 'common/models/page'; +import { MediaPlayerService } from '../../services/media-player.service'; @Component({ selector: 'aspect-page', @@ -14,6 +14,7 @@ export class PageComponent { @Input() page!: Page; @Input() isLastPage!: boolean; @Input() pageIndex!: number; + @Input() scrollPageIndex!: number; @Input() pagesContainer!: HTMLElement; @Output() selectedIndexChange = new EventEmitter<number>(); diff --git a/projects/player/src/app/directives/in-view-detection.directive.ts b/projects/player/src/app/directives/in-view-detection.directive.ts index 79a2cdbefaff3e5465f2af1ab0856bb1222e2eca..476e784669c6d86eeb3daddb9c44c5edcf7db3e2 100644 --- a/projects/player/src/app/directives/in-view-detection.directive.ts +++ b/projects/player/src/app/directives/in-view-detection.directive.ts @@ -1,6 +1,8 @@ import { Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import { IntersectionDetector } from '../classes/intersection-detector'; @Directive({ @@ -13,18 +15,22 @@ export class InViewDetectionDirective implements OnInit, OnDestroy { intersectionDetector!: IntersectionDetector; + private ngUnsubscribe = new Subject<void>(); + constructor(private elementRef: ElementRef) {} ngOnInit(): void { - const constraint = this.detectionType === 'top' ? '0px 0px 0px 0px' : '-95% 0px 0px 0px'; + const constraint = this.detectionType === 'top' ? '0px 0px -99% 0px' : '99% 0px 0px 0px'; this.intersectionDetector = new IntersectionDetector(this.intersectionContainer, constraint); this.intersectionDetector.observe(this.elementRef.nativeElement); - this.intersectionDetector.intersecting.subscribe(() => { - this.intersecting.emit(); - }); + this.intersectionDetector.intersecting + .pipe(takeUntil(this.ngUnsubscribe)) + .subscribe(() => this.intersecting.emit()); } ngOnDestroy(): void { + this.ngUnsubscribe.next(); + this.ngUnsubscribe.complete(); this.intersectionDetector.disconnect(this.elementRef.nativeElement); } } diff --git a/projects/player/src/app/directives/player-state.directive.ts b/projects/player/src/app/directives/player-state.directive.ts index 366af704508a2fb5235da64ad36c91f3e8ec11e1..68d71f7510a60a4ae6b374c97049cf8bad0fce19 100644 --- a/projects/player/src/app/directives/player-state.directive.ts +++ b/projects/player/src/app/directives/player-state.directive.ts @@ -1,8 +1,12 @@ -import { Directive, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { + Directive, Input, OnChanges, OnInit, SimpleChanges +} from '@angular/core'; import { PlayerState, RunningState } from 'player/modules/verona/models/verona'; -import { BehaviorSubject, Subject } from 'rxjs'; +import { + BehaviorSubject, map, merge, Subject +} from 'rxjs'; import { VeronaSubscriptionService } from 'player/modules/verona/services/verona-subscription.service'; import { VeronaPostService } from 'player/modules/verona/services/verona-post.service'; import { takeUntil } from 'rxjs/operators'; @@ -25,7 +29,6 @@ export class PlayerStateDirective implements OnInit, OnChanges { ngOnInit(): void { this.initSubscriptions(); - this.sendVopStateChangedNotification(); } ngOnChanges(changes: SimpleChanges): void { @@ -35,15 +38,15 @@ export class PlayerStateDirective implements OnInit, OnChanges { } private initSubscriptions(): void { - this.veronaSubscriptionService.vopContinueCommand - .pipe(takeUntil(this.ngUnsubscribe)) - .subscribe(() => this.setAndSendRunningState(true)); - this.veronaSubscriptionService.vopStopCommand - .pipe(takeUntil(this.ngUnsubscribe)) - .subscribe(() => this.setAndSendRunningState(false)); - this.veronaSubscriptionService.vopGetStateRequest - .pipe(takeUntil(this.ngUnsubscribe)) - .subscribe(message => this.setAndSendRunningState((!message.stop && this.state === 'running'))); + merge( + this.veronaSubscriptionService.vopContinueCommand + .pipe(map(() => true)), + this.veronaSubscriptionService.vopStopCommand + .pipe(map(() => false)), + this.veronaSubscriptionService.vopGetStateRequest + .pipe(map(message => (!message.stop && this.state === 'running'))) + ).pipe(takeUntil(this.ngUnsubscribe)) + .subscribe(isRunning => this.setAndSendRunningState(isRunning)); } private get state(): RunningState { diff --git a/projects/player/src/app/directives/scroll-to-index.directive.ts b/projects/player/src/app/directives/scroll-to-index.directive.ts index 9cc0c54148799a0e89b01445af5658268b3c6250..02c42aec571c7e301eda3fc06251c0db92287e97 100644 --- a/projects/player/src/app/directives/scroll-to-index.directive.ts +++ b/projects/player/src/app/directives/scroll-to-index.directive.ts @@ -1,22 +1,33 @@ import { - Directive, ElementRef, Input, OnInit + Directive, ElementRef, Input, OnDestroy, OnInit } from '@angular/core'; import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; @Directive({ selector: '[aspectScrollToIndex]' }) -export class ScrollToIndexDirective implements OnInit { +export class ScrollToIndexDirective implements OnInit, OnDestroy { @Input() selectIndex!: Subject<number>; @Input() index!: number; + private ngUnsubscribe = new Subject<void>(); + constructor(private elementRef: ElementRef) {} ngOnInit(): void { - this.selectIndex.subscribe((selectedIndex: number): void => { - if (selectedIndex === this.index) { - this.elementRef.nativeElement.scrollIntoView({ behavior: 'smooth' }); - } - }); + this.selectIndex + .pipe(takeUntil(this.ngUnsubscribe)) + .subscribe((selectedIndex: number): void => { + if (selectedIndex === this.index) { + // timeout is required because of side effects of concat scroll + setTimeout(() => this.elementRef.nativeElement.scrollIntoView({ behavior: 'smooth', block: 'start' })); + } + }); + } + + ngOnDestroy(): void { + this.ngUnsubscribe.next(); + this.ngUnsubscribe.complete(); } } diff --git a/projects/player/src/app/directives/unit-state.directive.ts b/projects/player/src/app/directives/unit-state.directive.ts index ad4eb142fcc4cde3b2bd92da502290782e22452f..666802c931152a484887d45aa11c6d509b805f57 100644 --- a/projects/player/src/app/directives/unit-state.directive.ts +++ b/projects/player/src/app/directives/unit-state.directive.ts @@ -1,13 +1,13 @@ import { Directive, OnDestroy, OnInit } from '@angular/core'; -import { Subject } from 'rxjs'; +import { debounceTime, merge, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { Progress, UnitState } from 'player/modules/verona/models/verona'; -import { UnitStateService } from '../services/unit-state.service'; -import { MediaPlayerService } from '../services/media-player.service'; import { VeronaSubscriptionService } from 'player/modules/verona/services/verona-subscription.service'; import { VeronaPostService } from 'player/modules/verona/services/verona-post.service'; -import { ValidationService } from '../services/validation.service'; import { LogService } from 'player/modules/logging/services/log.service'; +import { UnitStateService } from '../services/unit-state.service'; +import { MediaPlayerService } from '../services/media-player.service'; +import { ValidationService } from '../services/validation.service'; @Directive({ selector: '[aspectUnitState]' @@ -24,14 +24,15 @@ export class UnitStateDirective implements OnInit, OnDestroy { ) {} ngOnInit(): void { - this.mediaPlayerService.mediaStatusChanged - .pipe(takeUntil(this.ngUnsubscribe)) - .subscribe((): void => this.sendVopStateChangedNotification()); - this.unitStateService.presentedPageAdded - .pipe(takeUntil(this.ngUnsubscribe)) - .subscribe((): void => this.sendVopStateChangedNotification()); - this.unitStateService.elementCodeChanged - .pipe(takeUntil(this.ngUnsubscribe)) + merge( + this.mediaPlayerService.mediaStatusChanged, + this.unitStateService.pagePresented, + this.unitStateService.elementCodeChanged + ) + .pipe( + debounceTime(500), + takeUntil(this.ngUnsubscribe) + ) .subscribe((): void => this.sendVopStateChangedNotification()); } diff --git a/projects/player/src/app/services/element-model-element-code-mapping.service.spec.ts b/projects/player/src/app/services/element-model-element-code-mapping.service.spec.ts index 95b71b2495f5e4ffdc6c7f4f1acdb029b131ead1..b22e66659f2f8d1170302871c2c977688d946abb 100644 --- a/projects/player/src/app/services/element-model-element-code-mapping.service.spec.ts +++ b/projects/player/src/app/services/element-model-element-code-mapping.service.spec.ts @@ -1,5 +1,4 @@ import { TestBed } from '@angular/core/testing'; -import { ElementModelElementCodeMappingService } from './element-model-element-code-mapping.service'; import * as dropList_130 from 'test-data/element-models/drop-list_130.json'; import * as dropListSimple_131 from 'test-data/element-models/drop-list-simple_131.json'; import * as textField_130 from 'test-data/element-models/text-field_130.json'; @@ -33,6 +32,7 @@ import { RadioButtonGroupComplexElement } from 'common/models/elements/input-ele import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row'; import { ToggleButtonElement } from 'common/models/elements/compound-elements/cloze/cloze-child-elements/toggle-button'; import { DragNDropValueObject } from 'common/models/elements/element'; +import { ElementModelElementCodeMappingService } from './element-model-element-code-mapping.service'; describe('ElementModelElementCodeMappingService', () => { let service: ElementModelElementCodeMappingService; @@ -50,26 +50,26 @@ describe('ElementModelElementCodeMappingService', () => { it('should map the value of a drop-list elementModel to its elementCode value', () => { const dragNDropValueObjects: DragNDropValueObject[] = JSON.parse(JSON.stringify(dragNDropValues_01_130)).default; expect(service.mapToElementCodeValue(dragNDropValueObjects, 'drop-list')) - .toEqual( ['value_1', 'value_2', 'value_3']); + .toEqual(['value_1', 'value_2', 'value_3']); }); it('should map the value of a drop-list-simple elementModel to its elementCode value', () => { const dragNDropValueObjects: DragNDropValueObject[] = JSON.parse(JSON.stringify(dragNDropValues_01_130)).default; expect(service.mapToElementCodeValue(dragNDropValueObjects, 'drop-list-simple')) - .toEqual( ['value_1', 'value_2', 'value_3']); + .toEqual(['value_1', 'value_2', 'value_3']); }); it('should map the value of a text elementModel to its elementCode value', () => { const textValue = 'Lorem <aspect-marked style="background-color: rgb(249, 248, 113);">ipsum</aspect-marked> dolor sit amet'; expect(service.mapToElementCodeValue(textValue, 'text')) - .toEqual( ['6-11-#f9f871']); + .toEqual(['6-11-#f9f871']); }); it('should map the value of a text elementModel to its elementCode value - empty Array', () => { const textValue = 'Lorem dolor sit amet'; expect(service.mapToElementCodeValue(textValue, 'text')) - .toEqual( []); + .toEqual([]); }); it('should map the value of a audio elementModel to its elementCode value', () => { @@ -99,7 +99,6 @@ describe('ElementModelElementCodeMappingService', () => { .toBe(null); }); - it('should map the value of a radio-group-images elementModel to its elementCode value', () => { for (let i = 0; i < 10; i++) { expect(service.mapToElementCodeValue(i, 'radio-group-images')) @@ -151,82 +150,93 @@ describe('ElementModelElementCodeMappingService', () => { it('should map the value of a text-field elementModel to its elementCode value', () => { const textFieldValue = 'TEST'; expect(service.mapToElementCodeValue(textFieldValue, 'text-field')) - .toEqual( 'TEST'); + .toEqual('TEST'); }); it('should map the value of a text-field elementModel to its elementCode value', () => { const textFieldValue = null; expect(service.mapToElementCodeValue(textFieldValue, 'text-field')) - .toEqual( null); + .toEqual(null); }); it('should map the value of a text-field-simple elementModel to its elementCode value', () => { const textFieldValue = 'TEST'; expect(service.mapToElementCodeValue(textFieldValue, 'text-field-simple')) - .toEqual( 'TEST'); + .toEqual('TEST'); }); it('should map the value of a text-field-simple elementModel to its elementCode value', () => { const textFieldValue = null; expect(service.mapToElementCodeValue(textFieldValue, 'text-field-simple')) - .toEqual( null); + .toEqual(null); }); it('should map the value of a spell-correct elementModel to its elementCode value', () => { const spellCorrectValue = 'TEST'; expect(service.mapToElementCodeValue(spellCorrectValue, 'spell-correct')) - .toEqual( 'TEST'); + .toEqual('TEST'); }); it('should map the value of a spell-correct elementModel to its elementCode value', () => { const spellCorrectValue = null; expect(service.mapToElementCodeValue(spellCorrectValue, 'spell-correct')) - .toEqual( null); + .toEqual(null); }); it('should map the value of a text-area elementModel to its elementCode value', () => { const textAreaValue = 'TEST'; expect(service.mapToElementCodeValue(textAreaValue, 'text-area')) - .toEqual( 'TEST'); + .toEqual('TEST'); }); it('should map the value of a text-area elementModel to its elementCode value', () => { const textAreaValue = null; expect(service.mapToElementCodeValue(textAreaValue, 'text-area')) - .toEqual( null); + .toEqual(null); }); - // mapToElementValue it('should map an elementCode value to drop-list elementModel value', () => { - service.dragNDropValueObjects = [ + service.dragNDropValueObjects = [ { - 'stringValue': 'a', - 'id': 'value_1' + text: 'a', + id: 'value_1', + imgSrc: null, + imgPosition: 'above' }, { - 'stringValue': 'b', - 'id': 'value_2' + text: 'b', + id: 'value_2', + imgSrc: null, + imgPosition: 'above' }, { - 'stringValue': 'c', - 'id': 'value_3' + text: 'c', + id: 'value_3', + imgSrc: null, + imgPosition: 'above' }, { - 'stringValue': 'd', - 'id': 'value_4' + text: 'd', + id: 'value_4', + imgSrc: null, + imgPosition: 'above' }, { - 'stringValue': 'e', - 'id': 'value_5' + text: 'e', + id: 'value_5', + imgSrc: null, + imgPosition: 'above' } ]; const elementModel: DropListElement = JSON.parse(JSON.stringify(dropList_130)); - const expectedValue = [ + const expectedValue: DragNDropValueObject[] = [ { - 'stringValue': 'e', - 'id': 'value_5' + text: 'e', + id: 'value_5', + imgSrc: null, + imgPosition: 'above' } ]; expect(service.mapToElementModelValue(['value_5'], elementModel)) @@ -234,33 +244,45 @@ describe('ElementModelElementCodeMappingService', () => { }); it('should map an elementCode value to drop-list elementModel value', () => { - service.dragNDropValueObjects = [ + service.dragNDropValueObjects = [ { - 'stringValue': 'a', - 'id': 'value_1' + text: 'a', + id: 'value_1', + imgSrc: null, + imgPosition: 'above' }, { - 'stringValue': 'b', - 'id': 'value_2' + text: 'b', + id: 'value_2', + imgSrc: null, + imgPosition: 'above' }, { - 'stringValue': 'c', - 'id': 'value_3' + text: 'c', + id: 'value_3', + imgSrc: null, + imgPosition: 'above' }, { - 'stringValue': 'd', - 'id': 'value_4' + text: 'd', + id: 'value_4', + imgSrc: null, + imgPosition: 'above' }, { - 'stringValue': 'e', - 'id': 'value_5' + text: 'e', + id: 'value_5', + imgSrc: null, + imgPosition: 'above' } ]; const elementModel: DropListElement = JSON.parse(JSON.stringify(dropList_130)); - const expectedValue = [ + const expectedValue: DragNDropValueObject[] = [ { - 'stringValue': 'e', - 'id': 'value_5' + text: 'e', + id: 'value_5', + imgSrc: null, + imgPosition: 'above' } ]; expect(service.mapToElementModelValue(['value_5'], elementModel)) @@ -268,26 +290,36 @@ describe('ElementModelElementCodeMappingService', () => { }); it('should not map but return the drop-list-simple elementModel value', () => { - service.dragNDropValueObjects = [ + service.dragNDropValueObjects = [ { - 'stringValue': 'a', - 'id': 'value_1' + text: 'a', + id: 'value_1', + imgSrc: null, + imgPosition: 'above' }, { - 'stringValue': 'b', - 'id': 'value_2' + text: 'b', + id: 'value_2', + imgSrc: null, + imgPosition: 'above' }, { - 'stringValue': 'c', - 'id': 'value_3' + text: 'c', + id: 'value_3', + imgSrc: null, + imgPosition: 'above' }, { - 'stringValue': 'd', - 'id': 'value_4' + text: 'd', + id: 'value_4', + imgSrc: null, + imgPosition: 'above' }, { - 'stringValue': 'e', - 'id': 'value_5' + text: 'e', + id: 'value_5', + imgSrc: null, + imgPosition: 'above' } ]; const elementModel: DropListSimpleElement = JSON.parse(JSON.stringify(dropListSimple_131)); @@ -314,14 +346,14 @@ describe('ElementModelElementCodeMappingService', () => { it('should map an elementCode value to drop-list-simple elementModel value - an empty array', () => { service.dragNDropValueObjects = JSON.parse(JSON.stringify(dragNDropValues_01_130)).default; const elementModel: DropListElement = JSON.parse(JSON.stringify(dropList_130)); - expect(service.mapToElementModelValue([], elementModel )) + expect(service.mapToElementModelValue([], elementModel)) .toEqual([]); }); it('should map an elementCode value to drop-list elementModel value - an empty array', () => { service.dragNDropValueObjects = JSON.parse(JSON.stringify(dragNDropValues_01_130)).default; const elementModel: DropListSimpleElement = JSON.parse(JSON.stringify(dropListSimple_131)); - expect(service.mapToElementModelValue([], elementModel )) + expect(service.mapToElementModelValue([], elementModel)) .toEqual([]); }); @@ -347,121 +379,121 @@ describe('ElementModelElementCodeMappingService', () => { it('should map an elementCode value to audio elementModel value', () => { const elementModel: AudioElement = JSON.parse(JSON.stringify(audio_130)); - expect(service.mapToElementModelValue( 2, elementModel)) + expect(service.mapToElementModelValue(2, elementModel)) .toEqual(2); }); it('should not map but return the audio elementModel value (player.playbackTime)', () => { const elementModel: AudioElement = JSON.parse(JSON.stringify(audio_130)); - expect(service.mapToElementModelValue( undefined, elementModel)) + expect(service.mapToElementModelValue(undefined, elementModel)) .toEqual(0); }); it('should map an elementCode value to image elementModel value', () => { const elementModel: ImageElement = JSON.parse(JSON.stringify(image_130)); - expect(service.mapToElementModelValue( true, elementModel)) + expect(service.mapToElementModelValue(true, elementModel)) .toEqual(true); }); it('should not map but return the image elementModel value (magnifierUsed)', () => { const elementModel: ImageElement = JSON.parse(JSON.stringify(image_130)); - expect(service.mapToElementModelValue( undefined, elementModel)) + expect(service.mapToElementModelValue(undefined, elementModel)) .toEqual(false); }); it('should map an elementCode value to text-field elementModel value', () => { const elementModel: TextFieldElement = JSON.parse(JSON.stringify(textField_130)); - expect(service.mapToElementModelValue( 'TEST', elementModel)) + expect(service.mapToElementModelValue('TEST', elementModel)) .toEqual('TEST'); }); it('should not map but return the text-field elementModel value', () => { const elementModel: TextFieldElement = JSON.parse(JSON.stringify(textField_130)); - expect(service.mapToElementModelValue( undefined, elementModel)) + expect(service.mapToElementModelValue(undefined, elementModel)) .toEqual(null); }); it('should map an elementCode value to text-field-simple elementModel value', () => { const elementModel: TextFieldSimpleElement = JSON.parse(JSON.stringify(textFieldSimple_131)); - expect(service.mapToElementModelValue( 'TEST', elementModel)) + expect(service.mapToElementModelValue('TEST', elementModel)) .toEqual('TEST'); }); it('should not map but return the text-field-simple elementModel value', () => { const elementModel: TextFieldSimpleElement = JSON.parse(JSON.stringify(textFieldSimple_131)); - expect(service.mapToElementModelValue( undefined, elementModel)) + expect(service.mapToElementModelValue(undefined, elementModel)) .toEqual(null); }); it('should map an elementCode value to text-area elementModel value', () => { const elementModel: TextAreaElement = JSON.parse(JSON.stringify(textArea_130)); - expect(service.mapToElementModelValue( 'TEST', elementModel)) + expect(service.mapToElementModelValue('TEST', elementModel)) .toEqual('TEST'); }); it('should not map but return the text-area elementModel value', () => { const elementModel: TextAreaElement = JSON.parse(JSON.stringify(textArea_130)); - expect(service.mapToElementModelValue( undefined, elementModel)) + expect(service.mapToElementModelValue(undefined, elementModel)) .toEqual(null); }); it('should map an elementCode value to spell-correct elementModel value', () => { const elementModel: SpellCorrectElement = JSON.parse(JSON.stringify(spellCorrect_130)); - expect(service.mapToElementModelValue( 'TEST', elementModel)) + expect(service.mapToElementModelValue('TEST', elementModel)) .toEqual('TEST'); }); it('should not map but return the spell-correct elementModel value', () => { const elementModel: SpellCorrectElement = JSON.parse(JSON.stringify(spellCorrect_130)); - expect(service.mapToElementModelValue( undefined, elementModel)) + expect(service.mapToElementModelValue(undefined, elementModel)) .toEqual(null); }); it('should map an elementCode value to radio elementModel value', () => { const elementModel: RadioButtonGroupElement = JSON.parse(JSON.stringify(radio_130)); - expect(service.mapToElementModelValue( 1, elementModel)) + expect(service.mapToElementModelValue(1, elementModel)) .toEqual(0); }); it('should not map but return the radio elementModel value', () => { const elementModel: RadioButtonGroupElement = JSON.parse(JSON.stringify(radio_130)); - expect(service.mapToElementModelValue( undefined, elementModel)) + expect(service.mapToElementModelValue(undefined, elementModel)) .toEqual(null); }); it('should map an elementCode value to radio-group-images elementModel value', () => { const elementModel: RadioButtonGroupComplexElement = JSON.parse(JSON.stringify(radioGroupImages_130)); - expect(service.mapToElementModelValue( 2, elementModel)) + expect(service.mapToElementModelValue(2, elementModel)) .toEqual(1); }); it('should not map but return the radio-group-images elementModel value', () => { const elementModel: RadioButtonGroupComplexElement = JSON.parse(JSON.stringify(radioGroupImages_130)); - expect(service.mapToElementModelValue( undefined, elementModel)) + expect(service.mapToElementModelValue(undefined, elementModel)) .toEqual(null); }); it('should map an elementCode value to likert-row elementModel value', () => { const elementModel: LikertRowElement = JSON.parse(JSON.stringify(likertRow_130)); - expect(service.mapToElementModelValue( 3, elementModel)) + expect(service.mapToElementModelValue(3, elementModel)) .toEqual(2); }); it('should not map but return the likert-row elementModel value', () => { const elementModel: LikertRowElement = JSON.parse(JSON.stringify(likertRow_130)); - expect(service.mapToElementModelValue( undefined, elementModel)) + expect(service.mapToElementModelValue(undefined, elementModel)) .toEqual(null); }); it('should map an elementCode value to toggle-button elementModel value', () => { const elementModel: ToggleButtonElement = JSON.parse(JSON.stringify(toggleButton_130)); - expect(service.mapToElementModelValue( 1, elementModel)) + expect(service.mapToElementModelValue(1, elementModel)) .toEqual(0); }); it('should not map but return the toggle-button elementModel value', () => { const elementModel: ToggleButtonElement = JSON.parse(JSON.stringify(toggleButton_130)); - expect(service.mapToElementModelValue( undefined, elementModel)) + expect(service.mapToElementModelValue(undefined, elementModel)) .toEqual(null); }); }); diff --git a/projects/player/src/app/services/keyboard.service.spec.ts b/projects/player/src/app/services/keyboard.service.spec.ts index 9dd1c0d8e7545e64d76fad624b7a4874884dbea5..e0f2c71896a527e6f02c0fa90a2de7992c99c3a0 100644 --- a/projects/player/src/app/services/keyboard.service.spec.ts +++ b/projects/player/src/app/services/keyboard.service.spec.ts @@ -1,7 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { KeyInputModule } from 'player/modules/key-input/key-input.module'; -import { KeyboardService } from './keyboard.service'; import { TextFieldComponent } from 'common/components/input-elements/text-field.component'; import { TextFieldSimpleComponent @@ -13,6 +12,7 @@ import * as textField_130 from 'test-data/element-models/text-field_130.json'; import * as textFieldSimple_131 from 'test-data/element-models/text-field-simple_131.json'; import * as textArea_130 from 'test-data/element-models/text-area_130.json'; import * as spellCorrect_130 from 'test-data/element-models/spell-correct_130.json'; +import { KeyboardService } from './keyboard.service'; describe('KeyboardService', () => { let service: KeyboardService; @@ -76,7 +76,6 @@ describe('KeyboardService', () => { expect(service.isOpen).toBeFalse(); }); - it('should not toggle keyboard to open', () => { const element = textFieldComponent.domElement.querySelector('input') as HTMLInputElement; const input = { inputElement: element, focused: true }; @@ -84,7 +83,6 @@ describe('KeyboardService', () => { expect(service.isOpen).toBeFalse(); }); - it('should set the inputElement of the service', () => { const element = textFieldComponent.domElement.querySelector('input') as HTMLInputElement; const input = { inputElement: element, focused: true }; @@ -111,7 +109,6 @@ describe('KeyboardService', () => { expect(service.inputElement.value).toEqual('n!'); }); - it('enter "!" should replace the inputElement.value of the service with to "!"', () => { const element = textFieldComponent.domElement.querySelector('input') as HTMLInputElement; const input = { inputElement: element, focused: true }; @@ -178,7 +175,6 @@ describe('KeyboardService', () => { expect(service.isOpen).toBeTruthy(); }); - it('should toggle keyboard to close', () => { service.isOpen = true; const element = textFieldSimpleComponent.domElement.querySelector('input') as HTMLInputElement; @@ -187,7 +183,6 @@ describe('KeyboardService', () => { expect(service.isOpen).toBeFalse(); }); - it('should not toggle keyboard to open', () => { const element = textFieldSimpleComponent.domElement.querySelector('input') as HTMLInputElement; const input = { inputElement: element, focused: true }; @@ -195,7 +190,6 @@ describe('KeyboardService', () => { expect(service.isOpen).toBeFalse(); }); - it('should set the inputElement of the service', () => { const element = textFieldSimpleComponent.domElement.querySelector('input') as HTMLInputElement; const input = { inputElement: element, focused: true }; @@ -296,7 +290,6 @@ describe('KeyboardService', () => { expect(service.isOpen).toBeFalse(); }); - it('should not toggle keyboard to open', () => { const element = textAreaComponent.domElement.querySelector('textarea') as HTMLTextAreaElement; const input = { inputElement: element, focused: true }; @@ -304,7 +297,6 @@ describe('KeyboardService', () => { expect(service.isOpen).toBeFalse(); }); - it('should set the inputElement of the service', () => { const element = textAreaComponent.domElement.querySelector('textarea') as HTMLTextAreaElement; const input = { inputElement: element, focused: true }; @@ -405,7 +397,6 @@ describe('KeyboardService', () => { expect(service.isOpen).toBeFalse(); }); - it('should not toggle keyboard to open', () => { const element = spellCorrectComponent.domElement.querySelector('input') as HTMLInputElement; const input = { inputElement: element, focused: true }; @@ -413,7 +404,6 @@ describe('KeyboardService', () => { expect(service.isOpen).toBeFalse(); }); - it('should set the inputElement of the service', () => { const element = spellCorrectComponent.domElement.querySelector('input') as HTMLInputElement; const input = { inputElement: element, focused: true }; diff --git a/projects/player/src/app/services/keypad.service.spec.ts b/projects/player/src/app/services/keypad.service.spec.ts index 5ccd6deb555562aa4503d705199154db9617324b..e68e496706ef56dc2f72d1189f1270274cb332a8 100644 --- a/projects/player/src/app/services/keypad.service.spec.ts +++ b/projects/player/src/app/services/keypad.service.spec.ts @@ -1,7 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { KeyInputModule } from 'player/modules/key-input/key-input.module'; -import { KeypadService } from './keypad.service'; import { TextFieldComponent } from 'common/components/input-elements/text-field.component'; import { TextAreaComponent } from 'common/components/input-elements/text-area.component'; import { @@ -13,7 +12,7 @@ import * as textField_130 from 'test-data/element-models/text-field_130.json'; import * as textFieldSimple_131 from 'test-data/element-models/text-field-simple_131.json'; import * as textArea_130 from 'test-data/element-models/text-area_130.json'; import * as spellCorrect_130 from 'test-data/element-models/spell-correct_130.json'; - +import { KeypadService } from './keypad.service'; describe('KeypadService', () => { let service: KeypadService; @@ -69,7 +68,6 @@ describe('KeypadService', () => { expect(service.isOpen).toBeTruthy(); }); - it('should toggle keypad to close', () => { service.isOpen = true; const element = textFieldComponent.domElement.querySelector('input') as HTMLInputElement; @@ -78,7 +76,6 @@ describe('KeypadService', () => { expect(service.isOpen).toBeFalse(); }); - it('should set the inputElement of the service', () => { const element = textFieldComponent.domElement.querySelector('input') as HTMLInputElement; const input = { inputElement: element, focused: true }; @@ -178,7 +175,6 @@ describe('KeypadService', () => { expect(service.isOpen).toBeTruthy(); }); - it('should toggle keypad to close', () => { service.isOpen = true; const element = textFieldSimpleComponent.domElement.querySelector('input') as HTMLInputElement; @@ -393,7 +389,6 @@ describe('KeypadService', () => { expect(service.isOpen).toBeTruthy(); }); - it('should toggle keypad to close', () => { service.isOpen = true; const element = spellCorrectComponent.domElement.querySelector('input') as HTMLInputElement; diff --git a/projects/player/src/app/services/media-player.service.spec.ts b/projects/player/src/app/services/media-player.service.spec.ts index 57c27cabb92773e68384568017047aec231c56c9..ca5e291e7331cc71e80878bc6cb586d1c16edd2a 100644 --- a/projects/player/src/app/services/media-player.service.spec.ts +++ b/projects/player/src/app/services/media-player.service.spec.ts @@ -1,7 +1,6 @@ import { TestBed } from '@angular/core/testing'; import { MediaPlayerService } from './media-player.service'; - describe('MediaPlayerService', () => { let service: MediaPlayerService; beforeEach(() => { @@ -17,7 +16,7 @@ describe('MediaPlayerService', () => { const mediaId = 'audio_1'; service.registerMediaElement(mediaId, false); service.mediaStatusChanged - .subscribe( id => { + .subscribe(id => { expect(id).toEqual(mediaId); done(); }); @@ -29,7 +28,7 @@ describe('MediaPlayerService', () => { service.registerMediaElement(mediaId, false); service.registerMediaElement('audio_2', false); service.mediaStatusChanged - .subscribe( id => { + .subscribe(id => { expect(id).toEqual(mediaId); done(); }); @@ -41,7 +40,7 @@ describe('MediaPlayerService', () => { service.registerMediaElement(mediaId, false); service.registerMediaElement('audio_2', false); service.mediaStatusChanged - .subscribe( id => { + .subscribe(id => { expect(id).not.toEqual(mediaId); done(); }); @@ -77,7 +76,7 @@ describe('MediaPlayerService', () => { it('actualPlayingMediaId should be audio_1', done => { const mediaId = 'audio_1'; service.actualPlayingId - .subscribe( id => { + .subscribe(id => { expect(id).toEqual(mediaId); done(); }); @@ -87,7 +86,7 @@ describe('MediaPlayerService', () => { it('pageIndex should not be audio_1', done => { const mediaId = 'audio_1'; service.actualPlayingId - .subscribe( id => { + .subscribe(id => { expect(id).not.toEqual(mediaId); done(); }); diff --git a/projects/player/src/app/services/navigation.service.spec.ts b/projects/player/src/app/services/navigation.service.spec.ts index cba9d2f721f4e19efe56f8c4b15dac89ed818910..81fa74965e4aec819bd16495973b5dbc8e7bac39 100644 --- a/projects/player/src/app/services/navigation.service.spec.ts +++ b/projects/player/src/app/services/navigation.service.spec.ts @@ -14,7 +14,7 @@ describe('NavigationService', () => { it('pageIndex should be 2', done => { service.pageIndex - .subscribe( pageIndex => { + .subscribe(pageIndex => { expect(pageIndex).toEqual(2); done(); }); @@ -23,7 +23,7 @@ describe('NavigationService', () => { it('pageIndex should not be 2', done => { service.pageIndex - .subscribe( pageIndex => { + .subscribe(pageIndex => { expect(pageIndex).not.toEqual(2); done(); }); diff --git a/projects/player/src/app/services/text-marking.service.spec.ts b/projects/player/src/app/services/text-marking.service.spec.ts index 641c75187487321909abc1ed24f6c93a8a2fc4eb..6b36d3e629cb3b80da681dabf9d15addf01a39a7 100644 --- a/projects/player/src/app/services/text-marking.service.spec.ts +++ b/projects/player/src/app/services/text-marking.service.spec.ts @@ -26,5 +26,4 @@ describe('TextMarkingService', () => { const markings = ['6-11-#f9f871']; expect(TextMarkingService.restoreMarkedTextIndices(markings, text)).toEqual(expectedText); }); - }); diff --git a/projects/player/src/app/services/unit-state.service.spec.ts b/projects/player/src/app/services/unit-state.service.spec.ts index ce20e225b8a87afd5e242b467c80aa5f84e7b1df..863b6dbdf1f0a1e262fcef7f045dd4a74e39cac0 100644 --- a/projects/player/src/app/services/unit-state.service.spec.ts +++ b/projects/player/src/app/services/unit-state.service.spec.ts @@ -1,6 +1,6 @@ import { TestBed } from '@angular/core/testing'; -import { UnitStateService } from './unit-state.service'; import { ElementCode } from 'player/modules/verona/models/verona'; +import { UnitStateService } from './unit-state.service'; describe('UnitStateService', () => { let service: UnitStateService; @@ -14,16 +14,16 @@ describe('UnitStateService', () => { }); it('should get element by id', () => { - const element1: ElementCode = { id: 'element_1', status: 'DISPLAYED', value: 'TEST1' }; - const element2: ElementCode = { id: 'element_2', status: 'DISPLAYED', value: 'TEST2' }; - service.elementCodes = [ element1, element2 ]; + const element1: ElementCode = { id: 'element_1', status: 'DISPLAYED', value: 'TEST1' }; + const element2: ElementCode = { id: 'element_2', status: 'DISPLAYED', value: 'TEST2' }; + service.elementCodes = [element1, element2]; expect(service.getElementCodeById('element_1')).toEqual(element1); }); it('should return undefined for a not registered element id', () => { - const element1: ElementCode = { id: 'element_1', status: 'DISPLAYED', value: 'TEST1' }; - const element2: ElementCode = { id: 'element_2', status: 'DISPLAYED', value: 'TEST2' }; - service.elementCodes = [ element1, element2 ]; + const element1: ElementCode = { id: 'element_1', status: 'DISPLAYED', value: 'TEST1' }; + const element2: ElementCode = { id: 'element_2', status: 'DISPLAYED', value: 'TEST2' }; + service.elementCodes = [element1, element2]; expect(service.getElementCodeById('element_3')).toBeUndefined(); }); @@ -31,13 +31,13 @@ describe('UnitStateService', () => { service.elementCodes = []; const element = document.createElement('div'); service.registerElement('element', 'TEST', element, 1); - expect(service.elementCodes).toEqual([ { id: 'element', status: 'NOT_REACHED', value: 'TEST' } ]); + expect(service.elementCodes).toEqual([{ id: 'element', status: 'NOT_REACHED', value: 'TEST' }]); }); it('elementCode of an element should change', done => { service.elementCodes = [{ id: 'element_1', status: 'NOT_REACHED', value: 'TEST1' }]; service.elementCodeChanged - .subscribe( code => { + .subscribe(code => { expect(code.status).toEqual('DISPLAYED'); done(); }); @@ -47,11 +47,11 @@ describe('UnitStateService', () => { it('elementCode of an element should change', done => { service.elementCodes = [{ id: 'element_1', status: 'NOT_REACHED', value: 'TEST1' }]; service.elementCodeChanged - .subscribe( code => { + .subscribe(code => { expect(code.status).toEqual('VALUE_CHANGED'); expect(code.value).toEqual('NEU'); done(); - }) ; + }); service.changeElementCodeValue({ id: 'element_1', value: 'NEU' }); }); @@ -107,8 +107,8 @@ describe('UnitStateService', () => { service.elementCodes = []; const element = document.createElement('div'); service.registerElement('element_1', 'TEST1', element, 1); - service.presentedPageAdded - .subscribe( index => { + service.pagePresented + .subscribe(index => { expect(index).toEqual(1); done(); }); @@ -119,12 +119,11 @@ describe('UnitStateService', () => { service.elementCodes = []; const element = document.createElement('div'); service.registerElement('element_1', 'TEST1', element, 1); - service.presentedPageAdded - .subscribe( index => { + service.pagePresented + .subscribe(index => { expect(index).toEqual(1); done(); }); service.changeElementCodeValue({ id: 'element_1', value: 'NEU' }); }); - }); diff --git a/projects/player/src/app/services/unit-state.service.ts b/projects/player/src/app/services/unit-state.service.ts index dd22bd12dbbf27cbde3b4b00bb820931fce701bd..33c7db353537cc09dd0b0bc58d8157562d477d09 100644 --- a/projects/player/src/app/services/unit-state.service.ts +++ b/projects/player/src/app/services/unit-state.service.ts @@ -4,16 +4,16 @@ import { DOCUMENT } from '@angular/common'; import { Progress, StatusChangeElement, ElementCode, ElementCodeStatus, ElementCodeStatusValue } from 'player/modules/verona/models/verona'; -import { IntersectionDetector } from '../classes/intersection-detector'; import { LogService } from 'player/modules/logging/services/log.service'; import { InputElementValue, ValueChangeElement } from 'common/models/elements/element'; +import { IntersectionDetector } from '../classes/intersection-detector'; @Injectable({ providedIn: 'root' }) export class UnitStateService { private _elementCodes: ElementCode[] = []; - private _presentedPageAdded = new Subject<number>(); + private _pagePresented = new Subject<number>(); private _elementCodeChanged = new Subject<ElementCode>(); private presentedPages: number[] = []; private elementIdPageIndexMap: { [elementId: string]: number } = {}; @@ -40,8 +40,8 @@ export class UnitStateService { return this._elementCodeChanged.asObservable(); } - get presentedPageAdded(): Observable<number> { - return this._presentedPageAdded.asObservable(); + get pagePresented(): Observable<number> { + return this._pagePresented.asObservable(); } get presentedPagesProgress(): Progress { @@ -81,8 +81,10 @@ export class UnitStateService { this.intersectionDetector.observe(domElement, elementId); this.intersectionDetector.intersecting .subscribe((id: string) => { - this.changeElementCodeStatus({ id: id, status: 'DISPLAYED' }); - this.intersectionDetector.unobserve(id); + if (elementId === id) { + this.changeElementCodeStatus({ id: id, status: 'DISPLAYED' }); + this.intersectionDetector.unobserve(id); + } }); } @@ -115,7 +117,7 @@ export class UnitStateService { } private buildPresentedPages(): void { - const uniqPages = [...new Set( Object.values(this.elementIdPageIndexMap))]; + const uniqPages = [...new Set(Object.values(this.elementIdPageIndexMap))]; uniqPages.forEach(pageIndex => this .checkPresentedPageStatus(pageIndex)); } @@ -130,7 +132,7 @@ export class UnitStateService { ElementCodeStatusValue.DISPLAYED); if (notDisplayedElements.length === 0) { this.presentedPages.push(pageIndex); - this._presentedPageAdded.next(pageIndex); + this._pagePresented.next(pageIndex); } } else { LogService.warn(`player: page ${pageIndex} is already presented`); @@ -144,11 +146,9 @@ export class UnitStateService { unitStateElementCode = { id: id, value: value, status: 'NOT_REACHED' }; this.elementCodes.push(unitStateElementCode); this._elementCodeChanged.next(unitStateElementCode); - } else { + } else if (Object.keys(this.elementIdPageIndexMap).length === this.elementCodes.length) { // if all elements are registered, we can rebuild the presentedPages array - if (Object.keys(this.elementIdPageIndexMap).length === this.elementCodes.length) { - this.buildPresentedPages(); - } + this.buildPresentedPages(); } if (unitStateElementCode.status === 'NOT_REACHED') { this.addIntersectionDetection(id, domElement); diff --git a/projects/player/src/app/services/validation.service.spec.ts b/projects/player/src/app/services/validation.service.spec.ts index 7252c636c5e039a5bb65966e2816340aad4711c9..2fad00d2af24d612834fd971be4b71073cde18c7 100644 --- a/projects/player/src/app/services/validation.service.spec.ts +++ b/projects/player/src/app/services/validation.service.spec.ts @@ -1,6 +1,6 @@ import { TestBed } from '@angular/core/testing'; -import { ValidationService } from './validation.service'; import { FormControl } from '@angular/forms'; +import { ValidationService } from './validation.service'; describe('ValidationService', () => { let service: ValidationService; @@ -32,5 +32,4 @@ describe('ValidationService', () => { service.registerFormControl(new FormControl('')); expect(service.responseProgress).toEqual('some'); }); - }); diff --git a/scripts/wrap_and_pack.js b/scripts/wrap_and_pack.js index 27c94c5d70ad589ad56efda18ba7efced1e82781..6f8dcef68f4f15877eb2f7388c5002ad0016d7a8 100644 --- a/scripts/wrap_and_pack.js +++ b/scripts/wrap_and_pack.js @@ -1,19 +1,18 @@ #!/usr/bin/env node +// eslint-disable-next-line @typescript-eslint/no-var-requires const fs = require('fs'); -const childProcess = require('child_process'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const execSync = require('child_process').execSync; const packageName = process.argv[2]; const packageVersion = process.argv[3]; const wrapperPath = process.argv[4]; -childProcess.fork('node_modules/iqb-dev-components/src/js_css_packer.js', - ['dist', packageName, 'dist']); - +execSync(`node node_modules/iqb-dev-components/src/js_css_packer.js dist ${packageName} dist`); const fileContent = fs.readFileSync(wrapperPath, 'utf8').toString() .replace(/version-placeholder/g, packageVersion); fs.writeFileSync('dist/index.html', fileContent, 'utf8'); const targetFileName = `verona-${packageName}-aspect-${packageVersion}.html`; -childProcess.fork('node_modules/iqb-dev-components/src/distpacker.js', -['dist', targetFileName, packageName]); +execSync(`node node_modules/iqb-dev-components/src/distpacker.js dist ${targetFileName} ${packageName}`);