From 394520643189ad0dda296c09fb0f2dff73ce8165 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Mon, 9 Sep 2024 20:43:50 +0200 Subject: [PATCH 001/241] install tiptap --- package-lock.json | 521 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 3 + 2 files changed, 514 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 826baef473..cbc477b3b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,9 @@ "./src/packages/webhook" ], "dependencies": { + "@tiptap/core": "^2.6.6", + "@tiptap/pm": "^2.6.6", + "@tiptap/starter-kit": "^2.6.6", "@types/diff": "^5.2.1", "@types/dompurify": "^3.0.5", "@types/uuid": "^10.0.0", @@ -4640,6 +4643,11 @@ "@babel/runtime": "^7.13.10" } }, + "node_modules/@remirror/core-constants": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-2.0.2.tgz", + "integrity": "sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==" + }, "node_modules/@rollup/plugin-commonjs": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-26.0.1.tgz", @@ -6481,6 +6489,299 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@tiptap/core": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.6.6.tgz", + "integrity": "sha512-VO5qTsjt6rwworkuo0s5AqYMfDA0ZwiTiH6FHKFSu2G/6sS7HKcc/LjPq+5Legzps4QYdBDl3W28wGsGuS1GdQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-blockquote": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.6.6.tgz", + "integrity": "sha512-hAdsNlMfzzxld154hJqPqtWqO5i4/7HoDfuxmyqBxdMJ+e2UMaIGBGwoLRXG0V9UoRwJusjqlpyD7pIorxNlgA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.6.6.tgz", + "integrity": "sha512-CD6gBhdQtCoqYSmx8oAV8gvKtVOGZSyyvuNYo7by9eZ56DqLYnd7kbUj0RH7o9Ymf/iJTOUJ6XcvrsWwo4lubg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.6.6.tgz", + "integrity": "sha512-WEKxbVSYuvmX2wkHWP8HXk5nzA7stYwtdaubwWH/R17kGI3IGScJuMQ9sEN82uzJU8bfgL9yCbH2bY8Fj/Q4Ow==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.6.6.tgz", + "integrity": "sha512-JrEFKsZiLvfvOFhOnnrpA0TzCuJjDeysfbMeuKUZNV4+DhYOL28d39H1++rEtJAX0LcbBU60oC5/PrlU9SpvRQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.6.6.tgz", + "integrity": "sha512-1YLp/zHMHSkE2xzht8nPR6T4sQJJ3ket798czxWuQEbetFv/l0U/mpiPpYSLObj6oTAoqYZ0kWXZj5eQSpPB8Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6", + "@tiptap/pm": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-document": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.6.6.tgz", + "integrity": "sha512-6qlH5VWzLHHRVeeciRC6C4ZHpMsAGPNG16EF53z0GeMSaaFD/zU3B239QlmqXmLsAl8bpf8Bn93N0t2ABUvScw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.6.6.tgz", + "integrity": "sha512-O6CeKriA9uyHsg7Ui4z5ZjEWXQxrIL+1zDekffW0wenGC3G4LUsCzAiFS4LSrR9a3u7tnwqGApW10rdkmCGF4w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6", + "@tiptap/pm": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.6.6.tgz", + "integrity": "sha512-O2lQ2t0X0Vsbn3yLWxFFHrXY6C2N9Y6ZF/M7LWzpcDTUZeWuhoNkFE/1yOM0h6ZX1DO2A9hNIrKpi5Ny8yx+QA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6", + "@tiptap/pm": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-hard-break": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.6.6.tgz", + "integrity": "sha512-bsUuyYBrMDEiudx1dOQSr9MzKv13m0xHWrOK+DYxuIDYJb5g+c9un5cK7Js+et/HEYYSPOoH/iTW6h+4I5YeUg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.6.6.tgz", + "integrity": "sha512-bgx9vptVFi5yFkIw1OI53J7+xJ71Or3SOe/Q8eSpZv53DlaKpL/TzKw8Z54t1PrI2rJ6H9vrLtkvixJvBZH1Ug==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-history": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.6.6.tgz", + "integrity": "sha512-tPTzAmPGqMX5Bd5H8lzRpmsaMvB9DvI5Dy2za/VQuFtxgXmDiFVgHRkRXIuluSkPTuANu84XBOQ0cBijqY8x4w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6", + "@tiptap/pm": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.6.6.tgz", + "integrity": "sha512-cFEfv7euDpuLSe8exY8buwxkreKBAZY9Hn3EetKhPcLQo+ut5Y24chZTxFyf9b+Y0wz3UhOhLTZSz7fTobLqBA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6", + "@tiptap/pm": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.6.6.tgz", + "integrity": "sha512-t7ZPsXqa8nJZZ/6D0rQyZ/KsvzLaSihC6hBTjUQ77CeDGV9PhDWjIcBW4OrvwraJDBd12ETBeQ2CkULJOgH+lQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-list-item": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.6.6.tgz", + "integrity": "sha512-k+oEzZu2cgVKqPqOP1HzASOKLpTEV9m7mRVPAbuaaX8mSyvIgD6f+JUx9PvgYv//D918wk98LMoRBFX53tDJ4w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.6.6.tgz", + "integrity": "sha512-AJwyfLXIi7iUGnK5twJbwdVVpQyh7fU6OK75h1AwDztzsOcoPcxtffDlZvUOd4ZtwuyhkzYqVkeI0f+abTWZTw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.6.6.tgz", + "integrity": "sha512-fD/onCr16UQWx+/xEmuFC2MccZZ7J5u4YaENh8LMnAnBXf78iwU7CAcmuc9rfAEO3qiLoYGXgLKiHlh2ZfD4wA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-strike": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.6.6.tgz", + "integrity": "sha512-Ze8KhGk+wzSJSJRl5fbhTI6AvPu2LmcHYeO3pMEH8u4gV5WTXfmKJVStEIAzkoqvwEQVWzXvy8nDgsFQHiojPg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-text": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.6.6.tgz", + "integrity": "sha512-e84uILnRzNzcwK1DVQNpXVmBG1Cq3BJipTOIDl1LHifOok7MBjhI/X+/NR0bd3N2t6gmDTWi63+4GuJ5EeDmsg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, + "node_modules/@tiptap/pm": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.6.6.tgz", + "integrity": "sha512-56FGLPn3fwwUlIbLs+BO21bYfyqP9fKyZQbQyY0zWwA/AG2kOwoXaRn7FOVbjP6CylyWpFJnpRRmgn694QKHEg==", + "dependencies": { + "prosemirror-changeset": "^2.2.1", + "prosemirror-collab": "^1.3.1", + "prosemirror-commands": "^1.5.2", + "prosemirror-dropcursor": "^1.8.1", + "prosemirror-gapcursor": "^1.3.2", + "prosemirror-history": "^1.4.1", + "prosemirror-inputrules": "^1.4.0", + "prosemirror-keymap": "^1.2.2", + "prosemirror-markdown": "^1.13.0", + "prosemirror-menu": "^1.2.4", + "prosemirror-model": "^1.22.2", + "prosemirror-schema-basic": "^1.2.3", + "prosemirror-schema-list": "^1.4.1", + "prosemirror-state": "^1.4.3", + "prosemirror-tables": "^1.4.0", + "prosemirror-trailing-node": "^2.0.9", + "prosemirror-transform": "^1.9.0", + "prosemirror-view": "^1.33.9" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@tiptap/starter-kit": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.6.6.tgz", + "integrity": "sha512-zb9xIg3WjG9AsJoyWrfqx5SL9WH7/HTdkB79jFpWtOF/Kaigo7fHFmhs2FsXtJMJlcdMTO2xeRuCYHt5ozXlhg==", + "dependencies": { + "@tiptap/core": "^2.6.6", + "@tiptap/extension-blockquote": "^2.6.6", + "@tiptap/extension-bold": "^2.6.6", + "@tiptap/extension-bullet-list": "^2.6.6", + "@tiptap/extension-code": "^2.6.6", + "@tiptap/extension-code-block": "^2.6.6", + "@tiptap/extension-document": "^2.6.6", + "@tiptap/extension-dropcursor": "^2.6.6", + "@tiptap/extension-gapcursor": "^2.6.6", + "@tiptap/extension-hard-break": "^2.6.6", + "@tiptap/extension-heading": "^2.6.6", + "@tiptap/extension-history": "^2.6.6", + "@tiptap/extension-horizontal-rule": "^2.6.6", + "@tiptap/extension-italic": "^2.6.6", + "@tiptap/extension-list-item": "^2.6.6", + "@tiptap/extension-ordered-list": "^2.6.6", + "@tiptap/extension-paragraph": "^2.6.6", + "@tiptap/extension-strike": "^2.6.6", + "@tiptap/extension-text": "^2.6.6", + "@tiptap/pm": "^2.6.6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", @@ -9863,8 +10164,7 @@ "node_modules/argparse": { "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 + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/aria-hidden": { "version": "1.2.4", @@ -11439,6 +11739,11 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -12201,7 +12506,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, "engines": { "node": ">=0.12" }, @@ -16009,7 +16313,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "dev": true, "dependencies": { "uc.micro": "^2.0.0" } @@ -16387,7 +16690,6 @@ "version": "14.1.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", - "dev": true, "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", @@ -16663,8 +16965,7 @@ "node_modules/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "dev": true + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==" }, "node_modules/media-typer": { "version": "0.3.0", @@ -18249,6 +18550,11 @@ "node": ">=8" } }, + "node_modules/orderedmap": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", + "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==" + }, "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -18980,6 +19286,193 @@ "node": ">= 6" } }, + "node_modules/prosemirror-changeset": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.2.1.tgz", + "integrity": "sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==", + "dependencies": { + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-collab": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz", + "integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==", + "dependencies": { + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-commands": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.6.0.tgz", + "integrity": "sha512-xn1U/g36OqXn2tn5nGmvnnimAj/g1pUx2ypJJIe8WkVX83WyJVC5LTARaxZa2AtQRwntu9Jc5zXs9gL9svp/mg==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-dropcursor": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.1.tgz", + "integrity": "sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "node_modules/prosemirror-gapcursor": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz", + "integrity": "sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==", + "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.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.4.1.tgz", + "integrity": "sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==", + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.31.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-inputrules": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.4.0.tgz", + "integrity": "sha512-6ygpPRuTJ2lcOXs9JkefieMst63wVJBgHZGl5QOytN7oSZs3Co/BYbc3Yx9zm9H37Bxw8kVzCnDsihsVsL4yEg==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.2.tgz", + "integrity": "sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-markdown": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.0.tgz", + "integrity": "sha512-UziddX3ZYSYibgx8042hfGKmukq5Aljp2qoBiJRejD/8MH70siQNz5RB1TrdTPheqLMy4aCe4GYNF10/3lQS5g==", + "dependencies": { + "markdown-it": "^14.0.0", + "prosemirror-model": "^1.20.0" + } + }, + "node_modules/prosemirror-menu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.4.tgz", + "integrity": "sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==", + "dependencies": { + "crelt": "^1.0.0", + "prosemirror-commands": "^1.0.0", + "prosemirror-history": "^1.0.0", + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-model": { + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.22.3.tgz", + "integrity": "sha512-V4XCysitErI+i0rKFILGt/xClnFJaohe/wrrlT2NSZ+zk8ggQfDH4x2wNK7Gm0Hp4CIoWizvXFP7L9KMaCuI0Q==", + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/prosemirror-schema-basic": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.3.tgz", + "integrity": "sha512-h+H0OQwZVqMon1PNn0AG9cTfx513zgIG2DY00eJ00Yvgb3UD+GQ/VlWW5rcaxacpCGT1Yx8nuhwXk4+QbXUfJA==", + "dependencies": { + "prosemirror-model": "^1.19.0" + } + }, + "node_modules/prosemirror-schema-list": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.4.1.tgz", + "integrity": "sha512-jbDyaP/6AFfDfu70VzySsD75Om2t3sXTOdl5+31Wlxlg62td1haUpty/ybajSfJ1pkGadlOfwQq9kgW5IMo1Rg==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.7.3" + } + }, + "node_modules/prosemirror-state": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz", + "integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.27.0" + } + }, + "node_modules/prosemirror-tables": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.5.0.tgz", + "integrity": "sha512-VMx4zlYWm7aBlZ5xtfJHpqa3Xgu3b7srV54fXYnXgsAcIGRqKSrhiK3f89omzzgaAgAtDOV4ImXnLKhVfheVNQ==", + "dependencies": { + "prosemirror-keymap": "^1.1.2", + "prosemirror-model": "^1.8.1", + "prosemirror-state": "^1.3.1", + "prosemirror-transform": "^1.2.1", + "prosemirror-view": "^1.13.3" + } + }, + "node_modules/prosemirror-trailing-node": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.9.tgz", + "integrity": "sha512-YvyIn3/UaLFlFKrlJB6cObvUhmwFNZVhy1Q8OpW/avoTbD/Y7H5EcjK4AZFKhmuS6/N6WkGgt7gWtBWDnmFvHg==", + "dependencies": { + "@remirror/core-constants": "^2.0.2", + "escape-string-regexp": "^4.0.0" + }, + "peerDependencies": { + "prosemirror-model": "^1.22.1", + "prosemirror-state": "^1.4.2", + "prosemirror-view": "^1.33.8" + } + }, + "node_modules/prosemirror-trailing-node/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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prosemirror-transform": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.0.tgz", + "integrity": "sha512-9UOgFSgN6Gj2ekQH5CTDJ8Rp/fnKR2IkYfGdzzp5zQMFsS4zDllLVx/+jGcX86YlACpG7UR5fwAXiWzxqWtBTg==", + "dependencies": { + "prosemirror-model": "^1.21.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.34.2", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.34.2.tgz", + "integrity": "sha512-tPX/V2Xd70vrAGQ/V9CppJtPKnQyQMypJGlLylvdI94k6JaG+4P6fVmXPR1zc1eVTW0gq3c6zsfqwJKCRLaG9Q==", + "dependencies": { + "prosemirror-model": "^1.20.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -19071,7 +19564,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "dev": true, "engines": { "node": ">=6" } @@ -19938,6 +20430,11 @@ "rollup": "^1.9.2 || ^2.0.0" } }, + "node_modules/rope-sequence": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", + "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==" + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -21621,8 +22118,7 @@ "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "dev": true + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" }, "node_modules/ufo": { "version": "1.5.4", @@ -22615,6 +23111,11 @@ "@esbuild/win32-x64": "0.21.5" } }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, "node_modules/walkdir": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz", diff --git a/package.json b/package.json index 0d7af660b1..911ef8621f 100644 --- a/package.json +++ b/package.json @@ -212,6 +212,9 @@ "npm": ">=10.1 < 11" }, "dependencies": { + "@tiptap/core": "^2.6.6", + "@tiptap/pm": "^2.6.6", + "@tiptap/starter-kit": "^2.6.6", "@types/diff": "^5.2.1", "@types/dompurify": "^3.0.5", "@types/uuid": "^10.0.0", From 507ab979bd642ad783ecfac4e68362209589b4f1 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:00:45 +0200 Subject: [PATCH 002/241] init tiptap --- src/apps/backoffice/backoffice.element.ts | 1 + src/external/tiptap/index.ts | 2 + src/packages/tiptap/components/index.ts | 1 + .../tiptap/components/input-tiptap/index.ts | 1 + .../input-tiptap/input-tiptap.element.ts | 112 ++++++++ .../input-tiptap/tiptap-fixed-menu.element.ts | 266 ++++++++++++++++++ src/packages/tiptap/manifests.ts | 4 + .../tiptap/property-editors/manifests.ts | 4 + .../property-editors/tiptap/manifests.ts | 16 ++ .../property-editor-ui-tiptap.element.ts | 49 ++++ src/packages/tiptap/umbraco-package.ts | 9 + src/packages/tiptap/vite.config.ts | 12 + tsconfig.json | 1 + 13 files changed, 478 insertions(+) create mode 100644 src/external/tiptap/index.ts create mode 100644 src/packages/tiptap/components/index.ts create mode 100644 src/packages/tiptap/components/input-tiptap/index.ts create mode 100644 src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts create mode 100644 src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts create mode 100644 src/packages/tiptap/manifests.ts create mode 100644 src/packages/tiptap/property-editors/manifests.ts create mode 100644 src/packages/tiptap/property-editors/tiptap/manifests.ts create mode 100644 src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts create mode 100644 src/packages/tiptap/umbraco-package.ts create mode 100644 src/packages/tiptap/vite.config.ts diff --git a/src/apps/backoffice/backoffice.element.ts b/src/apps/backoffice/backoffice.element.ts index d59e98126c..42c5f423d6 100644 --- a/src/apps/backoffice/backoffice.element.ts +++ b/src/apps/backoffice/backoffice.element.ts @@ -37,6 +37,7 @@ const CORE_PACKAGES = [ import('../../packages/telemetry/umbraco-package.js'), import('../../packages/templating/umbraco-package.js'), import('../../packages/tiny-mce/umbraco-package.js'), + import('../../packages/tiptap/umbraco-package.js'), import('../../packages/ufm/umbraco-package.js'), import('../../packages/umbraco-news/umbraco-package.js'), import('../../packages/user/umbraco-package.js'), diff --git a/src/external/tiptap/index.ts b/src/external/tiptap/index.ts new file mode 100644 index 0000000000..1d5fb7fe1e --- /dev/null +++ b/src/external/tiptap/index.ts @@ -0,0 +1,2 @@ +export * from '@tiptap/core'; +export * from '@tiptap/starter-kit'; diff --git a/src/packages/tiptap/components/index.ts b/src/packages/tiptap/components/index.ts new file mode 100644 index 0000000000..d7e1544b7d --- /dev/null +++ b/src/packages/tiptap/components/index.ts @@ -0,0 +1 @@ +export * from './input-tiptap/index.js'; diff --git a/src/packages/tiptap/components/input-tiptap/index.ts b/src/packages/tiptap/components/input-tiptap/index.ts new file mode 100644 index 0000000000..f11b53c7d9 --- /dev/null +++ b/src/packages/tiptap/components/input-tiptap/index.ts @@ -0,0 +1 @@ +export * from './input-tiptap.element.js'; diff --git a/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts new file mode 100644 index 0000000000..1932b8d77e --- /dev/null +++ b/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -0,0 +1,112 @@ +import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; +import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; +import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; + +import './tiptap-fixed-menu.element.js'; +import { Editor, StarterKit } from '@umbraco-cms/backoffice/external/tiptap'; + +@customElement('umb-input-tiptap') +export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '') { + @property({ attribute: false }) + configuration?: UmbPropertyEditorConfigCollection; + + @state() + _editor!: Editor; + + protected override firstUpdated(_changedProperties: PropertyValueMap | Map): void { + super.firstUpdated(_changedProperties); + + const editor = this.shadowRoot?.querySelector('#editor'); + + if (!editor) return; + + const json = + this.value && typeof this.value === 'string' + ? JSON.parse(this.value) + : { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: 'Hello Umbraco', + }, + ], + }, + ], + }; + + // TODO: Try Disable css inject to remove prosemirror css + this._editor = new Editor({ + element: editor, + extensions: [StarterKit], + content: json, + onUpdate: ({ editor }) => { + const json = editor.getJSON(); + this.value = JSON.stringify(json); + console.log('json', json); + }, + }); + } + + protected getFormElement() { + return null; + } + + override render() { + return html` + +
+ `; + } + + static override styles = [ + css` + #editor { + border-radius: var(--uui-border-radius); + border: 1px solid var(--uui-color-border); + margin: 0 auto; + box-sizing: border-box; + height: 100%; + width: 100%; + padding: 1rem; + overflow: clip; + min-height: 400px; + display: grid; /* Don't ask me why this is needed, but it is. */ + } + + #editor code { + font-family: 'Roboto Mono', monospace; + background: none; + color: inherit; + font-size: 0.8rem; + padding: 0; + } + .ProseMirror { + height: 100%; + width: 100%; + outline: none; + white-space: pre-wrap; + } + .ProseMirror p, + .ProseMirror h1, + .ProseMirror h2, + .ProseMirror h3 { + margin-top: 0; + margin-bottom: 0.1rem; + } + `, + ]; +} + +export default UmbInputTiptapElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-input-tiptap': UmbInputTiptapElement; + } +} diff --git a/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts new file mode 100644 index 0000000000..28a3a2185f --- /dev/null +++ b/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -0,0 +1,266 @@ +import { LitElement, css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +@customElement('umb-tiptap-fixed-menu') +export class UmbTiptapFixedMenuElement extends LitElement { + @state() + actions = [ + { + name: 'paragraph', + icon: html` + + + `, + command: () => this.editor?.chain().focus().setParagraph().run(), + }, + { + name: 'bold', + icon: html` + + + `, + command: () => this.editor?.chain().focus().toggleBold().run(), + }, + { + name: 'italic', + icon: html` + + + `, + command: () => this.editor?.chain().focus().toggleItalic().run(), + }, + { + name: 'underline', + icon: html` + + + `, + // command: () => this.editor?.chain().focus().toggleUnderline().run(), + }, + { + name: 'strikethrough', + icon: html` + + + `, + command: () => this.editor?.chain().focus().toggleStrike().run(), + }, + { + name: 'heading-1', + icon: html` + + + `, + command: () => this.editor?.chain().focus().toggleHeading({ level: 1 }).run(), + }, + { + name: 'heading-2', + icon: html` + + + `, + command: () => this.editor?.chain().focus().toggleHeading({ level: 2 }).run(), + }, + { + name: 'heading-3', + icon: html` + + + `, + command: () => this.editor?.chain().focus().toggleHeading({ level: 3 }).run(), + }, + { + name: 'heading-4', + icon: html` + + + `, + command: () => this.editor?.chain().focus().toggleHeading({ level: 4 }).run(), + }, + { + name: 'heading-5', + icon: html` + + + `, + command: () => this.editor?.chain().focus().toggleHeading({ level: 5 }).run(), + }, + { + name: 'blockquote', + icon: html` + + + `, + command: () => this.editor?.chain().focus().toggleBlockquote().run(), + }, + { + name: 'code', + icon: html` + + + `, + command: () => this.editor?.chain().focus().toggleCodeBlock().run(), + }, + { + name: 'bullet-list', + icon: html` + + + `, + command: () => this.editor?.chain().focus().toggleBulletList().run(), + }, + { + name: 'ordered-list', + icon: html` + + + `, + command: () => this.editor?.chain().focus().toggleOrderedList().run(), + }, + { + name: 'horizontal-rule', + icon: html` + + + `, + command: () => this.editor?.chain().focus().setHorizontalRule().run(), + }, + { + name: 'align-left', + icon: html` + + + `, + // command: () => this.editor?.chain().focus().setTextAlign('left').run(), + }, + { + name: 'align-center', + icon: html` + + + `, + // command: () => this.editor?.chain().focus().setTextAlign('center').run(), + }, + { + name: 'align-right', + icon: html` + + + `, + // command: () => this.editor?.chain().focus().setTextAlign('right').run(), + }, + { + name: 'align-justify', + icon: html` + + + `, + // command: () => this.editor?.chain().focus().setTextAlign('justify').run(), + }, + { + name: 'image', + icon: html` + + + `, + command: () => { + const url = prompt('Enter the image URL'); + if (url) { + // this.editor?.chain().focus().setImage({ src: url }).run(); + } + }, + }, + { + name: 'add', + icon: html` + + + `, + command: () => { + const tag = prompt('Enter the tag'); + if (tag) { + // this.editor?.chain().focus().addCustomNode(tag).run(); + } + }, + }, + ]; + + @property({ attribute: false }) + editor?: Editor; + + override render() { + return html` + ${this.actions.map( + (action) => html` `, + )} + `; + } + + static override styles = css` + :host { + border-radius: var(--uui-border-radius); + border: 1px solid var(--uui-color-border); + background-color: var(--uui-color-surface); + color: var(--color-text); + display: grid; + grid-template-columns: repeat(auto-fill, minmax(32px, 1fr)); + position: sticky; + top: -18px; + left: 0px; + right: 0px; + height: 32px; + padding: 8px; + border-bottom: none; + } + + button { + color: var(--color-text); + border: none; + background: none; + cursor: pointer; + padding: 4px; + margin: 0; + border-radius: 4px; + box-sizing: border-box; + } + + button img { + width: 100%; + height: 100%; + } + + button:hover { + color: var(--color-primary); + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-tiptap-fixed-menu': UmbTiptapFixedMenuElement; + } +} diff --git a/src/packages/tiptap/manifests.ts b/src/packages/tiptap/manifests.ts new file mode 100644 index 0000000000..1dcd948374 --- /dev/null +++ b/src/packages/tiptap/manifests.ts @@ -0,0 +1,4 @@ +import { manifests as propertyEditors } from './property-editors/manifests.js'; +import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [...propertyEditors]; diff --git a/src/packages/tiptap/property-editors/manifests.ts b/src/packages/tiptap/property-editors/manifests.ts new file mode 100644 index 0000000000..1a38fe7fe9 --- /dev/null +++ b/src/packages/tiptap/property-editors/manifests.ts @@ -0,0 +1,4 @@ +import { manifests as tiptapManifests } from './tiptap/manifests.js'; +import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [...tiptapManifests]; diff --git a/src/packages/tiptap/property-editors/tiptap/manifests.ts b/src/packages/tiptap/property-editors/tiptap/manifests.ts new file mode 100644 index 0000000000..ba824b19d6 --- /dev/null +++ b/src/packages/tiptap/property-editors/tiptap/manifests.ts @@ -0,0 +1,16 @@ +import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [ + { + type: 'propertyEditorUi', + alias: 'Umb.PropertyEditorUi.Tiptap', + name: 'Tiptap Property Editor UI', + element: () => import('./property-editor-ui-tiptap.element.js'), + meta: { + label: 'Tiptap Editor', + propertyEditorSchemaAlias: 'Umbraco.Tiptap', + icon: 'icon-document', + group: 'richText', + }, + }, +]; diff --git a/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts new file mode 100644 index 0000000000..27390b2a0d --- /dev/null +++ b/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -0,0 +1,49 @@ +import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; +import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; + +import '../../components/input-tiptap/input-tiptap.element.js'; + +// Look at Tiny for correct types +export interface UmbRichTextEditorValueType { + markup?: string; + blocks?: any; +} + +/** + * @element umb-property-editor-ui-tiptap + */ +@customElement('umb-property-editor-ui-tiptap') +export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements UmbPropertyEditorUiElement { + // + public set config(config: UmbPropertyEditorConfigCollection | undefined) { + this._config = config; + } + + @property({ attribute: false }) + public set value(value: UmbRichTextEditorValueType) { + this._value = value; + } + public get value(): UmbRichTextEditorValueType { + return this._value; + } + + @state() + _config?: UmbPropertyEditorConfigCollection; + + @state() + private _value: UmbRichTextEditorValueType = {}; + + override render() { + return html``; + } +} + +export default UmbPropertyEditorUITiptapElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-property-editor-ui-tiptap': UmbPropertyEditorUITiptapElement; + } +} diff --git a/src/packages/tiptap/umbraco-package.ts b/src/packages/tiptap/umbraco-package.ts new file mode 100644 index 0000000000..79a0937305 --- /dev/null +++ b/src/packages/tiptap/umbraco-package.ts @@ -0,0 +1,9 @@ +export const name = 'Umbraco.Core.TipTap'; +export const extensions = [ + { + name: 'TipTap Bundle', + alias: 'Umb.Bundle.TipTap', + type: 'bundle', + js: () => import('./manifests.js'), + }, +]; diff --git a/src/packages/tiptap/vite.config.ts b/src/packages/tiptap/vite.config.ts new file mode 100644 index 0000000000..7e6c0e3f77 --- /dev/null +++ b/src/packages/tiptap/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'vite'; +import { rmSync } from 'fs'; +import { getDefaultConfig } from '../../vite-config-base'; + +const dist = '../../../dist-cms/packages/tiptap'; + +// delete the unbundled dist folder +rmSync(dist, { recursive: true, force: true }); + +export default defineConfig({ + ...getDefaultConfig({ dist }), +}); diff --git a/tsconfig.json b/tsconfig.json index 13058d558f..23ba90be60 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -135,6 +135,7 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js "@umbraco-cms/backoffice/external/router-slot": ["./src/external/router-slot/index.ts"], "@umbraco-cms/backoffice/external/rxjs": ["./src/external/rxjs/index.ts"], "@umbraco-cms/backoffice/external/tinymce": ["./src/external/tinymce/index.ts"], + "@umbraco-cms/backoffice/external/tiptap": ["./src/external/tiptap/index.ts"], "@umbraco-cms/backoffice/external/uui": ["./src/external/uui/index.ts"], "@umbraco-cms/backoffice/external/uuid": ["./src/external/uuid/index.ts"] } From 0cf07db73f06d004e6e15b07eb2f1f0d89df0aac Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:15:26 +0200 Subject: [PATCH 003/241] package json --- src/packages/tiptap/package.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/packages/tiptap/package.json diff --git a/src/packages/tiptap/package.json b/src/packages/tiptap/package.json new file mode 100644 index 0000000000..00d278e4d9 --- /dev/null +++ b/src/packages/tiptap/package.json @@ -0,0 +1,8 @@ +{ + "name": "@umbraco-backoffice/tiptap", + "private": true, + "type": "module", + "scripts": { + "build": "vite build" + } +} From 3f480945e5bb15736d7de24ea031fa4bcacee56b Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:33:09 +0200 Subject: [PATCH 004/241] add editor schema --- .../tiptap/property-editors/Umbraco.Tiptap.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/packages/tiptap/property-editors/Umbraco.Tiptap.ts diff --git a/src/packages/tiptap/property-editors/Umbraco.Tiptap.ts b/src/packages/tiptap/property-editors/Umbraco.Tiptap.ts new file mode 100644 index 0000000000..7837aea024 --- /dev/null +++ b/src/packages/tiptap/property-editors/Umbraco.Tiptap.ts @@ -0,0 +1,13 @@ +import type { ManifestPropertyEditorSchema } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifest: ManifestPropertyEditorSchema = { + type: 'propertyEditorSchema', + name: 'Rich Text Tiptap', + alias: 'Umbraco.Tiptap', + meta: { + defaultPropertyEditorUiAlias: 'Umb.PropertyEditorUi.Tiptap', + settings: { + properties: [], + }, + }, +}; From f6a788289ef1d120949d5f5e3a40ee4903e86d5d Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 10 Sep 2024 13:36:54 +0200 Subject: [PATCH 005/241] use json schema --- .../tiptap/property-editors/Umbraco.Tiptap.ts | 13 ------------- .../tiptap/property-editors/tiptap/manifests.ts | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 src/packages/tiptap/property-editors/Umbraco.Tiptap.ts diff --git a/src/packages/tiptap/property-editors/Umbraco.Tiptap.ts b/src/packages/tiptap/property-editors/Umbraco.Tiptap.ts deleted file mode 100644 index 7837aea024..0000000000 --- a/src/packages/tiptap/property-editors/Umbraco.Tiptap.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { ManifestPropertyEditorSchema } from '@umbraco-cms/backoffice/extension-registry'; - -export const manifest: ManifestPropertyEditorSchema = { - type: 'propertyEditorSchema', - name: 'Rich Text Tiptap', - alias: 'Umbraco.Tiptap', - meta: { - defaultPropertyEditorUiAlias: 'Umb.PropertyEditorUi.Tiptap', - settings: { - properties: [], - }, - }, -}; diff --git a/src/packages/tiptap/property-editors/tiptap/manifests.ts b/src/packages/tiptap/property-editors/tiptap/manifests.ts index ba824b19d6..140d8e4b96 100644 --- a/src/packages/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/tiptap/property-editors/tiptap/manifests.ts @@ -8,7 +8,7 @@ export const manifests: Array = [ element: () => import('./property-editor-ui-tiptap.element.js'), meta: { label: 'Tiptap Editor', - propertyEditorSchemaAlias: 'Umbraco.Tiptap', + propertyEditorSchemaAlias: 'Umbraco.Plain.Json', icon: 'icon-document', group: 'richText', }, From 8876590fc7fc622c355df5988b967ab69b27ac0e Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:12:05 +0200 Subject: [PATCH 006/241] read an write value --- .../input-tiptap/input-tiptap.element.ts | 21 +++------------- .../property-editor-ui-tiptap.element.ts | 25 +++++++++++++------ 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts index 1932b8d77e..2c6bfc01bc 100644 --- a/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -6,6 +6,7 @@ import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/ import './tiptap-fixed-menu.element.js'; import { Editor, StarterKit } from '@umbraco-cms/backoffice/external/tiptap'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; @customElement('umb-input-tiptap') export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '') { @@ -22,23 +23,7 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' if (!editor) return; - const json = - this.value && typeof this.value === 'string' - ? JSON.parse(this.value) - : { - type: 'doc', - content: [ - { - type: 'paragraph', - content: [ - { - type: 'text', - text: 'Hello Umbraco', - }, - ], - }, - ], - }; + const json = this.value && typeof this.value === 'string' ? JSON.parse(this.value) : this.value; // TODO: Try Disable css inject to remove prosemirror css this._editor = new Editor({ @@ -48,7 +33,7 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' onUpdate: ({ editor }) => { const json = editor.getJSON(); this.value = JSON.stringify(json); - console.log('json', json); + this.dispatchEvent(new UmbChangeEvent()); }, }); } diff --git a/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index 27390b2a0d..c1683214d7 100644 --- a/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -1,10 +1,12 @@ +import type UmbInputTiptapElement from '../../components/input-tiptap/input-tiptap.element.js'; import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; +import { + UmbPropertyValueChangeEvent, + type UmbPropertyEditorConfigCollection, +} from '@umbraco-cms/backoffice/property-editor'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; -import '../../components/input-tiptap/input-tiptap.element.js'; - // Look at Tiny for correct types export interface UmbRichTextEditorValueType { markup?: string; @@ -22,10 +24,10 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U } @property({ attribute: false }) - public set value(value: UmbRichTextEditorValueType) { + public set value(value: string) { this._value = value; } - public get value(): UmbRichTextEditorValueType { + public get value(): string { return this._value; } @@ -33,10 +35,19 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U _config?: UmbPropertyEditorConfigCollection; @state() - private _value: UmbRichTextEditorValueType = {}; + private _value: string = ''; + + #onChange(event: CustomEvent & { target: UmbInputTiptapElement }) { + const value = event.target.value as string; + this._value = value; + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + } override render() { - return html``; + return html``; } } From fe7b941dd26132d42d7efd5fa8900185ac6d8372 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:16:14 +0200 Subject: [PATCH 007/241] fix import --- .../tiptap/property-editor-ui-tiptap.element.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index c1683214d7..23fd1fbfb1 100644 --- a/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -7,6 +7,8 @@ import { } from '@umbraco-cms/backoffice/property-editor'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; +import '../../components/input-tiptap/input-tiptap.element.js'; + // Look at Tiny for correct types export interface UmbRichTextEditorValueType { markup?: string; From e20a7d3c28bac3ec99e9005d54c71fbd13194cbc Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:28:51 +0200 Subject: [PATCH 008/241] install more tiptap extensions --- package-lock.json | 26 ++++++++++++++++++++++++++ package.json | 2 ++ 2 files changed, 28 insertions(+) diff --git a/package-lock.json b/package-lock.json index cbc477b3b5..2707efe191 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,8 @@ ], "dependencies": { "@tiptap/core": "^2.6.6", + "@tiptap/extension-text-align": "^2.6.6", + "@tiptap/extension-underline": "^2.6.6", "@tiptap/pm": "^2.6.6", "@tiptap/starter-kit": "^2.6.6", "@types/diff": "^5.2.1", @@ -6722,6 +6724,30 @@ "@tiptap/core": "^2.6.6" } }, + "node_modules/@tiptap/extension-text-align": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.6.6.tgz", + "integrity": "sha512-WdyxULEEHfI3hRDHAFOUoeP84h9myabadfjtZrub7/zO2PKKPAZLBN2vWat5PowH8E8GYX8vqKr9vaX+slfh5g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, + "node_modules/@tiptap/extension-underline": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.6.6.tgz", + "integrity": "sha512-3A4HqsDM/AFb2VaeWACpGexjgI257kz0yU4jNV8uyydDR2KhqeinuEnoSoOmx9T3pL006TWfPg4vaQYPO3qvrQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, "node_modules/@tiptap/pm": { "version": "2.6.6", "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.6.6.tgz", diff --git a/package.json b/package.json index 911ef8621f..b63b9a48a1 100644 --- a/package.json +++ b/package.json @@ -213,6 +213,8 @@ }, "dependencies": { "@tiptap/core": "^2.6.6", + "@tiptap/extension-text-align": "^2.6.6", + "@tiptap/extension-underline": "^2.6.6", "@tiptap/pm": "^2.6.6", "@tiptap/starter-kit": "^2.6.6", "@types/diff": "^5.2.1", From 4188b13db12bfe08a1718fdd6681a59fd6eaad69 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:29:02 +0200 Subject: [PATCH 009/241] styling and icons --- src/external/tiptap/index.ts | 2 + .../tiptap/components/input-tiptap/icons.ts | 252 ++++++++++++++++++ .../input-tiptap/input-tiptap.element.ts | 37 ++- .../input-tiptap/tiptap-fixed-menu.element.ts | 214 +++++---------- 4 files changed, 343 insertions(+), 162 deletions(-) create mode 100644 src/packages/tiptap/components/input-tiptap/icons.ts diff --git a/src/external/tiptap/index.ts b/src/external/tiptap/index.ts index 1d5fb7fe1e..42ab4c52b1 100644 --- a/src/external/tiptap/index.ts +++ b/src/external/tiptap/index.ts @@ -1,2 +1,4 @@ export * from '@tiptap/core'; export * from '@tiptap/starter-kit'; +export * from '@tiptap/extension-underline'; +export * from '@tiptap/extension-text-align'; diff --git a/src/packages/tiptap/components/input-tiptap/icons.ts b/src/packages/tiptap/components/input-tiptap/icons.ts new file mode 100644 index 0000000000..1d6e9544aa --- /dev/null +++ b/src/packages/tiptap/components/input-tiptap/icons.ts @@ -0,0 +1,252 @@ +import { html } from '@umbraco-cms/backoffice/external/lit'; + +export const bold = html` + +`; + +export const italic = html` + + + +`; + +export const underline = html` + + +`; + +export const strikethrough = html` + + + +`; +export const heading1 = html` + + + + +`; +export const heading2 = html` + + + + +`; +export const heading3 = html` + + + + + +`; +export const blockquote = html` + + + + +`; +export const code = html` + + +`; +export const bulletList = html` + + + + + + +`; +export const orderedList = html` + + + + + + +`; +export const horizontalRule = html` + + + +`; +export const alignLeft = html` + + + +`; +export const alignCenter = html` + + + +`; +export const alignRight = html` + + + +`; +export const alignJustify = html` + + + +`; diff --git a/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts index 2c6bfc01bc..60e01af1c8 100644 --- a/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -5,7 +5,7 @@ import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import './tiptap-fixed-menu.element.js'; -import { Editor, StarterKit } from '@umbraco-cms/backoffice/external/tiptap'; +import { Editor, StarterKit, TextAlign, Underline } from '@umbraco-cms/backoffice/external/tiptap'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; @customElement('umb-input-tiptap') @@ -28,7 +28,13 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' // TODO: Try Disable css inject to remove prosemirror css this._editor = new Editor({ element: editor, - extensions: [StarterKit], + extensions: [ + StarterKit, + TextAlign.configure({ + types: ['heading', 'paragraph', 'blockquote', 'ordered_list', 'bullet_list'], + }), + Underline, + ], content: json, onUpdate: ({ editor }) => { const json = editor.getJSON(); @@ -44,7 +50,7 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' override render() { return html` - +
`; } @@ -54,6 +60,9 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' #editor { border-radius: var(--uui-border-radius); border: 1px solid var(--uui-color-border); + border-top-left-radius: 0; + border-top-right-radius: 0; + border-top: 0; margin: 0 auto; box-sizing: border-box; height: 100%; @@ -64,6 +73,18 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' display: grid; /* Don't ask me why this is needed, but it is. */ } + #editor pre { + background-color: var(--uui-color-surface-alt); + padding: var(--uui-size-space-2) var(--uui-size-space-4); + border-radius: calc(var(--uui-border-radius) * 2); + } + + #editor code:not(pre > code) { + background-color: var(--uui-color-surface-alt); + padding: var(--uui-size-space-1) var(--uui-size-space-2); + border-radius: calc(var(--uui-border-radius) * 2); + } + #editor code { font-family: 'Roboto Mono', monospace; background: none; @@ -77,12 +98,12 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' outline: none; white-space: pre-wrap; } - .ProseMirror p, - .ProseMirror h1, - .ProseMirror h2, - .ProseMirror h3 { + #editor p, + #editor h1, + #editor h2, + #editor h3 { margin-top: 0; - margin-bottom: 0.1rem; + margin-bottom: 0.5em; } `, ]; diff --git a/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 28a3a2185f..8dd8d78f6a 100644 --- a/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -1,3 +1,21 @@ +import { + alignCenter, + alignJustify, + alignLeft, + alignRight, + blockquote, + bold, + bulletList, + code, + heading1, + heading2, + heading3, + horizontalRule, + italic, + orderedList, + strikethrough, + underline, +} from './icons.js'; import { LitElement, css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; @@ -5,207 +23,94 @@ import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; export class UmbTiptapFixedMenuElement extends LitElement { @state() actions = [ - { - name: 'paragraph', - icon: html` - - - `, - command: () => this.editor?.chain().focus().setParagraph().run(), - }, + // TODO: I don't think we need a paragraph button. It's the default state. + // { + // name: 'paragraph', + // icon: html` + // + // + // `, + // command: () => this.editor?.chain().focus().setParagraph().run(), + // }, { name: 'bold', - icon: html` - - - `, + icon: bold, command: () => this.editor?.chain().focus().toggleBold().run(), }, { name: 'italic', - icon: html` - - - `, + icon: italic, command: () => this.editor?.chain().focus().toggleItalic().run(), }, { name: 'underline', - icon: html` - - - `, - // command: () => this.editor?.chain().focus().toggleUnderline().run(), + icon: underline, + command: () => this.editor?.chain().focus().toggleUnderline().run(), }, { name: 'strikethrough', - icon: html` - - - `, + icon: strikethrough, command: () => this.editor?.chain().focus().toggleStrike().run(), }, { - name: 'heading-1', - icon: html` - - - `, + name: 'h1', + icon: heading1, command: () => this.editor?.chain().focus().toggleHeading({ level: 1 }).run(), }, { - name: 'heading-2', - icon: html` - - - `, + name: 'h2', + icon: heading2, command: () => this.editor?.chain().focus().toggleHeading({ level: 2 }).run(), }, { - name: 'heading-3', - icon: html` - - - `, + name: 'h3', + icon: heading3, command: () => this.editor?.chain().focus().toggleHeading({ level: 3 }).run(), }, - { - name: 'heading-4', - icon: html` - - - `, - command: () => this.editor?.chain().focus().toggleHeading({ level: 4 }).run(), - }, - { - name: 'heading-5', - icon: html` - - - `, - command: () => this.editor?.chain().focus().toggleHeading({ level: 5 }).run(), - }, { name: 'blockquote', - icon: html` - - - `, + icon: blockquote, command: () => this.editor?.chain().focus().toggleBlockquote().run(), }, { name: 'code', - icon: html` - - - `, + icon: code, command: () => this.editor?.chain().focus().toggleCodeBlock().run(), }, { name: 'bullet-list', - icon: html` - - - `, + icon: bulletList, command: () => this.editor?.chain().focus().toggleBulletList().run(), }, { name: 'ordered-list', - icon: html` - - - `, + icon: orderedList, command: () => this.editor?.chain().focus().toggleOrderedList().run(), }, { name: 'horizontal-rule', - icon: html` - - - `, + icon: horizontalRule, command: () => this.editor?.chain().focus().setHorizontalRule().run(), }, { name: 'align-left', - icon: html` - - - `, - // command: () => this.editor?.chain().focus().setTextAlign('left').run(), + icon: alignLeft, + command: () => this.editor?.chain().focus().setTextAlign('left').run(), }, { name: 'align-center', - icon: html` - - - `, - // command: () => this.editor?.chain().focus().setTextAlign('center').run(), + icon: alignCenter, + command: () => this.editor?.chain().focus().setTextAlign('center').run(), }, { name: 'align-right', - icon: html` - - - `, - // command: () => this.editor?.chain().focus().setTextAlign('right').run(), + icon: alignRight, + command: () => this.editor?.chain().focus().setTextAlign('right').run(), }, { name: 'align-justify', - icon: html` - - - `, - // command: () => this.editor?.chain().focus().setTextAlign('justify').run(), - }, - { - name: 'image', - icon: html` - - - `, - command: () => { - const url = prompt('Enter the image URL'); - if (url) { - // this.editor?.chain().focus().setImage({ src: url }).run(); - } - }, - }, - { - name: 'add', - icon: html` - - - `, - command: () => { - const tag = prompt('Enter the tag'); - if (tag) { - // this.editor?.chain().focus().addCustomNode(tag).run(); - } - }, + icon: alignJustify, + command: () => this.editor?.chain().focus().setTextAlign('justify').run(), }, ]; @@ -227,22 +132,22 @@ export class UmbTiptapFixedMenuElement extends LitElement { background-color: var(--uui-color-surface); color: var(--color-text); display: grid; - grid-template-columns: repeat(auto-fill, minmax(32px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(24px, 1fr)); position: sticky; - top: -18px; + top: -25px; left: 0px; right: 0px; - height: 32px; - padding: 8px; - border-bottom: none; + padding: 4px; } button { - color: var(--color-text); + color: var(--uui-color-interactive); + width: 24px; + height: 24px; + padding: 4px; border: none; background: none; cursor: pointer; - padding: 4px; margin: 0; border-radius: 4px; box-sizing: border-box; @@ -254,7 +159,8 @@ export class UmbTiptapFixedMenuElement extends LitElement { } button:hover { - color: var(--color-primary); + color: var(--uui-color-interactive-emphasis); + background-color: var(--uui-color-surface-alt); } `; } From f2102c9e34717e59e668fdbe504cbddaa6fdf141 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Wed, 11 Sep 2024 10:03:35 +0200 Subject: [PATCH 010/241] install tiptap link --- package-lock.json | 22 ++++++++++++++++++++++ package.json | 1 + 2 files changed, 23 insertions(+) diff --git a/package-lock.json b/package-lock.json index 2707efe191..1f042effe8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,7 @@ ], "dependencies": { "@tiptap/core": "^2.6.6", + "@tiptap/extension-link": "^2.6.6", "@tiptap/extension-text-align": "^2.6.6", "@tiptap/extension-underline": "^2.6.6", "@tiptap/pm": "^2.6.6", @@ -6664,6 +6665,22 @@ "@tiptap/core": "^2.6.6" } }, + "node_modules/@tiptap/extension-link": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.6.6.tgz", + "integrity": "sha512-NJSR5Yf/dI3do0+Mr6e6nkbxRQcqbL7NOPxo5Xw8VaKs2Oe8PX+c7hyqN3GZgn6uEbZdbVi1xjAniUokouwpFg==", + "dependencies": { + "linkifyjs": "^4.1.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6", + "@tiptap/pm": "^2.6.6" + } + }, "node_modules/@tiptap/extension-list-item": { "version": "2.6.6", "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.6.6.tgz", @@ -16343,6 +16360,11 @@ "uc.micro": "^2.0.0" } }, + "node_modules/linkifyjs": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.1.3.tgz", + "integrity": "sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg==" + }, "node_modules/lit": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/lit/-/lit-3.2.0.tgz", diff --git a/package.json b/package.json index b63b9a48a1..321787d1bd 100644 --- a/package.json +++ b/package.json @@ -213,6 +213,7 @@ }, "dependencies": { "@tiptap/core": "^2.6.6", + "@tiptap/extension-link": "^2.6.6", "@tiptap/extension-text-align": "^2.6.6", "@tiptap/extension-underline": "^2.6.6", "@tiptap/pm": "^2.6.6", From 6323004492f153969eafad66b0f4a6f88b85b624 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Wed, 11 Sep 2024 10:03:43 +0200 Subject: [PATCH 011/241] styling --- src/external/tiptap/index.ts | 1 + .../tiptap/components/input-tiptap/icons.ts | 80 +++++++++++-------- .../input-tiptap/input-tiptap.element.ts | 25 +++++- .../input-tiptap/tiptap-fixed-menu.element.ts | 59 ++++++++++++-- 4 files changed, 123 insertions(+), 42 deletions(-) diff --git a/src/external/tiptap/index.ts b/src/external/tiptap/index.ts index 42ab4c52b1..9eae18bcdd 100644 --- a/src/external/tiptap/index.ts +++ b/src/external/tiptap/index.ts @@ -2,3 +2,4 @@ export * from '@tiptap/core'; export * from '@tiptap/starter-kit'; export * from '@tiptap/extension-underline'; export * from '@tiptap/extension-text-align'; +export * from '@tiptap/extension-link'; diff --git a/src/packages/tiptap/components/input-tiptap/icons.ts b/src/packages/tiptap/components/input-tiptap/icons.ts index 1d6e9544aa..2676404411 100644 --- a/src/packages/tiptap/components/input-tiptap/icons.ts +++ b/src/packages/tiptap/components/input-tiptap/icons.ts @@ -1,9 +1,10 @@ import { html } from '@umbraco-cms/backoffice/external/lit'; +const iconSize = '16px'; export const bold = html``; export const heading1 = html``; export const heading2 = html``; export const heading3 = html``; export const blockquote = html``; export const code = html``; export const bulletList = html``; export const orderedList = html``; export const horizontalRule = html``; export const alignLeft = html``; export const alignCenter = html``; export const alignRight = html``; export const alignJustify = html` `; + +export const link = html` + + +`; diff --git a/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts index 60e01af1c8..209fb1836d 100644 --- a/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -1,15 +1,17 @@ +import type { UmbTiptapFixedMenuElement } from './tiptap-fixed-menu.element.js'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; -import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { css, customElement, html, property, query, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import './tiptap-fixed-menu.element.js'; -import { Editor, StarterKit, TextAlign, Underline } from '@umbraco-cms/backoffice/external/tiptap'; +import { Editor, Link, StarterKit, TextAlign, Underline } from '@umbraco-cms/backoffice/external/tiptap'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; @customElement('umb-input-tiptap') export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '') { + @query('umb-tiptap-fixed-menu') _fixedMenuElement!: UmbTiptapFixedMenuElement; @property({ attribute: false }) configuration?: UmbPropertyEditorConfigCollection; @@ -31,15 +33,25 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' extensions: [ StarterKit, TextAlign.configure({ - types: ['heading', 'paragraph', 'blockquote', 'ordered_list', 'bullet_list'], + types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], }), + Link, Underline, ], content: json, + onSelectionUpdate: ({ editor }) => { + const { $from } = editor.state.selection; + const activeMarks = $from.node(); + + // Log the active marks + console.log('Active Marks:', activeMarks); + this._fixedMenuElement.onUpdate(); // TODO: This is a hack to force the fixed menu to update. We need to find a better way. + }, onUpdate: ({ editor }) => { const json = editor.getJSON(); this.value = JSON.stringify(json); this.dispatchEvent(new UmbChangeEvent()); + this._fixedMenuElement.onUpdate(); // TODO: This is a hack to force the fixed menu to update. We need to find a better way. }, }); } @@ -50,7 +62,10 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' override render() { return html` - +
`; } @@ -77,6 +92,7 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' background-color: var(--uui-color-surface-alt); padding: var(--uui-size-space-2) var(--uui-size-space-4); border-radius: calc(var(--uui-border-radius) * 2); + overflow-x: auto; } #editor code:not(pre > code) { @@ -97,6 +113,7 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' width: 100%; outline: none; white-space: pre-wrap; + min-width: 0; } #editor p, #editor h1, diff --git a/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 8dd8d78f6a..e34eb1445f 100644 --- a/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -12,6 +12,7 @@ import { heading3, horizontalRule, italic, + link, orderedList, strikethrough, underline, @@ -35,92 +36,130 @@ export class UmbTiptapFixedMenuElement extends LitElement { { name: 'bold', icon: bold, + isActive: () => this.editor?.isActive('bold'), command: () => this.editor?.chain().focus().toggleBold().run(), }, { name: 'italic', icon: italic, + isActive: () => this.editor?.isActive('italic'), command: () => this.editor?.chain().focus().toggleItalic().run(), }, { name: 'underline', icon: underline, + isActive: () => this.editor?.isActive('underline'), command: () => this.editor?.chain().focus().toggleUnderline().run(), }, { name: 'strikethrough', icon: strikethrough, + isActive: () => this.editor?.isActive('strike'), command: () => this.editor?.chain().focus().toggleStrike().run(), }, { name: 'h1', icon: heading1, + isActive: () => this.editor?.isActive('heading', { level: 1 }), command: () => this.editor?.chain().focus().toggleHeading({ level: 1 }).run(), }, { name: 'h2', icon: heading2, + isActive: () => this.editor?.isActive('heading', { level: 2 }), command: () => this.editor?.chain().focus().toggleHeading({ level: 2 }).run(), }, { name: 'h3', icon: heading3, + isActive: () => this.editor?.isActive('heading', { level: 3 }), command: () => this.editor?.chain().focus().toggleHeading({ level: 3 }).run(), }, { name: 'blockquote', icon: blockquote, + isActive: () => this.editor?.isActive('blockquote'), command: () => this.editor?.chain().focus().toggleBlockquote().run(), }, { name: 'code', icon: code, + isActive: () => this.editor?.isActive('codeBlock'), command: () => this.editor?.chain().focus().toggleCodeBlock().run(), }, { name: 'bullet-list', icon: bulletList, + isActive: () => this.editor?.isActive('bulletList'), command: () => this.editor?.chain().focus().toggleBulletList().run(), }, { name: 'ordered-list', icon: orderedList, + isActive: () => this.editor?.isActive('orderedList'), command: () => this.editor?.chain().focus().toggleOrderedList().run(), }, { name: 'horizontal-rule', icon: horizontalRule, + isActive: () => this.editor?.isActive('horizontalRule'), command: () => this.editor?.chain().focus().setHorizontalRule().run(), }, { name: 'align-left', icon: alignLeft, + isActive: () => this.editor?.isActive({ textAlign: 'left' }), command: () => this.editor?.chain().focus().setTextAlign('left').run(), }, { name: 'align-center', icon: alignCenter, + isActive: () => this.editor?.isActive({ textAlign: 'center' }), command: () => this.editor?.chain().focus().setTextAlign('center').run(), }, { name: 'align-right', icon: alignRight, + isActive: () => this.editor?.isActive({ textAlign: 'right' }), command: () => this.editor?.chain().focus().setTextAlign('right').run(), }, { name: 'align-justify', icon: alignJustify, + isActive: () => this.editor?.isActive({ textAlign: 'justify' }), command: () => this.editor?.chain().focus().setTextAlign('justify').run(), }, + { + name: 'link', + icon: link, + command: () => { + const url = prompt('Enter the URL'); + + if (url) { + this.editor?.chain().focus().setLink({ href: url, target: '_blank' }).run(); + } + }, + }, ]; @property({ attribute: false }) editor?: Editor; + public onUpdate() { + //TODO: Find a better way to trigger a re-render of the menu when the editor is updated + // This is used to update the active state of the buttons + this.requestUpdate(); + console.log('onUpdate'); + } + override render() { return html` ${this.actions.map( - (action) => html` `, + (action) => html` + + `, )} `; } @@ -153,15 +192,23 @@ export class UmbTiptapFixedMenuElement extends LitElement { box-sizing: border-box; } - button img { - width: 100%; - height: 100%; - } - button:hover { color: var(--uui-color-interactive-emphasis); background-color: var(--uui-color-surface-alt); } + + button.active { + background-color: var(--uui-color-selected); + color: var(--uui-color-selected-contrast); + } + button.active:hover { + background-color: var(--uui-color-selected-emphasis); + } + + button img { + width: 100%; + height: 100%; + } `; } From 1744192f5f840c709d934272c41889ae0c93b659 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:06:27 +0200 Subject: [PATCH 012/241] fix update listeners --- .../input-tiptap/input-tiptap.element.ts | 16 ++---- .../input-tiptap/tiptap-fixed-menu.element.ts | 34 +++++++++--- .../input-tiptap/tiptap-hover-menu.element.ts | 55 +++++++++++++++++++ 3 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts diff --git a/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts index 209fb1836d..0b632b56d4 100644 --- a/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -6,6 +6,7 @@ import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import './tiptap-fixed-menu.element.js'; +import './tiptap-hover-menu.element.js'; import { Editor, Link, StarterKit, TextAlign, Underline } from '@umbraco-cms/backoffice/external/tiptap'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; @@ -35,23 +36,18 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' TextAlign.configure({ types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], }), - Link, + Link.configure({ openOnClick: false }), Underline, ], content: json, onSelectionUpdate: ({ editor }) => { const { $from } = editor.state.selection; const activeMarks = $from.node(); - - // Log the active marks - console.log('Active Marks:', activeMarks); - this._fixedMenuElement.onUpdate(); // TODO: This is a hack to force the fixed menu to update. We need to find a better way. }, onUpdate: ({ editor }) => { const json = editor.getJSON(); this.value = JSON.stringify(json); this.dispatchEvent(new UmbChangeEvent()); - this._fixedMenuElement.onUpdate(); // TODO: This is a hack to force the fixed menu to update. We need to find a better way. }, }); } @@ -62,10 +58,8 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' override render() { return html` - + +
`; } @@ -108,7 +102,7 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' font-size: 0.8rem; padding: 0; } - .ProseMirror { + .tiptap { height: 100%; width: 100%; outline: none; diff --git a/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index e34eb1445f..a63fb72121 100644 --- a/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -17,6 +17,7 @@ import { strikethrough, underline, } from './icons.js'; +import type { PropertyValues } from '@umbraco-cms/backoffice/external/lit'; import { LitElement, css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; @@ -133,24 +134,41 @@ export class UmbTiptapFixedMenuElement extends LitElement { name: 'link', icon: link, command: () => { + const text = prompt('Enter the text'); const url = prompt('Enter the URL'); - if (url) { - this.editor?.chain().focus().setLink({ href: url, target: '_blank' }).run(); + if (url && text && this.editor) { + const { from } = this.editor.state.selection; + this.editor + .chain() + .focus() + .insertContent(text) + .setTextSelection({ from: from, to: from + text.length }) + .setLink({ href: url, target: '_blank' }) + .run(); } }, }, ]; @property({ attribute: false }) - editor?: Editor; + get editor() { + return this.#editor; + } + set editor(value) { + const oldValue = this.#editor; + if (value === oldValue) { + return; + } + this.#editor = value; + this.#editor?.on('selectionUpdate', this.#onUpdate); + this.#editor?.on('update', this.#onUpdate); + } + #editor?: Editor; - public onUpdate() { - //TODO: Find a better way to trigger a re-render of the menu when the editor is updated - // This is used to update the active state of the buttons + #onUpdate = () => { this.requestUpdate(); - console.log('onUpdate'); - } + }; override render() { return html` diff --git a/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts b/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts new file mode 100644 index 0000000000..6dfc91ac56 --- /dev/null +++ b/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts @@ -0,0 +1,55 @@ +import { LitElement, PropertyValues, css, customElement, html, property } from '@umbraco-cms/backoffice/external/lit'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +@customElement('umb-tiptap-hover-menu') +export class UmbTiptapHoverMenuElement extends LitElement { + @property({ attribute: false }) + get editor() { + return this.#editor; + } + set editor(value) { + const oldValue = this.#editor; + if (value === oldValue) { + return; + } + this.#editor = value; + this.#editor?.on('selectionUpdate', this.#onUpdate); + this.#editor?.on('update', this.#onUpdate); + } + #editor?: Editor; + + override connectedCallback(): void { + super.connectedCallback(); + this.setAttribute('popover', ''); + } + + #onUpdate = () => { + console.log('LINK ACTIVE'); + if (this.editor?.isActive('link')) { + // show the popover + this.showPopover(); + } else { + this.requestUpdate(); + } + }; + + override render() { + console.log('RENDER HOVER MENU'); + return html``; + } + + static override styles = css` + :host { + position: fixed; + background-color: var(--uui-color-surface-alt); + border: 1px solid var(--uui-color-border); + border-radius: var(--uui-size-border-radius); + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-tiptap-hover-menu': UmbTiptapHoverMenuElement; + } +} From 4e2af4703d4a9d26e279a9f8de4993b59510fa15 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:11:21 +0200 Subject: [PATCH 013/241] cleanup --- .../tiptap/components/input-tiptap/input-tiptap.element.ts | 5 ----- .../components/input-tiptap/tiptap-fixed-menu.element.ts | 1 - .../components/input-tiptap/tiptap-hover-menu.element.ts | 2 +- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts index 0b632b56d4..1615081f67 100644 --- a/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -28,7 +28,6 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' const json = this.value && typeof this.value === 'string' ? JSON.parse(this.value) : this.value; - // TODO: Try Disable css inject to remove prosemirror css this._editor = new Editor({ element: editor, extensions: [ @@ -40,10 +39,6 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' Underline, ], content: json, - onSelectionUpdate: ({ editor }) => { - const { $from } = editor.state.selection; - const activeMarks = $from.node(); - }, onUpdate: ({ editor }) => { const json = editor.getJSON(); this.value = JSON.stringify(json); diff --git a/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index a63fb72121..598b787211 100644 --- a/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -17,7 +17,6 @@ import { strikethrough, underline, } from './icons.js'; -import type { PropertyValues } from '@umbraco-cms/backoffice/external/lit'; import { LitElement, css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; diff --git a/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts b/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts index 6dfc91ac56..a280a832ed 100644 --- a/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts +++ b/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts @@ -1,4 +1,4 @@ -import { LitElement, PropertyValues, css, customElement, html, property } from '@umbraco-cms/backoffice/external/lit'; +import { LitElement, css, customElement, html, property } from '@umbraco-cms/backoffice/external/lit'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; @customElement('umb-tiptap-hover-menu') From f33c4c5d3cd1804834855665552587bd27008ac8 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:22:05 +0200 Subject: [PATCH 014/241] move tiptap into rte package --- package.json | 1 + src/apps/backoffice/backoffice.element.ts | 2 +- src/packages/rte/manifests.ts | 3 +++ src/packages/{tiptap => rte}/package.json | 2 +- src/packages/{ => rte}/tiptap/components/index.ts | 0 .../{ => rte}/tiptap/components/input-tiptap/icons.ts | 0 .../{ => rte}/tiptap/components/input-tiptap/index.ts | 0 .../components/input-tiptap/input-tiptap.element.ts | 0 .../components/input-tiptap/tiptap-fixed-menu.element.ts | 0 .../components/input-tiptap/tiptap-hover-menu.element.ts | 0 src/packages/{ => rte}/tiptap/manifests.ts | 0 .../{ => rte}/tiptap/property-editors/manifests.ts | 0 .../tiptap/property-editors/tiptap/manifests.ts | 0 .../tiptap/property-editor-ui-tiptap.element.ts | 0 src/packages/rte/umbraco-package.ts | 9 +++++++++ src/packages/{tiptap => rte}/vite.config.ts | 2 +- src/packages/tiptap/umbraco-package.ts | 9 --------- 17 files changed, 16 insertions(+), 12 deletions(-) create mode 100644 src/packages/rte/manifests.ts rename src/packages/{tiptap => rte}/package.json (67%) rename src/packages/{ => rte}/tiptap/components/index.ts (100%) rename src/packages/{ => rte}/tiptap/components/input-tiptap/icons.ts (100%) rename src/packages/{ => rte}/tiptap/components/input-tiptap/index.ts (100%) rename src/packages/{ => rte}/tiptap/components/input-tiptap/input-tiptap.element.ts (100%) rename src/packages/{ => rte}/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts (100%) rename src/packages/{ => rte}/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts (100%) rename src/packages/{ => rte}/tiptap/manifests.ts (100%) rename src/packages/{ => rte}/tiptap/property-editors/manifests.ts (100%) rename src/packages/{ => rte}/tiptap/property-editors/tiptap/manifests.ts (100%) rename src/packages/{ => rte}/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts (100%) create mode 100644 src/packages/rte/umbraco-package.ts rename src/packages/{tiptap => rte}/vite.config.ts (84%) delete mode 100644 src/packages/tiptap/umbraco-package.ts diff --git a/package.json b/package.json index 321787d1bd..167886f918 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "./temporary-file": "./dist-cms/packages/core/temporary-file/index.js", "./themes": "./dist-cms/packages/core/themes/index.js", "./tiny-mce": "./dist-cms/packages/tiny-mce/index.js", + "./tiptap": "./dist-cms/packages/rte/tiptap/index.js", "./tree": "./dist-cms/packages/core/tree/index.js", "./ufm": "./dist-cms/packages/ufm/index.js", "./user-group": "./dist-cms/packages/user/user-group/index.js", diff --git a/src/apps/backoffice/backoffice.element.ts b/src/apps/backoffice/backoffice.element.ts index 42c5f423d6..21a07f340f 100644 --- a/src/apps/backoffice/backoffice.element.ts +++ b/src/apps/backoffice/backoffice.element.ts @@ -30,6 +30,7 @@ const CORE_PACKAGES = [ import('../../packages/property-editors/umbraco-package.js'), import('../../packages/publish-cache/umbraco-package.js'), import('../../packages/relations/umbraco-package.js'), + import('../../packages/rte/umbraco-package.js'), import('../../packages/search/umbraco-package.js'), import('../../packages/settings/umbraco-package.js'), import('../../packages/static-file/umbraco-package.js'), @@ -37,7 +38,6 @@ const CORE_PACKAGES = [ import('../../packages/telemetry/umbraco-package.js'), import('../../packages/templating/umbraco-package.js'), import('../../packages/tiny-mce/umbraco-package.js'), - import('../../packages/tiptap/umbraco-package.js'), import('../../packages/ufm/umbraco-package.js'), import('../../packages/umbraco-news/umbraco-package.js'), import('../../packages/user/umbraco-package.js'), diff --git a/src/packages/rte/manifests.ts b/src/packages/rte/manifests.ts new file mode 100644 index 0000000000..f4d7d8f1ff --- /dev/null +++ b/src/packages/rte/manifests.ts @@ -0,0 +1,3 @@ +import { manifests as tiptapManifests } from './tiptap/manifests.js'; + +export const manifests = [...tiptapManifests]; diff --git a/src/packages/tiptap/package.json b/src/packages/rte/package.json similarity index 67% rename from src/packages/tiptap/package.json rename to src/packages/rte/package.json index 00d278e4d9..20394e7e36 100644 --- a/src/packages/tiptap/package.json +++ b/src/packages/rte/package.json @@ -1,5 +1,5 @@ { - "name": "@umbraco-backoffice/tiptap", + "name": "@umbraco-backoffice/rte", "private": true, "type": "module", "scripts": { diff --git a/src/packages/tiptap/components/index.ts b/src/packages/rte/tiptap/components/index.ts similarity index 100% rename from src/packages/tiptap/components/index.ts rename to src/packages/rte/tiptap/components/index.ts diff --git a/src/packages/tiptap/components/input-tiptap/icons.ts b/src/packages/rte/tiptap/components/input-tiptap/icons.ts similarity index 100% rename from src/packages/tiptap/components/input-tiptap/icons.ts rename to src/packages/rte/tiptap/components/input-tiptap/icons.ts diff --git a/src/packages/tiptap/components/input-tiptap/index.ts b/src/packages/rte/tiptap/components/input-tiptap/index.ts similarity index 100% rename from src/packages/tiptap/components/input-tiptap/index.ts rename to src/packages/rte/tiptap/components/input-tiptap/index.ts diff --git a/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts similarity index 100% rename from src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts rename to src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts diff --git a/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts similarity index 100% rename from src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts rename to src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts diff --git a/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts similarity index 100% rename from src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts rename to src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts diff --git a/src/packages/tiptap/manifests.ts b/src/packages/rte/tiptap/manifests.ts similarity index 100% rename from src/packages/tiptap/manifests.ts rename to src/packages/rte/tiptap/manifests.ts diff --git a/src/packages/tiptap/property-editors/manifests.ts b/src/packages/rte/tiptap/property-editors/manifests.ts similarity index 100% rename from src/packages/tiptap/property-editors/manifests.ts rename to src/packages/rte/tiptap/property-editors/manifests.ts diff --git a/src/packages/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts similarity index 100% rename from src/packages/tiptap/property-editors/tiptap/manifests.ts rename to src/packages/rte/tiptap/property-editors/tiptap/manifests.ts diff --git a/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts similarity index 100% rename from src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts rename to src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts diff --git a/src/packages/rte/umbraco-package.ts b/src/packages/rte/umbraco-package.ts new file mode 100644 index 0000000000..68eece9518 --- /dev/null +++ b/src/packages/rte/umbraco-package.ts @@ -0,0 +1,9 @@ +export const name = 'Umbraco.Core.Rte'; +export const extensions = [ + { + name: 'RTE Bundle', + alias: 'Umb.Bundle.Rte', + type: 'bundle', + js: () => import('./manifests.js'), + }, +]; diff --git a/src/packages/tiptap/vite.config.ts b/src/packages/rte/vite.config.ts similarity index 84% rename from src/packages/tiptap/vite.config.ts rename to src/packages/rte/vite.config.ts index 7e6c0e3f77..1385afcf23 100644 --- a/src/packages/tiptap/vite.config.ts +++ b/src/packages/rte/vite.config.ts @@ -2,7 +2,7 @@ import { defineConfig } from 'vite'; import { rmSync } from 'fs'; import { getDefaultConfig } from '../../vite-config-base'; -const dist = '../../../dist-cms/packages/tiptap'; +const dist = '../../../dist-cms/packages/rte'; // delete the unbundled dist folder rmSync(dist, { recursive: true, force: true }); diff --git a/src/packages/tiptap/umbraco-package.ts b/src/packages/tiptap/umbraco-package.ts deleted file mode 100644 index 79a0937305..0000000000 --- a/src/packages/tiptap/umbraco-package.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const name = 'Umbraco.Core.TipTap'; -export const extensions = [ - { - name: 'TipTap Bundle', - alias: 'Umb.Bundle.TipTap', - type: 'bundle', - js: () => import('./manifests.js'), - }, -]; From a04830c5f63081141308c1b1f1e22dc1ae4ce610 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:25:43 +0200 Subject: [PATCH 015/241] generate tsconfig --- package.json | 1 + tsconfig.json | 1 + 2 files changed, 2 insertions(+) diff --git a/package.json b/package.json index 167886f918..e8c1739816 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,7 @@ "./external/router-slot": "./dist-cms/external/router-slot/index.js", "./external/rxjs": "./dist-cms/external/rxjs/index.js", "./external/tinymce": "./dist-cms/external/tinymce/index.js", + "./external/tiptap": "./dist-cms/external/tiptap/index.js", "./external/uui": "./dist-cms/external/uui/index.js", "./external/uuid": "./dist-cms/external/uuid/index.js" }, diff --git a/tsconfig.json b/tsconfig.json index 23ba90be60..e43e8f36d1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -114,6 +114,7 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js "@umbraco-cms/backoffice/temporary-file": ["./src/packages/core/temporary-file/index.ts"], "@umbraco-cms/backoffice/themes": ["./src/packages/core/themes/index.ts"], "@umbraco-cms/backoffice/tiny-mce": ["./src/packages/tiny-mce/index.ts"], + "@umbraco-cms/backoffice/tiptap": ["./src/packages/rte/tiptap/index.ts"], "@umbraco-cms/backoffice/tree": ["./src/packages/core/tree/index.ts"], "@umbraco-cms/backoffice/ufm": ["./src/packages/ufm/index.ts"], "@umbraco-cms/backoffice/user-group": ["./src/packages/user/user-group/index.ts"], From 72ce4d6c1e323272c7c10605bcc711ee44b4a237 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:30:38 +0200 Subject: [PATCH 016/241] merge --- package-lock.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/package-lock.json b/package-lock.json index a2d1864d46..b4f40bd654 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8203,6 +8203,10 @@ "resolved": "src/packages/relations", "link": true }, + "node_modules/@umbraco-backoffice/rte": { + "resolved": "src/packages/rte", + "link": true + }, "node_modules/@umbraco-backoffice/search": { "resolved": "src/packages/search", "link": true @@ -23663,6 +23667,9 @@ "src/packages/relations": { "name": "@umbraco-backoffice/relation" }, + "src/packages/rte": { + "name": "@umbraco-backoffice/rte" + }, "src/packages/search": { "name": "@umbraco-backoffice/search" }, From bec849ee5d848b4ed07863c9f9c531f252e344c3 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 13 Sep 2024 14:38:21 +0200 Subject: [PATCH 017/241] chore: set up test mock data for tiptap --- src/mocks/data/data-type/data-type.data.ts | 18 ++- .../data/document-type/document-type.data.ts | 114 +++++++++++++++++- src/mocks/data/document/document.data.ts | 84 +++++++++++-- 3 files changed, 203 insertions(+), 13 deletions(-) diff --git a/src/mocks/data/data-type/data-type.data.ts b/src/mocks/data/data-type/data-type.data.ts index f3c0ab8d4d..eec1070db2 100644 --- a/src/mocks/data/data-type/data-type.data.ts +++ b/src/mocks/data/data-type/data-type.data.ts @@ -934,7 +934,23 @@ export const data: Array = [ }, { name: 'Rich Text Editor', - id: 'dt-richTextEditor', + id: 'dt-richTextEditorTiptap', + parent: null, + editorAlias: 'Umbraco.RichText', + editorUiAlias: 'Umb.PropertyEditorUi.Tiptap', + hasChildren: false, + isFolder: false, + isDeletable: true, + canIgnoreStartNodes: false, + values: [ + { alias: 'dimensions', value: { height: 500 } }, + { alias: 'maxImageSize', value: 500 }, + { alias: 'ignoreUserStartNodes', value: false }, + ], + }, + { + name: 'Rich Text Editor (TinyMCE)', + id: 'dt-richTextEditorTinyMce', parent: null, editorAlias: 'Umbraco.RichText', editorUiAlias: 'Umb.PropertyEditorUi.TinyMCE', diff --git a/src/mocks/data/document-type/document-type.data.ts b/src/mocks/data/document-type/document-type.data.ts index 1011e3c467..4fef647c2e 100644 --- a/src/mocks/data/document-type/document-type.data.ts +++ b/src/mocks/data/document-type/document-type.data.ts @@ -87,10 +87,34 @@ export const data: Array = [ id: 'all-properties-group-key', }, alias: 'richTextEditor', - name: 'Rich Text editor', + name: 'Rich Text editor (Tiptap)', description: 'Some description to test with a long description.', dataType: { - id: 'dt-richTextEditor', + id: 'dt-richTextEditorTiptap', + }, + variesByCulture: false, + variesBySegment: false, + sortOrder: 0, + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + id: '1', + container: { + id: 'all-properties-group-key', + }, + alias: 'richTextEditorTinyMce', + name: 'Rich Text editor (TinyMce)', + description: 'Some description to test with a long description.', + dataType: { + id: 'dt-richTextEditorTinyMce', }, variesByCulture: false, variesBySegment: false, @@ -1721,4 +1745,90 @@ export const data: Array = [ properties: [], containers: [], }, + { + allowedTemplates: [], + defaultTemplate: { id: 'all-rtes-document-type-id' }, + id: 'all-rtes-document-type-id', + alias: 'allRtesDocumentType', + name: 'All RTEs document type', + description: null, + icon: 'icon-document', + allowedAsRoot: true, + variesByCulture: false, + variesBySegment: false, + isElement: false, + hasChildren: false, + parent: null, + isFolder: false, + properties: [ + { + id: '1dd0d4d2-cda8-4ac2-affd-a69fc10382b1', + container: { id: 'the-simplest-document-type-id-container' }, + alias: 'tiptap', + name: 'Tiptap', + description: + 'This is to test the default configuration for the Tiptap editor.\n\nSearch for **dt-richTextEditorTiptap** in the codebase to find the configuration and add configuration values.', + dataType: { id: 'dt-richTextEditorTiptap' }, + variesByCulture: false, + variesBySegment: false, + sortOrder: 0, + validation: { + mandatory: false, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + id: '2dd0d4d2-cda8-4ac2-affd-a69fc10382b1', + container: { id: 'the-simplest-document-type-id-container' }, + alias: 'tinymce', + name: 'TinyMCE', + description: ` +This is to test the default configuration of the TinyMCE editor. + +Search for **dt-richTextEditorTinyMce** in the codebase to find the configuration and add configuration values. + +**NB!** If this throws an error in console, go to \`input-tiny-mce.defaults.ts\` and comment out the script append on line 126: + +\`\`\`js +script.text = \`import "@umbraco-cms/backoffice/extension-registry";\`; +script.text = \`import "\${UMB_BLOCK_ENTRY_WEB_COMPONENTS_ABSOLUTE_PATH}";\`; +//editor.dom.doc.head.appendChild(script); +\`\`\``, + dataType: { id: 'dt-richTextEditorTinyMce' }, + variesByCulture: false, + variesBySegment: false, + sortOrder: 0, + validation: { + mandatory: false, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + ], + containers: [ + { + id: 'the-simplest-document-type-id-container', + parent: null, + name: 'Content', + type: 'Group', + sortOrder: 0, + }, + ], + allowedDocumentTypes: [], + compositions: [], + cleanup: { + preventCleanup: false, + keepAllVersionsNewerThanDays: null, + keepLatestVersionPerDayForDays: null, + }, + }, ]; diff --git a/src/mocks/data/document/document.data.ts b/src/mocks/data/document/document.data.ts index 2e3dce12d4..c8eb2ab5f0 100644 --- a/src/mocks/data/document/document.data.ts +++ b/src/mocks/data/document/document.data.ts @@ -93,20 +93,20 @@ export const data: Array = [
  • Aenean massa cum sociis natoque penatibus.
  • - Lorem ipsum dolor sit amet, consectetuer adipiscing + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor.

    - Lorem ipsum dolor sit amet, consectetuer - adipiscing elit. Aenean commodo ligula eget dolor. - Aenean massa strong. Cum sociis - natoque penatibus et magnis dis parturient montes, - nascetur ridiculus mus. Donec quam felis, ultricies - nec, pellentesque eu, pretium quis, sem. Nulla consequat - massa quis enim. Donec pede justo, fringilla vel, - aliquet nec, vulputate eget, arcu. In em - enim justo, rhoncus ut, imperdiet a, venenatis vitae, + Lorem ipsum dolor sit amet, consectetuer + adipiscing elit. Aenean commodo ligula eget dolor. + Aenean massa strong. Cum sociis + natoque penatibus et magnis dis parturient montes, + nascetur ridiculus mus. Donec quam felis, ultricies + nec, pellentesque eu, pretium quis, sem. Nulla consequat + massa quis enim. Donec pede justo, fringilla vel, + aliquet nec, vulputate eget, arcu. In em + enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam link dictum felis eu pede mollis pretium.
    @@ -802,4 +802,68 @@ export const data: Array = [ }, ], }, + { + urls: [ + { + culture: 'en-US', + url: '/', + }, + ], + template: null, + id: 'all-rtes-id', + parent: null, + documentType: { + id: 'all-rtes-document-type-id', + icon: 'icon-document', + }, + hasChildren: false, + noAccess: false, + isProtected: false, + isTrashed: false, + variants: [ + { + state: DocumentVariantStateModel.PUBLISHED, + publishDate: '2023-02-06T15:32:24.957009', + culture: null, + segment: null, + name: 'All RTEs', + createDate: '2023-02-06T15:32:05.350038', + updateDate: '2023-02-06T15:32:24.957009', + }, + ], + values: [ + { + alias: 'tiptap', + culture: null, + segment: null, + value: { + blocks: undefined, + markup: ` +

    + Some value for the RTE with an external link and an internal link foo foo +

    +

    + +

    +

    End of test content

    + `, + }, + }, + { + alias: 'tinymce', + culture: null, + segment: null, + value: { + blocks: undefined, + markup: ` +

    + Some value for the RTE with an external link and an internal link foo foo +

    +
    Macro alias: TestMacro
    +

    End of test content

    + `, + }, + }, + ], + }, ]; From 3f054cb12ef51dbe1add72f1221b35a3cc75aa76 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 13 Sep 2024 14:55:23 +0200 Subject: [PATCH 018/241] chore: add more test content --- src/mocks/data/document/document.data.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mocks/data/document/document.data.ts b/src/mocks/data/document/document.data.ts index c8eb2ab5f0..09ec5524e0 100644 --- a/src/mocks/data/document/document.data.ts +++ b/src/mocks/data/document/document.data.ts @@ -843,7 +843,7 @@ export const data: Array = [ Some value for the RTE with an external link and an internal link foo foo

    - + Jason

    End of test content

    `, @@ -860,6 +860,9 @@ export const data: Array = [ Some value for the RTE with an external link and an internal link foo foo

    Macro alias: TestMacro
    +

    + Jason +

    End of test content

    `, }, From 81c79088537672995c59d04bc81f14a37643561e Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 13 Sep 2024 14:55:40 +0200 Subject: [PATCH 019/241] chore: use already defined function for same purpose --- .../tiny-mce/property-editor-ui-tiny-mce.element.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts index d40071b228..8cb80938c6 100644 --- a/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts +++ b/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts @@ -149,7 +149,8 @@ export class UmbPropertyEditorUITinyMceElement extends UmbLitElement implements ...this._value, markup: markup, }; - this.dispatchEvent(new UmbPropertyValueChangeEvent()); + + this.#fireChangeEvent(); } override render() { From c5d33e403a10877fcb1fee3804b378fe6d9d2a4b Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 13 Sep 2024 14:56:00 +0200 Subject: [PATCH 020/241] feat: make tiptap use the predefined format of Umbraco.RichText --- .../input-tiptap/input-tiptap.element.ts | 11 +- .../property-editor-ui-tiptap.element.ts | 113 ++++++++++++++++-- 2 files changed, 106 insertions(+), 18 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 1615081f67..10d3ee9432 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -19,15 +19,11 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' @state() _editor!: Editor; - protected override firstUpdated(_changedProperties: PropertyValueMap | Map): void { - super.firstUpdated(_changedProperties); - + protected override firstUpdated(): void { const editor = this.shadowRoot?.querySelector('#editor'); if (!editor) return; - const json = this.value && typeof this.value === 'string' ? JSON.parse(this.value) : this.value; - this._editor = new Editor({ element: editor, extensions: [ @@ -38,10 +34,9 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' Link.configure({ openOnClick: false }), Underline, ], - content: json, + content: this.value.toString(), onUpdate: ({ editor }) => { - const json = editor.getJSON(); - this.value = JSON.stringify(json); + this.value = editor.getHTML(); this.dispatchEvent(new UmbChangeEvent()); }, }); diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index 23fd1fbfb1..7ab482cb77 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -8,13 +8,21 @@ import { import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import '../../components/input-tiptap/input-tiptap.element.js'; +import { + UmbBlockRteEntriesContext, + UmbBlockRteManagerContext, + type UmbBlockRteLayoutModel, +} from '@umbraco-cms/backoffice/block-rte'; +import type { UmbBlockValueType } from '@umbraco-cms/backoffice/block'; // Look at Tiny for correct types export interface UmbRichTextEditorValueType { - markup?: string; - blocks?: any; + markup: string; + blocks: UmbBlockValueType; } +const UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS = 'Umbraco.RichText'; + /** * @element umb-property-editor-ui-tiptap */ @@ -26,30 +34,115 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U } @property({ attribute: false }) - public set value(value: string) { - this._value = value; + public set value(value: UmbRichTextEditorValueType | undefined) { + const buildUpValue: Partial = value ? { ...value } : {}; + buildUpValue.markup ??= ''; + buildUpValue.blocks ??= { layout: {}, contentData: [], settingsData: [] }; + buildUpValue.blocks.layout ??= {}; + buildUpValue.blocks.contentData ??= []; + buildUpValue.blocks.settingsData ??= []; + this._value = buildUpValue as UmbRichTextEditorValueType; + + if (this._latestMarkup !== this._value.markup) { + this._markup = this._value.markup; + } + + this.#managerContext.setLayouts(buildUpValue.blocks.layout[UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS] ?? []); + this.#managerContext.setContents(buildUpValue.blocks.contentData); + this.#managerContext.setSettings(buildUpValue.blocks.settingsData); } - public get value(): string { + public get value(): UmbRichTextEditorValueType { return this._value; } + /** + * Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content. + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + readonly = false; + @state() _config?: UmbPropertyEditorConfigCollection; @state() - private _value: string = ''; + private _value: UmbRichTextEditorValueType = { + markup: '', + blocks: { layout: {}, contentData: [], settingsData: [] }, + }; + + // Separate state for markup, to avoid re-rendering/re-setting the value of the Tiptap editor when the value does not really change. + @state() + private _markup = ''; + private _latestMarkup = ''; // The latest value gotten from the Tiptap editor. + + #managerContext = new UmbBlockRteManagerContext(this); + #entriesContext = new UmbBlockRteEntriesContext(this); + + constructor() { + super(); + + this.observe(this.#entriesContext.layoutEntries, (layouts) => { + // Update manager: + this.#managerContext.setLayouts(layouts); + }); + + this.observe(this.#managerContext.layouts, (layouts) => { + this._value = { + ...this._value, + blocks: { ...this._value.blocks, layout: { [UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS]: layouts } }, + }; + this.#fireChangeEvent(); + }); + this.observe(this.#managerContext.contents, (contents) => { + this._value = { ...this._value, blocks: { ...this._value.blocks, contentData: contents } }; + this.#fireChangeEvent(); + }); + this.observe(this.#managerContext.settings, (settings) => { + this._value = { ...this._value, blocks: { ...this._value.blocks, settingsData: settings } }; + this.#fireChangeEvent(); + }); + } + + #fireChangeEvent() { + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + } #onChange(event: CustomEvent & { target: UmbInputTiptapElement }) { const value = event.target.value as string; - this._value = value; - this.dispatchEvent(new UmbPropertyValueChangeEvent()); + this._latestMarkup = value; + + // TODO: Validate blocks + // Loop through used, to remove the classes on these. + /*const blockEls = div.querySelectorAll(`umb-rte-block, umb-rte-block-inline`); + blockEls.forEach((blockEl) => { + blockEl.removeAttribute('contenteditable'); + blockEl.removeAttribute('class'); + }); + + // Remove unused Blocks of Blocks Layout. Leaving only the Blocks that are present in Markup. + //const blockElements = editor.dom.select(`umb-rte-block, umb-rte-block-inline`); + const usedContentUdis = Array.from(blockEls).map((blockElement) => blockElement.getAttribute('data-content-udi')); + const unusedBlocks = this.#managerContext.getLayouts().filter((x) => usedContentUdis.indexOf(x.contentUdi) === -1); + unusedBlocks.forEach((blockLayout) => { + this.#managerContext.removeOneLayout(blockLayout.contentUdi); + });*/ + + this._value = { + ...this._value, + markup: this._latestMarkup, + }; + + this.#fireChangeEvent(); } override render() { return html``; + .configuration=${this._config} + ?readonly=${this.readonly}>`; } } From ffaa17ab44377bc14e36303870a3cdfe28759fec Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 16 Sep 2024 15:22:36 +0100 Subject: [PATCH 021/241] Adds `tiptapExtension` manifest type --- .../core/extension-registry/models/index.ts | 3 +++ .../models/tiptap-extension.model.ts | 6 +++++ src/packages/rte/manifests.ts | 3 ++- .../tiptap/components/input-tiptap/index.ts | 1 + .../input-tiptap/tiptap-extension.ts | 22 +++++++++++++++++++ src/packages/rte/tiptap/index.ts | 1 + 6 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 src/packages/core/extension-registry/models/tiptap-extension.model.ts create mode 100644 src/packages/rte/tiptap/components/input-tiptap/tiptap-extension.ts create mode 100644 src/packages/rte/tiptap/index.ts diff --git a/src/packages/core/extension-registry/models/index.ts b/src/packages/core/extension-registry/models/index.ts index 00fec52d8f..d79b3f9e4a 100644 --- a/src/packages/core/extension-registry/models/index.ts +++ b/src/packages/core/extension-registry/models/index.ts @@ -42,6 +42,7 @@ import type { ManifestSectionView } from './section-view.model.js'; import type { ManifestStore, ManifestTreeStore, ManifestItemStore } from './store.model.js'; import type { ManifestTheme } from './theme.model.js'; import type { ManifestTinyMcePlugin } from './tinymce-plugin.model.js'; +import type { ManifestTiptapExtension } from './tiptap-extension.model.js'; import type { ManifestTree } from './tree.model.js'; import type { ManifestTreeItem } from './tree-item.model.js'; import type { ManifestUfmComponent } from './ufm-component.model.js'; @@ -113,6 +114,7 @@ export type * from './section.model.js'; export type * from './store.model.js'; export type * from './theme.model.js'; export type * from './tinymce-plugin.model.js'; +export type * from './tiptap-extension.model.js'; export type * from './tree-item.model.js'; export type * from './tree.model.js'; export type * from './ufm-component.model.js'; @@ -206,6 +208,7 @@ export type ManifestTypes = | ManifestStore | ManifestTheme | ManifestTinyMcePlugin + | ManifestTiptapExtension | ManifestTree | ManifestTreeItem | ManifestTreeStore diff --git a/src/packages/core/extension-registry/models/tiptap-extension.model.ts b/src/packages/core/extension-registry/models/tiptap-extension.model.ts new file mode 100644 index 0000000000..5d9ff73dfa --- /dev/null +++ b/src/packages/core/extension-registry/models/tiptap-extension.model.ts @@ -0,0 +1,6 @@ +import type { UmbTiptapExtensionBase } from '@umbraco-cms/backoffice/tiptap'; +import type { ManifestApi } from '@umbraco-cms/backoffice/extension-api'; + +export interface ManifestTiptapExtension extends ManifestApi { + type: 'tiptapExtension'; +} diff --git a/src/packages/rte/manifests.ts b/src/packages/rte/manifests.ts index f4d7d8f1ff..1a38fe7fe9 100644 --- a/src/packages/rte/manifests.ts +++ b/src/packages/rte/manifests.ts @@ -1,3 +1,4 @@ import { manifests as tiptapManifests } from './tiptap/manifests.js'; +import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; -export const manifests = [...tiptapManifests]; +export const manifests: Array = [...tiptapManifests]; diff --git a/src/packages/rte/tiptap/components/input-tiptap/index.ts b/src/packages/rte/tiptap/components/input-tiptap/index.ts index f11b53c7d9..0d9456b123 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/index.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/index.ts @@ -1 +1,2 @@ export * from './input-tiptap.element.js'; +export * from './tiptap-extension.js'; diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-extension.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-extension.ts new file mode 100644 index 0000000000..aa01ea3420 --- /dev/null +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-extension.ts @@ -0,0 +1,22 @@ +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { Editor, Extension, Mark, Node } from '@umbraco-cms/backoffice/external/tiptap'; +import type { TemplateResult } from '@umbraco-cms/backoffice/external/lit'; +import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export abstract class UmbTiptapExtensionBase extends UmbControllerBase implements UmbApi { + constructor(host: UmbControllerHost) { + super(host); + } + + abstract getExtensions(): Array; + + abstract getToolbarButtons(): Array; +} + +export interface UmbTiptapToolbarButton { + name: string; + icon: string | TemplateResult; + isActive: (editor?: Editor) => boolean | undefined; + command: (editor?: Editor) => boolean | undefined | void | Promise | Promise | Promise; +} diff --git a/src/packages/rte/tiptap/index.ts b/src/packages/rte/tiptap/index.ts new file mode 100644 index 0000000000..8a8c2711ca --- /dev/null +++ b/src/packages/rte/tiptap/index.ts @@ -0,0 +1 @@ +export * from './components/index.js'; From 1dc79920811840d0f7d0d3122b41227d5a1ab0d2 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 16 Sep 2024 15:24:41 +0100 Subject: [PATCH 022/241] [WIP] Placeholder media picker extension for Tiptap RTE --- .../rte/tiptap/extensions/manifests.ts | 10 +++ .../tiptap-mediapicker.extension.ts | 68 +++++++++++++++++++ src/packages/rte/tiptap/manifests.ts | 3 +- 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 src/packages/rte/tiptap/extensions/manifests.ts create mode 100644 src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts new file mode 100644 index 0000000000..6780ce1c09 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -0,0 +1,10 @@ +import type { ManifestTiptapExtension } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [ + { + type: 'tiptapExtension', + alias: 'Umb.Tiptap.MediaPicker', + name: 'Media Picker Tiptap Extension', + api: () => import('./tiptap-mediapicker.extension.js'), + }, +]; diff --git a/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts b/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts new file mode 100644 index 0000000000..be14701cef --- /dev/null +++ b/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts @@ -0,0 +1,68 @@ +import { UmbTiptapExtensionBase } from '../components/input-tiptap/tiptap-extension.js'; +import { mergeAttributes, Node } from '@umbraco-cms/backoffice/external/tiptap'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; +import { UMB_MEDIA_PICKER_MODAL } from '@umbraco-cms/backoffice/media'; + +export default class UmbTiptapMediaPickerPlugin extends UmbTiptapExtensionBase { + getExtensions() { + return [ + Node.create({ + name: 'umbMediaPicker', + group: 'block', + marks: '', + draggable: true, + addNodeView() { + return () => { + //console.log('umb-media.addNodeView'); + const dom = document.createElement('umb-debug'); + dom.attributes.setNamedItem(document.createAttribute('visible')); + dom.attributes.setNamedItem(document.createAttribute('dialog')); + return { dom }; + }; + }, + parseHTML() { + //console.log('umb-media.parseHTML'); + return [{ tag: 'umb-media' }]; + }, + renderHTML({ HTMLAttributes }) { + //console.log('umb-media.renderHTML'); + return ['umb-media', mergeAttributes(HTMLAttributes)]; + }, + }), + ]; + } + + getToolbarButtons() { + return [ + { + name: 'umb-media', + icon: 'icon-picture', + isActive: (editor?: Editor) => editor?.isActive('umbMediaPicker'), + command: async (editor?: Editor) => { + //console.log('umb-media.command', editor); + + const selection = await this.#openMediaPicker(); + if (!selection || !selection.length) return; + + editor?.chain().focus().insertContent(`${selection}`).run(); + }, + }, + ]; + } + + async #openMediaPicker() { + const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const modalHandler = modalManager?.open(this, UMB_MEDIA_PICKER_MODAL, { + data: { multiple: false }, + value: { selection: [] }, + }); + + if (!modalHandler) return; + + const { selection } = await modalHandler.onSubmit().catch(() => ({ selection: undefined })); + + //console.log('umb-media.selection', selection); + return selection; + } +} diff --git a/src/packages/rte/tiptap/manifests.ts b/src/packages/rte/tiptap/manifests.ts index 1dcd948374..8e6836b5cc 100644 --- a/src/packages/rte/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/manifests.ts @@ -1,4 +1,5 @@ +import { manifests as extensions } from './extensions/manifests.js'; import { manifests as propertyEditors } from './property-editors/manifests.js'; import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; -export const manifests: Array = [...propertyEditors]; +export const manifests: Array = [...extensions, ...propertyEditors]; From e7ccf77f7705321fbddcafa59cda5e2dcda40c24 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 16 Sep 2024 15:26:18 +0100 Subject: [PATCH 023/241] Refactored `umb-input-tiptap` to await loading extensions before initializing the Tiptap editor. --- .../input-tiptap/input-tiptap.element.ts | 63 +++++++++++++------ 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 10d3ee9432..87e7b4e387 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -1,31 +1,60 @@ -import type { UmbTiptapFixedMenuElement } from './tiptap-fixed-menu.element.js'; -import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; -import { css, customElement, html, property, query, state } from '@umbraco-cms/backoffice/external/lit'; +import type { UmbTiptapExtensionBase } from './tiptap-extension.js'; +import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { loadManifestApi } from '@umbraco-cms/backoffice/extension-api'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { Editor, Link, StarterKit, TextAlign, Underline } from '@umbraco-cms/backoffice/external/tiptap'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; +import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import './tiptap-fixed-menu.element.js'; import './tiptap-hover-menu.element.js'; -import { Editor, Link, StarterKit, TextAlign, Underline } from '@umbraco-cms/backoffice/external/tiptap'; -import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; @customElement('umb-input-tiptap') -export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '') { - @query('umb-tiptap-fixed-menu') _fixedMenuElement!: UmbTiptapFixedMenuElement; +export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, '') { + @state() + private _extensions: Array = []; + @property({ attribute: false }) configuration?: UmbPropertyEditorConfigCollection; @state() - _editor!: Editor; + private _editor!: Editor; + + protected override async firstUpdated() { + await Promise.all([await this.#loadExtensions(), await this.#loadEditor()]); + } - protected override firstUpdated(): void { - const editor = this.shadowRoot?.querySelector('#editor'); + async #loadExtensions() { + await new Promise((resolve) => { + this.observe(umbExtensionsRegistry.byType('tiptapExtension'), async (manifests) => { + this._extensions = []; - if (!editor) return; + for (const manifest of manifests) { + if (manifest.api) { + const extension = await loadManifestApi(manifest.api); + if (extension) { + this._extensions.push(new extension(this)); + } + } + } + + this.requestUpdate('_extensions'); + + resolve(); + }); + }); + } + + async #loadEditor() { + const element = this.shadowRoot?.querySelector('#editor'); + if (!element) return; + + const extensions = this._extensions.map((ext) => ext.getExtensions()).flat(); this._editor = new Editor({ - element: editor, + element: element, extensions: [ StarterKit, TextAlign.configure({ @@ -33,6 +62,7 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' }), Link.configure({ openOnClick: false }), Underline, + ...extensions, ], content: this.value.toString(), onUpdate: ({ editor }) => { @@ -42,14 +72,11 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' }); } - protected getFormElement() { - return null; - } - override render() { + if (!this._extensions?.length) return html``; return html` - +
    `; } From 35f30170cd5919e375ed11940241f0fb2fcf4dec Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 16 Sep 2024 15:26:52 +0100 Subject: [PATCH 024/241] Refactored `umb-tiptap-fixed-menu` to load in the toolbar button extensions --- .../input-tiptap/tiptap-fixed-menu.element.ts | 154 +++++++++--------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 598b787211..ab47a21d1a 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -1,29 +1,13 @@ -import { - alignCenter, - alignJustify, - alignLeft, - alignRight, - blockquote, - bold, - bulletList, - code, - heading1, - heading2, - heading3, - horizontalRule, - italic, - link, - orderedList, - strikethrough, - underline, -} from './icons.js'; -import { LitElement, css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; +import * as icons from './icons.js'; +import type { UmbTiptapExtensionBase, UmbTiptapToolbarButton } from './tiptap-extension.js'; +import { css, customElement, html, property, state, when } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; @customElement('umb-tiptap-fixed-menu') -export class UmbTiptapFixedMenuElement extends LitElement { +export class UmbTiptapFixedMenuElement extends UmbLitElement { @state() - actions = [ + actions: Array = [ // TODO: I don't think we need a paragraph button. It's the default state. // { // name: 'paragraph', @@ -31,107 +15,108 @@ export class UmbTiptapFixedMenuElement extends LitElement { // // // `, - // command: () => this.editor?.chain().focus().setParagraph().run(), + // command: (editor) => editor?.chain().focus().setParagraph().run(), // }, { name: 'bold', - icon: bold, - isActive: () => this.editor?.isActive('bold'), - command: () => this.editor?.chain().focus().toggleBold().run(), + icon: icons.bold, + isActive: (editor) => editor?.isActive('bold'), + command: (editor) => editor?.chain().focus().toggleBold().run(), }, { name: 'italic', - icon: italic, - isActive: () => this.editor?.isActive('italic'), - command: () => this.editor?.chain().focus().toggleItalic().run(), + icon: icons.italic, + isActive: (editor) => editor?.isActive('italic'), + command: (editor) => editor?.chain().focus().toggleItalic().run(), }, { name: 'underline', - icon: underline, - isActive: () => this.editor?.isActive('underline'), - command: () => this.editor?.chain().focus().toggleUnderline().run(), + icon: icons.underline, + isActive: (editor) => editor?.isActive('underline'), + command: (editor) => editor?.chain().focus().toggleUnderline().run(), }, { name: 'strikethrough', - icon: strikethrough, - isActive: () => this.editor?.isActive('strike'), - command: () => this.editor?.chain().focus().toggleStrike().run(), + icon: icons.strikethrough, + isActive: (editor) => editor?.isActive('strike'), + command: (editor) => editor?.chain().focus().toggleStrike().run(), }, { name: 'h1', - icon: heading1, - isActive: () => this.editor?.isActive('heading', { level: 1 }), - command: () => this.editor?.chain().focus().toggleHeading({ level: 1 }).run(), + icon: icons.heading1, + isActive: (editor) => editor?.isActive('heading', { level: 1 }), + command: (editor) => editor?.chain().focus().toggleHeading({ level: 1 }).run(), }, { name: 'h2', - icon: heading2, - isActive: () => this.editor?.isActive('heading', { level: 2 }), - command: () => this.editor?.chain().focus().toggleHeading({ level: 2 }).run(), + icon: icons.heading2, + isActive: (editor) => editor?.isActive('heading', { level: 2 }), + command: (editor) => editor?.chain().focus().toggleHeading({ level: 2 }).run(), }, { name: 'h3', - icon: heading3, - isActive: () => this.editor?.isActive('heading', { level: 3 }), - command: () => this.editor?.chain().focus().toggleHeading({ level: 3 }).run(), + icon: icons.heading3, + isActive: (editor) => editor?.isActive('heading', { level: 3 }), + command: (editor) => editor?.chain().focus().toggleHeading({ level: 3 }).run(), }, { name: 'blockquote', - icon: blockquote, - isActive: () => this.editor?.isActive('blockquote'), - command: () => this.editor?.chain().focus().toggleBlockquote().run(), + icon: icons.blockquote, + isActive: (editor) => editor?.isActive('blockquote'), + command: (editor) => editor?.chain().focus().toggleBlockquote().run(), }, { name: 'code', - icon: code, - isActive: () => this.editor?.isActive('codeBlock'), - command: () => this.editor?.chain().focus().toggleCodeBlock().run(), + icon: icons.code, + isActive: (editor) => editor?.isActive('codeBlock'), + command: (editor) => editor?.chain().focus().toggleCodeBlock().run(), }, { name: 'bullet-list', - icon: bulletList, - isActive: () => this.editor?.isActive('bulletList'), - command: () => this.editor?.chain().focus().toggleBulletList().run(), + icon: icons.bulletList, + isActive: (editor) => editor?.isActive('bulletList'), + command: (editor) => editor?.chain().focus().toggleBulletList().run(), }, { name: 'ordered-list', - icon: orderedList, - isActive: () => this.editor?.isActive('orderedList'), - command: () => this.editor?.chain().focus().toggleOrderedList().run(), + icon: icons.orderedList, + isActive: (editor) => editor?.isActive('orderedList'), + command: (editor) => editor?.chain().focus().toggleOrderedList().run(), }, { name: 'horizontal-rule', - icon: horizontalRule, - isActive: () => this.editor?.isActive('horizontalRule'), - command: () => this.editor?.chain().focus().setHorizontalRule().run(), + icon: icons.horizontalRule, + isActive: (editor) => editor?.isActive('horizontalRule'), + command: (editor) => editor?.chain().focus().setHorizontalRule().run(), }, { name: 'align-left', - icon: alignLeft, - isActive: () => this.editor?.isActive({ textAlign: 'left' }), - command: () => this.editor?.chain().focus().setTextAlign('left').run(), + icon: icons.alignLeft, + isActive: (editor) => editor?.isActive({ textAlign: 'left' }), + command: (editor) => editor?.chain().focus().setTextAlign('left').run(), }, { name: 'align-center', - icon: alignCenter, - isActive: () => this.editor?.isActive({ textAlign: 'center' }), - command: () => this.editor?.chain().focus().setTextAlign('center').run(), + icon: icons.alignCenter, + isActive: (editor) => editor?.isActive({ textAlign: 'center' }), + command: (editor) => editor?.chain().focus().setTextAlign('center').run(), }, { name: 'align-right', - icon: alignRight, - isActive: () => this.editor?.isActive({ textAlign: 'right' }), - command: () => this.editor?.chain().focus().setTextAlign('right').run(), + icon: icons.alignRight, + isActive: (editor) => editor?.isActive({ textAlign: 'right' }), + command: (editor) => editor?.chain().focus().setTextAlign('right').run(), }, { name: 'align-justify', - icon: alignJustify, - isActive: () => this.editor?.isActive({ textAlign: 'justify' }), - command: () => this.editor?.chain().focus().setTextAlign('justify').run(), + icon: icons.alignJustify, + isActive: (editor) => editor?.isActive({ textAlign: 'justify' }), + command: (editor) => editor?.chain().focus().setTextAlign('justify').run(), }, { name: 'link', - icon: link, + icon: icons.link, + isActive: (editor) => editor?.isActive('link'), command: () => { const text = prompt('Enter the text'); const url = prompt('Enter the URL'); @@ -151,9 +136,6 @@ export class UmbTiptapFixedMenuElement extends LitElement { ]; @property({ attribute: false }) - get editor() { - return this.#editor; - } set editor(value) { const oldValue = this.#editor; if (value === oldValue) { @@ -163,18 +145,36 @@ export class UmbTiptapFixedMenuElement extends LitElement { this.#editor?.on('selectionUpdate', this.#onUpdate); this.#editor?.on('update', this.#onUpdate); } + get editor() { + return this.#editor; + } #editor?: Editor; + @property({ attribute: false }) + extensions: Array = []; + #onUpdate = () => { this.requestUpdate(); }; + protected override firstUpdated() { + const buttons = this.extensions.flatMap((ext) => ext.getToolbarButtons()); + this.actions.push(...buttons); + } + override render() { return html` ${this.actions.map( (action) => html` - `, )} From 866838beb32db899afb5a4337c89047900e455ff Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 16 Sep 2024 15:27:30 +0100 Subject: [PATCH 025/241] Updated Tiptap RTE property-editor manifest to be more consistent with TinyMCE meta-data --- .../rte/tiptap/property-editors/tiptap/manifests.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 140d8e4b96..ada8881892 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -4,13 +4,13 @@ export const manifests: Array = [ { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.Tiptap', - name: 'Tiptap Property Editor UI', + name: 'Rich Text Editor [Tiptap] Property Editor UI', element: () => import('./property-editor-ui-tiptap.element.js'), meta: { - label: 'Tiptap Editor', - propertyEditorSchemaAlias: 'Umbraco.Plain.Json', - icon: 'icon-document', - group: 'richText', + label: 'Rich Text Editor [Tiptap]', + propertyEditorSchemaAlias: 'Umbraco.RichText', + icon: 'icon-browser-window', + group: 'richContent', }, }, ]; From 8aea280cbdc47d83cfdb03c4516286bae2e79b12 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Mon, 16 Sep 2024 21:07:02 +0200 Subject: [PATCH 026/241] WIP config manifest --- .../input-tiptap/input-tiptap.element.ts | 19 ++- .../property-editors/tiptap/manifests.ts | 116 ++++++++++++++++++ 2 files changed, 131 insertions(+), 4 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 10d3ee9432..9c8ec93f70 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -24,6 +24,16 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' if (!editor) return; + const toolbar = this.configuration?.getValueByAlias('toolbar'); + const maxWidth = this.configuration?.getValueByAlias('maxWidth'); + const maxHeight = this.configuration?.getValueByAlias('maxHeight'); + const mode = this.configuration?.getValueByAlias('mode'); + + this.setAttribute('style', `max-width: ${maxWidth}px;`); + editor.setAttribute('style', `max-height: ${maxHeight}px;`); + + if (!editor) return; + this._editor = new Editor({ element: editor, extensions: [ @@ -48,7 +58,6 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' override render() { return html` -
    `; @@ -56,18 +65,20 @@ export class UmbInputTiptapElement extends UUIFormControlMixin(UmbLitElement, '' static override styles = [ css` + :host { + display: block; + } #editor { + overflow: auto; border-radius: var(--uui-border-radius); border: 1px solid var(--uui-color-border); + padding: 1rem; border-top-left-radius: 0; border-top-right-radius: 0; border-top: 0; - margin: 0 auto; box-sizing: border-box; height: 100%; width: 100%; - padding: 1rem; - overflow: clip; min-height: 400px; display: grid; /* Don't ask me why this is needed, but it is. */ } diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 140d8e4b96..d3bdd9e2db 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -11,6 +11,122 @@ export const manifests: Array = [ propertyEditorSchemaAlias: 'Umbraco.Plain.Json', icon: 'icon-document', group: 'richText', + settings: { + properties: [ + { + alias: 'toolbar', + label: 'Toolbar - NOT IMPLEMENTED', + description: 'Pick the toolbar options that should be available when editing', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.Tiptap.ToolbarConfiguration', + weight: 10, + config: [ + { + alias: 'toolbar', + value: [ + // Clipboard Group + { alias: 'undo', label: 'Undo', icon: 'undo', group: 'clipboard' }, + { alias: 'redo', label: 'Redo', icon: 'redo', group: 'clipboard' }, + { alias: 'cut', label: 'Cut', icon: 'cut', group: 'clipboard' }, + { alias: 'copy', label: 'Copy', icon: 'copy', group: 'clipboard' }, + { alias: 'paste', label: 'Paste', icon: 'paste', group: 'clipboard' }, + + // Formatting Group + { alias: 'bold', label: 'Bold', icon: 'bold', group: 'formatting' }, + { alias: 'italic', label: 'Italic', icon: 'italic', group: 'formatting' }, + { alias: 'underline', label: 'Underline', icon: 'underline', group: 'formatting' }, + { alias: 'strikethrough', label: 'Strikethrough', icon: 'strike-through', group: 'formatting' }, + { alias: 'removeformat', label: 'Remove format', icon: 'remove-formatting', group: 'formatting' }, + + // Color Group + { alias: 'forecolor', label: 'Text color', icon: 'text-color', group: 'color' }, + { alias: 'backcolor', label: 'Background color', icon: 'highlight-bg-color', group: 'color' }, + + // Alignment Group + { alias: 'alignleft', label: 'Align left', icon: 'align-left', group: 'alignment' }, + { alias: 'aligncenter', label: 'Align center', icon: 'align-center', group: 'alignment' }, + { alias: 'alignright', label: 'Align right', icon: 'align-right', group: 'alignment' }, + { alias: 'alignjustify', label: 'Justify justify', icon: 'align-justify', group: 'alignment' }, + + // List Group + { alias: 'bullist', label: 'Bullet list', icon: 'unordered-list', group: 'list' }, + { alias: 'numlist', label: 'Numbered list', icon: 'ordered-list', group: 'list' }, + + // Indentation Group + { alias: 'outdent', label: 'Outdent', icon: 'outdent', group: 'indentation' }, + { alias: 'indent', label: 'Indent', icon: 'indent', group: 'indentation' }, + + // Insert Elements Group + { alias: 'anchor', label: 'Anchor', icon: 'bookmark', group: 'insert' }, + { alias: 'table', label: 'Table', icon: 'table', group: 'insert' }, + { alias: 'hr', label: 'Horizontal rule', icon: 'horizontal-rule', group: 'insert' }, + { alias: 'charmap', label: 'Character map', icon: 'insert-character', group: 'insert' }, + + // Direction Group + { alias: 'rtl', label: 'Right to left', icon: 'rtl', group: 'direction' }, + { alias: 'ltr', label: 'Left to right', icon: 'ltr', group: 'direction' }, + + // Text Transformation Group + { alias: 'subscript', label: 'Subscript', icon: 'subscript', group: 'text-transformation' }, + { alias: 'superscript', label: 'Superscript', icon: 'superscript', group: 'text-transformation' }, + + // Styling and Font Group + { alias: 'styles', label: 'Style select', icon: 'permanent-pen', group: 'styling' }, + { alias: 'fontname', label: 'Font select', icon: 'text-color', group: 'styling' }, + { alias: 'fontsize', label: 'Font size', icon: 'text-color', group: 'styling' }, + + // Block Element Group + { alias: 'blockquote', label: 'Blockquote', icon: 'quote', group: 'block-elements' }, + { alias: 'formatblock', label: 'Format block', icon: 'format', group: 'block-elements' }, + ], + }, + ], + }, + { + alias: 'maxWidth', + label: 'MaxWidth', + description: 'Editor max width', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.Integer', + weight: 20, + }, + { + alias: 'maxHeight', + label: 'MaxHeight', + description: 'Editor max height', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.Integer', + weight: 30, + }, + { + alias: 'mode', + label: 'Mode - NOT IMPLEMENTED', + description: 'Select the mode for the editor', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.RadioButtonList', + config: [{ alias: 'items', value: ['Classic', 'Inline'] }], + weight: 50, + }, + ], + defaultData: [ + { + alias: 'toolbar', + value: [ + 'styles', + 'bold', + 'italic', + 'alignleft', + 'aligncenter', + 'alignright', + 'bullist', + 'numlist', + 'outdent', + 'indent', + 'sourcecode', + 'link', + ], + }, + { alias: 'mode', value: 'Classic' }, + { alias: 'maxWidth', value: 800 }, + { alias: 'maxHeight', value: 500 }, + ], + }, }, }, ]; From 72b59262228a87540b08610665052f40af5661b1 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Mon, 16 Sep 2024 21:42:14 +0200 Subject: [PATCH 027/241] custom toolbar config --- .../rte/tiptap/property-editors/manifests.ts | 15 +- ...ui-tiptap-toolbar-configuration.element.ts | 149 ++++++++++++++++++ 2 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts diff --git a/src/packages/rte/tiptap/property-editors/manifests.ts b/src/packages/rte/tiptap/property-editors/manifests.ts index 1a38fe7fe9..b9b660577c 100644 --- a/src/packages/rte/tiptap/property-editors/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/manifests.ts @@ -1,4 +1,17 @@ import { manifests as tiptapManifests } from './tiptap/manifests.js'; import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; -export const manifests: Array = [...tiptapManifests]; +export const manifests: Array = [ + ...tiptapManifests, + { + type: 'propertyEditorUi', + alias: 'Umb.PropertyEditorUi.Tiptap.ToolbarConfiguration', + name: 'Tiptap Toolbar Property Editor UI', + js: () => import('./property-editor-ui-tiptap-toolbar-configuration.element.js'), + meta: { + label: 'Tiptap Toolbar Configuration', + icon: 'icon-autofill', + group: 'common', + }, + }, +]; diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts new file mode 100644 index 0000000000..6524700bb1 --- /dev/null +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -0,0 +1,149 @@ +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; +import { customElement, css, html, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs'; +import { + UmbPropertyValueChangeEvent, + type UmbPropertyEditorConfigCollection, +} from '@umbraco-cms/backoffice/property-editor'; +import { tinymce } from '@umbraco-cms/backoffice/external/tinymce'; + +const tinyIconSet = tinymce.IconManager.get('default'); + +type ToolbarConfig = { + alias: string; + label: string; + icon?: string; + selected: boolean; +}; + +/** + * @element umb-property-editor-ui-tiptap-toolbar-configuration + */ +@customElement('umb-property-editor-ui-tiptap-toolbar-configuration') +export class UmbPropertyEditorUiTiptapToolbarConfigurationElement + extends UmbLitElement + implements UmbPropertyEditorUiElement +{ + @property({ attribute: false }) + set value(value: string | string[] | null) { + if (!value) return; + + if (typeof value === 'string') { + this.#selectedValues = value.split(',').filter((x) => x.length > 0); + } else if (Array.isArray(value)) { + this.#selectedValues = value; + } else { + this.#selectedValues = []; + return; + } + + // Migrations + if (this.#selectedValues.includes('ace')) { + this.#selectedValues = this.#selectedValues.filter((v) => v !== 'ace'); + this.#selectedValues.push('sourcecode'); + } + + this._toolbarConfig.forEach((v) => { + v.selected = this.#selectedValues.includes(v.alias); + }); + } + get value(): string[] { + return this.#selectedValues; + } + + @property({ attribute: false }) + config?: UmbPropertyEditorConfigCollection; + + @state() + private _toolbarConfig: ToolbarConfig[] = []; + + #selectedValues: string[] = []; + + protected override async firstUpdated(_changedProperties: PropertyValueMap) { + super.firstUpdated(_changedProperties); + + this.config?.getValueByAlias('toolbar')?.forEach((v) => { + this._toolbarConfig.push({ + ...v, + selected: this.value.includes(v.alias), + }); + }); + + await this.getToolbarPlugins(); + + this.requestUpdate('_toolbarConfig'); + } + + private async getToolbarPlugins(): Promise { + // Get all the toolbar plugins + const plugin$ = umbExtensionsRegistry.byType('tinyMcePlugin'); + + const plugins = await firstValueFrom(plugin$); + + plugins.forEach((p) => { + // If the plugin has a toolbar, add it to the config + if (p.meta?.toolbar) { + p.meta.toolbar.forEach((t: any) => { + this._toolbarConfig.push({ + alias: t.alias, + label: this.localize.string(t.label), + icon: t.icon ?? 'icon-autofill', + selected: this.value.includes(t.alias), + }); + }); + } + }); + } + + private onChange(event: CustomEvent) { + const checkbox = event.target as HTMLInputElement; + const alias = checkbox.value; + + const value = this._toolbarConfig + .filter((t) => (t.alias !== alias && t.selected) || (t.alias === alias && checkbox.checked)) + .map((v) => v.alias); + + this.value = value; + + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + } + + override render() { + return html`
      + ${repeat( + this._toolbarConfig, + (v) => v.alias, + (v) => + html`
    • + + + ${v.label} + +
    • `, + )} +
    `; + } + + static override styles = [ + UmbTextStyles, + css` + ul { + list-style: none; + padding: 0; + margin: 0; + } + `, + ]; +} + +export default UmbPropertyEditorUiTiptapToolbarConfigurationElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-property-editor-ui-tiptap-toolbar-configuration': UmbPropertyEditorUiTiptapToolbarConfigurationElement; + } +} From dcdcf4d7de88ee69dbf5a9af3d955480be4f549e Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 17 Sep 2024 10:05:24 +0200 Subject: [PATCH 028/241] todo --- .../tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index ab47a21d1a..da000da655 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -144,6 +144,7 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { this.#editor = value; this.#editor?.on('selectionUpdate', this.#onUpdate); this.#editor?.on('update', this.#onUpdate); + // todo add listener for commands } get editor() { return this.#editor; From c8c8b96b5567b3e7d742ffc3bf43946cab492296 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 17 Sep 2024 10:22:58 +0200 Subject: [PATCH 029/241] remove starterkit and add required extensions --- .vscode/settings.json | 2 + package-lock.json | 235 +++--------------- package.json | 8 +- src/external/tiptap/index.ts | 10 +- .../input-tiptap/input-tiptap.element.ts | 24 +- 5 files changed, 78 insertions(+), 201 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f4ca096129..bc10a8c132 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,7 +6,9 @@ "combobox", "ctrls", "devs", + "Dropcursor", "Elementable", + "Gapcursor", "iframes", "invariantable", "lucide", diff --git a/package-lock.json b/package-lock.json index 20b45d5a49..fdd671ea90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,11 +13,17 @@ ], "dependencies": { "@tiptap/core": "^2.6.6", + "@tiptap/extension-document": "^2.7.0", + "@tiptap/extension-dropcursor": "^2.7.0", + "@tiptap/extension-gapcursor": "^2.7.0", + "@tiptap/extension-hard-break": "^2.7.0", + "@tiptap/extension-history": "^2.7.0", "@tiptap/extension-link": "^2.6.6", + "@tiptap/extension-paragraph": "^2.7.0", + "@tiptap/extension-text": "^2.7.0", "@tiptap/extension-text-align": "^2.6.6", "@tiptap/extension-underline": "^2.6.6", "@tiptap/pm": "^2.6.6", - "@tiptap/starter-kit": "^2.6.6", "@types/diff": "^5.2.1", "@types/dompurify": "^3.0.5", "@types/uuid": "^10.0.0", @@ -6476,165 +6482,67 @@ "@tiptap/pm": "^2.6.6" } }, - "node_modules/@tiptap/extension-blockquote": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.6.6.tgz", - "integrity": "sha512-hAdsNlMfzzxld154hJqPqtWqO5i4/7HoDfuxmyqBxdMJ+e2UMaIGBGwoLRXG0V9UoRwJusjqlpyD7pIorxNlgA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.6.6" - } - }, - "node_modules/@tiptap/extension-bold": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.6.6.tgz", - "integrity": "sha512-CD6gBhdQtCoqYSmx8oAV8gvKtVOGZSyyvuNYo7by9eZ56DqLYnd7kbUj0RH7o9Ymf/iJTOUJ6XcvrsWwo4lubg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.6.6" - } - }, - "node_modules/@tiptap/extension-bullet-list": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.6.6.tgz", - "integrity": "sha512-WEKxbVSYuvmX2wkHWP8HXk5nzA7stYwtdaubwWH/R17kGI3IGScJuMQ9sEN82uzJU8bfgL9yCbH2bY8Fj/Q4Ow==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.6.6" - } - }, - "node_modules/@tiptap/extension-code": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.6.6.tgz", - "integrity": "sha512-JrEFKsZiLvfvOFhOnnrpA0TzCuJjDeysfbMeuKUZNV4+DhYOL28d39H1++rEtJAX0LcbBU60oC5/PrlU9SpvRQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.6.6" - } - }, - "node_modules/@tiptap/extension-code-block": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.6.6.tgz", - "integrity": "sha512-1YLp/zHMHSkE2xzht8nPR6T4sQJJ3ket798czxWuQEbetFv/l0U/mpiPpYSLObj6oTAoqYZ0kWXZj5eQSpPB8Q==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.6.6", - "@tiptap/pm": "^2.6.6" - } - }, "node_modules/@tiptap/extension-document": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.6.6.tgz", - "integrity": "sha512-6qlH5VWzLHHRVeeciRC6C4ZHpMsAGPNG16EF53z0GeMSaaFD/zU3B239QlmqXmLsAl8bpf8Bn93N0t2ABUvScw==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.7.0.tgz", + "integrity": "sha512-F00nBhp+IM/vJVr0G7iMcaanVGhYfTaF1kafxE6PYnKV4d4BDJeLq5OvPzJHaP3P1frqIEci7trUW1MqQANSjQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.6.6" + "@tiptap/core": "^2.7.0-pre.0" } }, "node_modules/@tiptap/extension-dropcursor": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.6.6.tgz", - "integrity": "sha512-O6CeKriA9uyHsg7Ui4z5ZjEWXQxrIL+1zDekffW0wenGC3G4LUsCzAiFS4LSrR9a3u7tnwqGApW10rdkmCGF4w==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.7.0.tgz", + "integrity": "sha512-sbeio2DlPdm0XRyqpJ9qv0Eg3MhWucqBH4olrasPtLxnxY9S2NX7ztKhk/dkXnG45ioq2HlgbLp/ZxbF8cYjfA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.6.6", - "@tiptap/pm": "^2.6.6" + "@tiptap/core": "^2.7.0-pre.0", + "@tiptap/pm": "^2.7.0-pre.0" } }, "node_modules/@tiptap/extension-gapcursor": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.6.6.tgz", - "integrity": "sha512-O2lQ2t0X0Vsbn3yLWxFFHrXY6C2N9Y6ZF/M7LWzpcDTUZeWuhoNkFE/1yOM0h6ZX1DO2A9hNIrKpi5Ny8yx+QA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.7.0.tgz", + "integrity": "sha512-sVf5wGXkhQIyU+qhjI79Ms6OkEjb6/1VLmTWVvE/5l1+TT4r7/PhcJdEX0XaePNabH3ArZNOgBbhkjw7HJvqyw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.6.6", - "@tiptap/pm": "^2.6.6" + "@tiptap/core": "^2.7.0-pre.0", + "@tiptap/pm": "^2.7.0-pre.0" } }, "node_modules/@tiptap/extension-hard-break": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.6.6.tgz", - "integrity": "sha512-bsUuyYBrMDEiudx1dOQSr9MzKv13m0xHWrOK+DYxuIDYJb5g+c9un5cK7Js+et/HEYYSPOoH/iTW6h+4I5YeUg==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.7.0.tgz", + "integrity": "sha512-IHzba0lWf+GU+9GToWi223aY8j/CSrg1mdNb2DvljP224a5MiE3aReT6E3ZfaxONhkrq93Q6PRlC9PUwLdyJdQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.6.6" - } - }, - "node_modules/@tiptap/extension-heading": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.6.6.tgz", - "integrity": "sha512-bgx9vptVFi5yFkIw1OI53J7+xJ71Or3SOe/Q8eSpZv53DlaKpL/TzKw8Z54t1PrI2rJ6H9vrLtkvixJvBZH1Ug==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.6.6" + "@tiptap/core": "^2.7.0-pre.0" } }, "node_modules/@tiptap/extension-history": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.6.6.tgz", - "integrity": "sha512-tPTzAmPGqMX5Bd5H8lzRpmsaMvB9DvI5Dy2za/VQuFtxgXmDiFVgHRkRXIuluSkPTuANu84XBOQ0cBijqY8x4w==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.6.6", - "@tiptap/pm": "^2.6.6" - } - }, - "node_modules/@tiptap/extension-horizontal-rule": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.6.6.tgz", - "integrity": "sha512-cFEfv7euDpuLSe8exY8buwxkreKBAZY9Hn3EetKhPcLQo+ut5Y24chZTxFyf9b+Y0wz3UhOhLTZSz7fTobLqBA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.6.6", - "@tiptap/pm": "^2.6.6" - } - }, - "node_modules/@tiptap/extension-italic": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.6.6.tgz", - "integrity": "sha512-t7ZPsXqa8nJZZ/6D0rQyZ/KsvzLaSihC6hBTjUQ77CeDGV9PhDWjIcBW4OrvwraJDBd12ETBeQ2CkULJOgH+lQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.7.0.tgz", + "integrity": "sha512-33GtG+SIHi6c8briilU2OZ7pt7W8XoNscXokJsXqLlGZDC7mlLs6N9OMQ7qNbcycz7uxRqi5k7jWpxwxRTQiKQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.6.6" + "@tiptap/core": "^2.7.0-pre.0", + "@tiptap/pm": "^2.7.0-pre.0" } }, "node_modules/@tiptap/extension-link": { @@ -6653,64 +6561,28 @@ "@tiptap/pm": "^2.6.6" } }, - "node_modules/@tiptap/extension-list-item": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.6.6.tgz", - "integrity": "sha512-k+oEzZu2cgVKqPqOP1HzASOKLpTEV9m7mRVPAbuaaX8mSyvIgD6f+JUx9PvgYv//D918wk98LMoRBFX53tDJ4w==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.6.6" - } - }, - "node_modules/@tiptap/extension-ordered-list": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.6.6.tgz", - "integrity": "sha512-AJwyfLXIi7iUGnK5twJbwdVVpQyh7fU6OK75h1AwDztzsOcoPcxtffDlZvUOd4ZtwuyhkzYqVkeI0f+abTWZTw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.6.6" - } - }, "node_modules/@tiptap/extension-paragraph": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.6.6.tgz", - "integrity": "sha512-fD/onCr16UQWx+/xEmuFC2MccZZ7J5u4YaENh8LMnAnBXf78iwU7CAcmuc9rfAEO3qiLoYGXgLKiHlh2ZfD4wA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - }, - "peerDependencies": { - "@tiptap/core": "^2.6.6" - } - }, - "node_modules/@tiptap/extension-strike": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.6.6.tgz", - "integrity": "sha512-Ze8KhGk+wzSJSJRl5fbhTI6AvPu2LmcHYeO3pMEH8u4gV5WTXfmKJVStEIAzkoqvwEQVWzXvy8nDgsFQHiojPg==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.7.0.tgz", + "integrity": "sha512-yCZ9srptAzZIragP0Evu6hvgUbhezYkhvlCU5w4Ecpp9FMU5FwjN1NnkkxnqSrp90LjsMRLFgUklybOfZ8EVQA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.6.6" + "@tiptap/core": "^2.7.0-pre.0" } }, "node_modules/@tiptap/extension-text": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.6.6.tgz", - "integrity": "sha512-e84uILnRzNzcwK1DVQNpXVmBG1Cq3BJipTOIDl1LHifOok7MBjhI/X+/NR0bd3N2t6gmDTWi63+4GuJ5EeDmsg==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.7.0.tgz", + "integrity": "sha512-v2Wh9XvLpBGWqPMD19BI4y8hGqinGNTnGRMph2NXDkx+aG/42pEktd2KCgouE/La8nusj1FyWurQ1hK5XUBMOw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.6.6" + "@tiptap/core": "^2.7.0-pre.0" } }, "node_modules/@tiptap/extension-text-align": { @@ -6766,37 +6638,6 @@ "url": "https://github.com/sponsors/ueberdosis" } }, - "node_modules/@tiptap/starter-kit": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.6.6.tgz", - "integrity": "sha512-zb9xIg3WjG9AsJoyWrfqx5SL9WH7/HTdkB79jFpWtOF/Kaigo7fHFmhs2FsXtJMJlcdMTO2xeRuCYHt5ozXlhg==", - "dependencies": { - "@tiptap/core": "^2.6.6", - "@tiptap/extension-blockquote": "^2.6.6", - "@tiptap/extension-bold": "^2.6.6", - "@tiptap/extension-bullet-list": "^2.6.6", - "@tiptap/extension-code": "^2.6.6", - "@tiptap/extension-code-block": "^2.6.6", - "@tiptap/extension-document": "^2.6.6", - "@tiptap/extension-dropcursor": "^2.6.6", - "@tiptap/extension-gapcursor": "^2.6.6", - "@tiptap/extension-hard-break": "^2.6.6", - "@tiptap/extension-heading": "^2.6.6", - "@tiptap/extension-history": "^2.6.6", - "@tiptap/extension-horizontal-rule": "^2.6.6", - "@tiptap/extension-italic": "^2.6.6", - "@tiptap/extension-list-item": "^2.6.6", - "@tiptap/extension-ordered-list": "^2.6.6", - "@tiptap/extension-paragraph": "^2.6.6", - "@tiptap/extension-strike": "^2.6.6", - "@tiptap/extension-text": "^2.6.6", - "@tiptap/pm": "^2.6.6" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/ueberdosis" - } - }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", diff --git a/package.json b/package.json index 4034bcc87c..ccc67189dd 100644 --- a/package.json +++ b/package.json @@ -190,11 +190,17 @@ }, "dependencies": { "@tiptap/core": "^2.6.6", + "@tiptap/extension-document": "^2.7.0", + "@tiptap/extension-dropcursor": "^2.7.0", + "@tiptap/extension-gapcursor": "^2.7.0", + "@tiptap/extension-hard-break": "^2.7.0", + "@tiptap/extension-history": "^2.7.0", "@tiptap/extension-link": "^2.6.6", + "@tiptap/extension-paragraph": "^2.7.0", + "@tiptap/extension-text": "^2.7.0", "@tiptap/extension-text-align": "^2.6.6", "@tiptap/extension-underline": "^2.6.6", "@tiptap/pm": "^2.6.6", - "@tiptap/starter-kit": "^2.6.6", "@types/diff": "^5.2.1", "@types/dompurify": "^3.0.5", "@types/uuid": "^10.0.0", diff --git a/src/external/tiptap/index.ts b/src/external/tiptap/index.ts index 9eae18bcdd..fcd28e8cf6 100644 --- a/src/external/tiptap/index.ts +++ b/src/external/tiptap/index.ts @@ -1,5 +1,13 @@ +// REQUIRED EXTENSIONS START export * from '@tiptap/core'; -export * from '@tiptap/starter-kit'; +export * from '@tiptap/extension-document'; +export * from '@tiptap/extension-dropcursor'; +export * from '@tiptap/extension-gapcursor'; +export * from '@tiptap/extension-hard-break'; +export * from '@tiptap/extension-history'; +export * from '@tiptap/extension-paragraph'; +export * from '@tiptap/extension-text'; +// REQUIRED EXTENSIONS END export * from '@tiptap/extension-underline'; export * from '@tiptap/extension-text-align'; export * from '@tiptap/extension-link'; diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 87e7b4e387..c766a512ef 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -2,7 +2,19 @@ import type { UmbTiptapExtensionBase } from './tiptap-extension.js'; import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import { loadManifestApi } from '@umbraco-cms/backoffice/extension-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; -import { Editor, Link, StarterKit, TextAlign, Underline } from '@umbraco-cms/backoffice/external/tiptap'; +import { + Document, + Dropcursor, + Editor, + Gapcursor, + HardBreak, + History, + Link, + Paragraph, + Text, + TextAlign, + Underline, +} from '@umbraco-cms/backoffice/external/tiptap'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; @@ -56,7 +68,15 @@ export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, '' this._editor = new Editor({ element: element, extensions: [ - StarterKit, + // REQUIRED EXTENSIONS START + Document, + Dropcursor, + Gapcursor, + HardBreak, + History, + Paragraph, + Text, + // REQUIRED EXTENSIONS END TextAlign.configure({ types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], }), From f1df8cbb793684e9c3d30843335f16569d25cae1 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 17 Sep 2024 10:36:04 +0200 Subject: [PATCH 030/241] add rest of starterkit --- package-lock.json | 145 ++++++++++++++++++ package.json | 11 ++ src/external/tiptap/index.ts | 31 ++-- .../input-tiptap/input-tiptap.element.ts | 22 ++- 4 files changed, 198 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index fdd671ea90..40a4bafd3d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,13 +13,24 @@ ], "dependencies": { "@tiptap/core": "^2.6.6", + "@tiptap/extension-blockquote": "^2.7.0", + "@tiptap/extension-bold": "^2.7.0", + "@tiptap/extension-bullet-list": "^2.7.0", + "@tiptap/extension-code": "^2.7.0", + "@tiptap/extension-code-block": "^2.7.0", "@tiptap/extension-document": "^2.7.0", "@tiptap/extension-dropcursor": "^2.7.0", "@tiptap/extension-gapcursor": "^2.7.0", "@tiptap/extension-hard-break": "^2.7.0", + "@tiptap/extension-heading": "^2.7.0", "@tiptap/extension-history": "^2.7.0", + "@tiptap/extension-horizontal-rule": "^2.7.0", + "@tiptap/extension-italic": "^2.7.0", "@tiptap/extension-link": "^2.6.6", + "@tiptap/extension-list-item": "^2.7.0", + "@tiptap/extension-ordered-list": "^2.7.0", "@tiptap/extension-paragraph": "^2.7.0", + "@tiptap/extension-strike": "^2.7.0", "@tiptap/extension-text": "^2.7.0", "@tiptap/extension-text-align": "^2.6.6", "@tiptap/extension-underline": "^2.6.6", @@ -6482,6 +6493,67 @@ "@tiptap/pm": "^2.6.6" } }, + "node_modules/@tiptap/extension-blockquote": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.7.0.tgz", + "integrity": "sha512-cAJsQ2SrwiFMazZyG+CgG3Ljdc1DjFTGQyZxV/7smfO/pS/x8OgOStL1gJFBTbDbiR8sZVuJEV8m3h+95s3Rkw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0-pre.0" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.7.0.tgz", + "integrity": "sha512-/BCdlVHmYm1HmpxjUZ5Ba6+H23mmG3aDT2lg4x/DdPVEEudQkUEfHCiIfu+hFiBWqJBt43QmiR3Tt5hr/wfDvw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0-pre.0" + } + }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.7.0.tgz", + "integrity": "sha512-wQy0EXM/Do5bjOMWUBAEBJiOAhAm038skPYCL7Glu5vV9IOr9aZ0uEBl324U8y/AB1HsFqAI1F+xqD+6XwzorQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0-pre.0" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.7.0.tgz", + "integrity": "sha512-QQpHDv5r9proQp0sMK8hR+sY+jCbXKdEHkle23ZESXP/k75eBJNIkkHUCOAJpYtkZdGjlQ1AjW9H/L513IE8cw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0-pre.0" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.7.0.tgz", + "integrity": "sha512-SDU+ZITxZfD3fsValCPnU+VwMrEmL0SedvuIqRTIWBd6qxwJD4suRcALYYFAmgmGXS794kjVLb8ONAw/69Svqw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0-pre.0", + "@tiptap/pm": "^2.7.0-pre.0" + } + }, "node_modules/@tiptap/extension-document": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.7.0.tgz", @@ -6532,6 +6604,18 @@ "@tiptap/core": "^2.7.0-pre.0" } }, + "node_modules/@tiptap/extension-heading": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.7.0.tgz", + "integrity": "sha512-J5jERidjtntZh4Du7n9PbMoD3ibiWjQH8Kc9AINOzK/bLBGIsDN15reOVGyto+ZYhTtU2pe7fWAjJYp+zCIcKQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0-pre.0" + } + }, "node_modules/@tiptap/extension-history": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.7.0.tgz", @@ -6545,6 +6629,31 @@ "@tiptap/pm": "^2.7.0-pre.0" } }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.7.0.tgz", + "integrity": "sha512-15N8+OxJa7yGN8PX5odBTCAjqwsDoQOBe/WWWy0viGdWSl0uBcThfm99YV+C72Qtv+PM4+2gkXbl6FEw5ZkwVg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0-pre.0", + "@tiptap/pm": "^2.7.0-pre.0" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.7.0.tgz", + "integrity": "sha512-2qnX7e17Ppb4/R+ZlhNegse9NAcnlKVKa6izqqX7LNCm5Uf27PO0vxg7E2X6BTSyx+gsd9bBQRGNteVE01UdHQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0-pre.0" + } + }, "node_modules/@tiptap/extension-link": { "version": "2.6.6", "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.6.6.tgz", @@ -6561,6 +6670,30 @@ "@tiptap/pm": "^2.6.6" } }, + "node_modules/@tiptap/extension-list-item": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.7.0.tgz", + "integrity": "sha512-DbkijEOj7xl6T5NOTRw4GJoBTIwDdgBlSRMCliYJeKLFU/1TBIsi3MDdK08He/owdBnoF3qfMaKqvfi0HflwbQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0-pre.0" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.7.0.tgz", + "integrity": "sha512-jkac6lGvazqZFsIg1kLdR+5UdZRoaOA4v2+s8LXOxdZ0dr/hVuKPKcVoqnS7EJf4bLJ7ie1q8wV+59lQjpmadA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0-pre.0" + } + }, "node_modules/@tiptap/extension-paragraph": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.7.0.tgz", @@ -6573,6 +6706,18 @@ "@tiptap/core": "^2.7.0-pre.0" } }, + "node_modules/@tiptap/extension-strike": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.7.0.tgz", + "integrity": "sha512-um+QCPYXQIcFMIR5mZgey3DbAmH1L+38+lwD5CHF7Xu3f9qkoHr+zGBVyEwvIi6VBo3ghMiGmDjm0EZZAlL0VA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0-pre.0" + } + }, "node_modules/@tiptap/extension-text": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.7.0.tgz", diff --git a/package.json b/package.json index ccc67189dd..3b6ffcf294 100644 --- a/package.json +++ b/package.json @@ -190,13 +190,24 @@ }, "dependencies": { "@tiptap/core": "^2.6.6", + "@tiptap/extension-blockquote": "^2.7.0", + "@tiptap/extension-bold": "^2.7.0", + "@tiptap/extension-bullet-list": "^2.7.0", + "@tiptap/extension-code": "^2.7.0", + "@tiptap/extension-code-block": "^2.7.0", "@tiptap/extension-document": "^2.7.0", "@tiptap/extension-dropcursor": "^2.7.0", "@tiptap/extension-gapcursor": "^2.7.0", "@tiptap/extension-hard-break": "^2.7.0", + "@tiptap/extension-heading": "^2.7.0", "@tiptap/extension-history": "^2.7.0", + "@tiptap/extension-horizontal-rule": "^2.7.0", + "@tiptap/extension-italic": "^2.7.0", "@tiptap/extension-link": "^2.6.6", + "@tiptap/extension-list-item": "^2.7.0", + "@tiptap/extension-ordered-list": "^2.7.0", "@tiptap/extension-paragraph": "^2.7.0", + "@tiptap/extension-strike": "^2.7.0", "@tiptap/extension-text": "^2.7.0", "@tiptap/extension-text-align": "^2.6.6", "@tiptap/extension-underline": "^2.6.6", diff --git a/src/external/tiptap/index.ts b/src/external/tiptap/index.ts index fcd28e8cf6..1e3e36e59d 100644 --- a/src/external/tiptap/index.ts +++ b/src/external/tiptap/index.ts @@ -1,13 +1,24 @@ // REQUIRED EXTENSIONS START export * from '@tiptap/core'; -export * from '@tiptap/extension-document'; -export * from '@tiptap/extension-dropcursor'; -export * from '@tiptap/extension-gapcursor'; -export * from '@tiptap/extension-hard-break'; -export * from '@tiptap/extension-history'; -export * from '@tiptap/extension-paragraph'; -export * from '@tiptap/extension-text'; +export { Document } from '@tiptap/extension-document'; +export { Dropcursor } from '@tiptap/extension-dropcursor'; +export { Gapcursor } from '@tiptap/extension-gapcursor'; +export { HardBreak } from '@tiptap/extension-hard-break'; +export { History } from '@tiptap/extension-history'; +export { Paragraph } from '@tiptap/extension-paragraph'; +export { Text } from '@tiptap/extension-text'; // REQUIRED EXTENSIONS END -export * from '@tiptap/extension-underline'; -export * from '@tiptap/extension-text-align'; -export * from '@tiptap/extension-link'; +export { Blockquote } from '@tiptap/extension-blockquote'; +export { Bold } from '@tiptap/extension-bold'; +export { BulletList } from '@tiptap/extension-bullet-list'; +export { Code } from '@tiptap/extension-code'; +export { CodeBlock } from '@tiptap/extension-code-block'; +export { Heading } from '@tiptap/extension-heading'; +export { HorizontalRule } from '@tiptap/extension-horizontal-rule'; +export { Italic } from '@tiptap/extension-italic'; +export { Link } from '@tiptap/extension-link'; +export { ListItem } from '@tiptap/extension-list-item'; +export { OrderedList } from '@tiptap/extension-ordered-list'; +export { Strike } from '@tiptap/extension-strike'; +export { TextAlign } from '@tiptap/extension-text-align'; +export { Underline } from '@tiptap/extension-underline'; diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index c766a512ef..aa453dd069 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -3,14 +3,24 @@ import { css, customElement, html, property, state } from '@umbraco-cms/backoffi import { loadManifestApi } from '@umbraco-cms/backoffice/extension-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { + Blockquote, + Bold, + BulletList, + Code, + CodeBlock, Document, Dropcursor, Editor, Gapcursor, HardBreak, History, + HorizontalRule, + Italic, Link, + ListItem, + OrderedList, Paragraph, + Strike, Text, TextAlign, Underline, @@ -77,10 +87,20 @@ export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, '' Paragraph, Text, // REQUIRED EXTENSIONS END + Blockquote, + Bold, + BulletList, + Code, + CodeBlock, + HorizontalRule, + Italic, + Link.configure({ openOnClick: false }), + ListItem, // This is needed for BulletList and OrderedList. When moving to an umbraco-extension, how should we handle shared extensions? + OrderedList, + Strike, TextAlign.configure({ types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], }), - Link.configure({ openOnClick: false }), Underline, ...extensions, ], From 68d975656d520525803904a69167983845f913ff Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 17 Sep 2024 10:37:23 +0200 Subject: [PATCH 031/241] add image extension --- package-lock.json | 13 +++++++++++++ package.json | 1 + src/external/tiptap/index.ts | 1 + .../components/input-tiptap/input-tiptap.element.ts | 2 ++ 4 files changed, 17 insertions(+) diff --git a/package-lock.json b/package-lock.json index 40a4bafd3d..df7c63d807 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "@tiptap/extension-heading": "^2.7.0", "@tiptap/extension-history": "^2.7.0", "@tiptap/extension-horizontal-rule": "^2.7.0", + "@tiptap/extension-image": "^2.7.0", "@tiptap/extension-italic": "^2.7.0", "@tiptap/extension-link": "^2.6.6", "@tiptap/extension-list-item": "^2.7.0", @@ -6642,6 +6643,18 @@ "@tiptap/pm": "^2.7.0-pre.0" } }, + "node_modules/@tiptap/extension-image": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.7.0.tgz", + "integrity": "sha512-F1WvetjXxbvIMhfMDBh3dXKxJtvd8KUJH42V4wLgJaA+oKXHy8wG6eYKB4Y4kieKEzwwZuIE2PUAd1d8gmrlhA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0-pre.0" + } + }, "node_modules/@tiptap/extension-italic": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.7.0.tgz", diff --git a/package.json b/package.json index 3b6ffcf294..310f0d869a 100644 --- a/package.json +++ b/package.json @@ -202,6 +202,7 @@ "@tiptap/extension-heading": "^2.7.0", "@tiptap/extension-history": "^2.7.0", "@tiptap/extension-horizontal-rule": "^2.7.0", + "@tiptap/extension-image": "^2.7.0", "@tiptap/extension-italic": "^2.7.0", "@tiptap/extension-link": "^2.6.6", "@tiptap/extension-list-item": "^2.7.0", diff --git a/src/external/tiptap/index.ts b/src/external/tiptap/index.ts index 1e3e36e59d..e050542465 100644 --- a/src/external/tiptap/index.ts +++ b/src/external/tiptap/index.ts @@ -22,3 +22,4 @@ export { OrderedList } from '@tiptap/extension-ordered-list'; export { Strike } from '@tiptap/extension-strike'; export { TextAlign } from '@tiptap/extension-text-align'; export { Underline } from '@tiptap/extension-underline'; +export { Image } from '@tiptap/extension-image'; diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index aa453dd069..0540719aac 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -15,6 +15,7 @@ import { HardBreak, History, HorizontalRule, + Image, Italic, Link, ListItem, @@ -93,6 +94,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, '' Code, CodeBlock, HorizontalRule, + Image, Italic, Link.configure({ openOnClick: false }), ListItem, // This is needed for BulletList and OrderedList. When moving to an umbraco-extension, how should we handle shared extensions? From a0a714d59d66b190c2cacd5eb74c4e6576e766c6 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 17 Sep 2024 09:47:43 +0100 Subject: [PATCH 032/241] Refactored `tiptapExtension` to register as global manifest --- .../core/extension-registry/models/index.ts | 3 --- .../models/tiptap-extension.model.ts | 6 ------ .../rte/tiptap/components/input-tiptap/index.ts | 1 - .../components/input-tiptap/input-tiptap.element.ts | 2 +- .../input-tiptap/tiptap-fixed-menu.element.ts | 2 +- src/packages/rte/tiptap/extensions/index.ts | 1 + src/packages/rte/tiptap/extensions/manifests.ts | 2 +- .../input-tiptap => extensions}/tiptap-extension.ts | 13 ++++++++++++- .../extensions/tiptap-mediapicker.extension.ts | 2 +- src/packages/rte/tiptap/index.ts | 1 + 10 files changed, 18 insertions(+), 15 deletions(-) delete mode 100644 src/packages/core/extension-registry/models/tiptap-extension.model.ts create mode 100644 src/packages/rte/tiptap/extensions/index.ts rename src/packages/rte/tiptap/{components/input-tiptap => extensions}/tiptap-extension.ts (71%) diff --git a/src/packages/core/extension-registry/models/index.ts b/src/packages/core/extension-registry/models/index.ts index d79b3f9e4a..00fec52d8f 100644 --- a/src/packages/core/extension-registry/models/index.ts +++ b/src/packages/core/extension-registry/models/index.ts @@ -42,7 +42,6 @@ import type { ManifestSectionView } from './section-view.model.js'; import type { ManifestStore, ManifestTreeStore, ManifestItemStore } from './store.model.js'; import type { ManifestTheme } from './theme.model.js'; import type { ManifestTinyMcePlugin } from './tinymce-plugin.model.js'; -import type { ManifestTiptapExtension } from './tiptap-extension.model.js'; import type { ManifestTree } from './tree.model.js'; import type { ManifestTreeItem } from './tree-item.model.js'; import type { ManifestUfmComponent } from './ufm-component.model.js'; @@ -114,7 +113,6 @@ export type * from './section.model.js'; export type * from './store.model.js'; export type * from './theme.model.js'; export type * from './tinymce-plugin.model.js'; -export type * from './tiptap-extension.model.js'; export type * from './tree-item.model.js'; export type * from './tree.model.js'; export type * from './ufm-component.model.js'; @@ -208,7 +206,6 @@ export type ManifestTypes = | ManifestStore | ManifestTheme | ManifestTinyMcePlugin - | ManifestTiptapExtension | ManifestTree | ManifestTreeItem | ManifestTreeStore diff --git a/src/packages/core/extension-registry/models/tiptap-extension.model.ts b/src/packages/core/extension-registry/models/tiptap-extension.model.ts deleted file mode 100644 index 5d9ff73dfa..0000000000 --- a/src/packages/core/extension-registry/models/tiptap-extension.model.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { UmbTiptapExtensionBase } from '@umbraco-cms/backoffice/tiptap'; -import type { ManifestApi } from '@umbraco-cms/backoffice/extension-api'; - -export interface ManifestTiptapExtension extends ManifestApi { - type: 'tiptapExtension'; -} diff --git a/src/packages/rte/tiptap/components/input-tiptap/index.ts b/src/packages/rte/tiptap/components/input-tiptap/index.ts index 0d9456b123..f11b53c7d9 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/index.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/index.ts @@ -1,2 +1 @@ export * from './input-tiptap.element.js'; -export * from './tiptap-extension.js'; diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 0540719aac..891d8ef366 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -1,4 +1,4 @@ -import type { UmbTiptapExtensionBase } from './tiptap-extension.js'; +import type { UmbTiptapExtensionBase } from '../../extensions/tiptap-extension.js'; import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import { loadManifestApi } from '@umbraco-cms/backoffice/extension-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index ab47a21d1a..1f0fe4c463 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -1,5 +1,5 @@ +import type { UmbTiptapExtensionBase, UmbTiptapToolbarButton } from '../../extensions/tiptap-extension.js'; import * as icons from './icons.js'; -import type { UmbTiptapExtensionBase, UmbTiptapToolbarButton } from './tiptap-extension.js'; import { css, customElement, html, property, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; diff --git a/src/packages/rte/tiptap/extensions/index.ts b/src/packages/rte/tiptap/extensions/index.ts new file mode 100644 index 0000000000..6ff82ee0b6 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/index.ts @@ -0,0 +1 @@ +export * from './tiptap-extension.js'; diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index 6780ce1c09..4de8b54d84 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -1,4 +1,4 @@ -import type { ManifestTiptapExtension } from '@umbraco-cms/backoffice/extension-registry'; +import type { ManifestTiptapExtension } from './tiptap-extension.js'; export const manifests: Array = [ { diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-extension.ts b/src/packages/rte/tiptap/extensions/tiptap-extension.ts similarity index 71% rename from src/packages/rte/tiptap/components/input-tiptap/tiptap-extension.ts rename to src/packages/rte/tiptap/extensions/tiptap-extension.ts index aa01ea3420..b35de22127 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-extension.ts @@ -1,9 +1,14 @@ import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { Editor, Extension, Mark, Node } from '@umbraco-cms/backoffice/external/tiptap'; import type { TemplateResult } from '@umbraco-cms/backoffice/external/lit'; -import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; +import type { ManifestApi, UmbApi } from '@umbraco-cms/backoffice/extension-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +export interface ManifestTiptapExtension extends ManifestApi { + type: 'tiptapExtension'; +} + +// TODO: [LK] Move to a `types.ts` file! export abstract class UmbTiptapExtensionBase extends UmbControllerBase implements UmbApi { constructor(host: UmbControllerHost) { super(host); @@ -20,3 +25,9 @@ export interface UmbTiptapToolbarButton { isActive: (editor?: Editor) => boolean | undefined; command: (editor?: Editor) => boolean | undefined | void | Promise | Promise | Promise; } + +declare global { + interface UmbExtensionManifestMap { + tiptapExtension: ManifestTiptapExtension; + } +} diff --git a/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts b/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts index be14701cef..1b98dbb40f 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts @@ -1,4 +1,4 @@ -import { UmbTiptapExtensionBase } from '../components/input-tiptap/tiptap-extension.js'; +import { UmbTiptapExtensionBase } from './tiptap-extension.js'; import { mergeAttributes, Node } from '@umbraco-cms/backoffice/external/tiptap'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; diff --git a/src/packages/rte/tiptap/index.ts b/src/packages/rte/tiptap/index.ts index 8a8c2711ca..f0f1ade33d 100644 --- a/src/packages/rte/tiptap/index.ts +++ b/src/packages/rte/tiptap/index.ts @@ -1 +1,2 @@ export * from './components/index.js'; +export * from './extensions/index.js'; From ef7d5830733a1bc2cbfa492aae47b93f96626c72 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 17 Sep 2024 09:58:38 +0100 Subject: [PATCH 033/241] Moved Tiptap extension types to their own file/export --- .../input-tiptap/input-tiptap.element.ts | 2 +- .../input-tiptap/tiptap-fixed-menu.element.ts | 2 +- src/packages/rte/tiptap/extensions/index.ts | 3 ++- .../rte/tiptap/extensions/tiptap-extension.ts | 25 ++----------------- .../tiptap-mediapicker.extension.ts | 2 +- src/packages/rte/tiptap/extensions/types.ts | 22 ++++++++++++++++ 6 files changed, 29 insertions(+), 27 deletions(-) create mode 100644 src/packages/rte/tiptap/extensions/types.ts diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 891d8ef366..b5741d6c14 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -1,4 +1,4 @@ -import type { UmbTiptapExtensionBase } from '../../extensions/tiptap-extension.js'; +import type { UmbTiptapExtensionBase } from '../../extensions/types.js'; import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import { loadManifestApi } from '@umbraco-cms/backoffice/extension-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 1f0fe4c463..4764ef5727 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -1,4 +1,4 @@ -import type { UmbTiptapExtensionBase, UmbTiptapToolbarButton } from '../../extensions/tiptap-extension.js'; +import type { UmbTiptapExtensionBase, UmbTiptapToolbarButton } from '../../extensions/types.js'; import * as icons from './icons.js'; import { css, customElement, html, property, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; diff --git a/src/packages/rte/tiptap/extensions/index.ts b/src/packages/rte/tiptap/extensions/index.ts index 6ff82ee0b6..20b2f7a29a 100644 --- a/src/packages/rte/tiptap/extensions/index.ts +++ b/src/packages/rte/tiptap/extensions/index.ts @@ -1 +1,2 @@ -export * from './tiptap-extension.js'; +export type * from './tiptap-extension.js'; +export * from './types.js'; diff --git a/src/packages/rte/tiptap/extensions/tiptap-extension.ts b/src/packages/rte/tiptap/extensions/tiptap-extension.ts index b35de22127..d7fb3485b5 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-extension.ts @@ -1,31 +1,10 @@ -import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import type { Editor, Extension, Mark, Node } from '@umbraco-cms/backoffice/external/tiptap'; -import type { TemplateResult } from '@umbraco-cms/backoffice/external/lit'; -import type { ManifestApi, UmbApi } from '@umbraco-cms/backoffice/extension-api'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbTiptapExtensionBase } from './types.js'; +import type { ManifestApi } from '@umbraco-cms/backoffice/extension-api'; export interface ManifestTiptapExtension extends ManifestApi { type: 'tiptapExtension'; } -// TODO: [LK] Move to a `types.ts` file! -export abstract class UmbTiptapExtensionBase extends UmbControllerBase implements UmbApi { - constructor(host: UmbControllerHost) { - super(host); - } - - abstract getExtensions(): Array; - - abstract getToolbarButtons(): Array; -} - -export interface UmbTiptapToolbarButton { - name: string; - icon: string | TemplateResult; - isActive: (editor?: Editor) => boolean | undefined; - command: (editor?: Editor) => boolean | undefined | void | Promise | Promise | Promise; -} - declare global { interface UmbExtensionManifestMap { tiptapExtension: ManifestTiptapExtension; diff --git a/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts b/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts index 1b98dbb40f..1b69751c54 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts @@ -1,4 +1,4 @@ -import { UmbTiptapExtensionBase } from './tiptap-extension.js'; +import { UmbTiptapExtensionBase } from './types.js'; import { mergeAttributes, Node } from '@umbraco-cms/backoffice/external/tiptap'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; diff --git a/src/packages/rte/tiptap/extensions/types.ts b/src/packages/rte/tiptap/extensions/types.ts new file mode 100644 index 0000000000..aa01ea3420 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/types.ts @@ -0,0 +1,22 @@ +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { Editor, Extension, Mark, Node } from '@umbraco-cms/backoffice/external/tiptap'; +import type { TemplateResult } from '@umbraco-cms/backoffice/external/lit'; +import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export abstract class UmbTiptapExtensionBase extends UmbControllerBase implements UmbApi { + constructor(host: UmbControllerHost) { + super(host); + } + + abstract getExtensions(): Array; + + abstract getToolbarButtons(): Array; +} + +export interface UmbTiptapToolbarButton { + name: string; + icon: string | TemplateResult; + isActive: (editor?: Editor) => boolean | undefined; + command: (editor?: Editor) => boolean | undefined | void | Promise | Promise | Promise; +} From a43545c7a7ca844bbe32b1d67dc7bdde84084209 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 17 Sep 2024 15:26:16 +0200 Subject: [PATCH 034/241] drag and drop wip --- ...ui-tiptap-toolbar-configuration.element.ts | 323 ++++++++++++++---- 1 file changed, 260 insertions(+), 63 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 6524700bb1..de1c6e67ab 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -1,14 +1,13 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; -import { customElement, css, html, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; +import { customElement, css, html, property, state, repeat, render } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; -import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs'; import { UmbPropertyValueChangeEvent, type UmbPropertyEditorConfigCollection, } from '@umbraco-cms/backoffice/property-editor'; + import { tinymce } from '@umbraco-cms/backoffice/external/tinymce'; const tinyIconSet = tinymce.IconManager.get('default'); @@ -18,8 +17,14 @@ type ToolbarConfig = { label: string; icon?: string; selected: boolean; + group: string; }; +type ToolbarItems = Array<{ + name: string; + items: ToolbarConfig[]; +}>; + /** * @element umb-property-editor-ui-tiptap-toolbar-configuration */ @@ -30,26 +35,55 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement { @property({ attribute: false }) set value(value: string | string[] | null) { - if (!value) return; - - if (typeof value === 'string') { - this.#selectedValues = value.split(',').filter((x) => x.length > 0); - } else if (Array.isArray(value)) { - this.#selectedValues = value; - } else { + if (!value) { this.#selectedValues = []; - return; + } else { + if (typeof value === 'string') { + this.#selectedValues = value.split(',').filter((x) => x.length > 0); + } else if (Array.isArray(value)) { + this.#selectedValues = value; + } else { + this.#selectedValues = []; + } } - // Migrations - if (this.#selectedValues.includes('ace')) { - this.#selectedValues = this.#selectedValues.filter((v) => v !== 'ace'); - this.#selectedValues.push('sourcecode'); - } + this._selectedValuesNew = [ + [ + [ + { + alias: 'undo', + label: 'Undo', + icon: 'undo', + selected: false, + group: 'clipboard', + }, + ], + ], + [[]], + ]; - this._toolbarConfig.forEach((v) => { - v.selected = this.#selectedValues.includes(v.alias); + this.#selectedValues.forEach((alias) => { + const row = Math.floor(Math.random() * 2); + const group = Math.floor(Math.random() * 2); + const item = this._toolbarConfig.find((value) => value.alias === alias); + + if (!item) return; + + // Ensure the row exists + if (!this._selectedValuesNew[row]) { + this._selectedValuesNew[row] = []; // Initialize the row if it doesn't exist + } + + // Ensure the group exists within the row + if (!this._selectedValuesNew[row][group]) { + this._selectedValuesNew[row][group] = []; // Initialize the group if it doesn't exist + } + + // Add the item to the selectedValuesNew array + this._selectedValuesNew[row][group].push(item); }); + + this.requestUpdate('#selectedValuesNew'); } get value(): string[] { return this.#selectedValues; @@ -59,10 +93,16 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement config?: UmbPropertyEditorConfigCollection; @state() - private _toolbarConfig: ToolbarConfig[] = []; + private _toolbarItems: ToolbarItems = []; + + @state() + private _toolbarConfig: Array = []; #selectedValues: string[] = []; + @state() + _selectedValuesNew: ToolbarConfig[][][] = [[[]]]; + protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); @@ -73,68 +113,225 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement }); }); - await this.getToolbarPlugins(); + const grouped = this._toolbarConfig.reduce((acc: any, item) => { + const group = item.group || 'miscellaneous'; // Assign to "miscellaneous" if no group + + if (!acc[group]) { + acc[group] = []; + } + acc[group].push(item); + return acc; + }, {}); + + this._toolbarItems = Object.keys(grouped).map((group) => ({ + name: group.charAt(0).toUpperCase() + group.slice(1).replace(/-/g, ' '), + items: grouped[group], + })); this.requestUpdate('_toolbarConfig'); } - private async getToolbarPlugins(): Promise { - // Get all the toolbar plugins - const plugin$ = umbExtensionsRegistry.byType('tinyMcePlugin'); - - const plugins = await firstValueFrom(plugin$); - - plugins.forEach((p) => { - // If the plugin has a toolbar, add it to the config - if (p.meta?.toolbar) { - p.meta.toolbar.forEach((t: any) => { - this._toolbarConfig.push({ - alias: t.alias, - label: this.localize.string(t.label), - icon: t.icon ?? 'icon-autofill', - selected: this.value.includes(t.alias), - }); - }); - } - }); + #onExtensionSelect(item: ToolbarConfig, row?: number, group?: number) { + // if no row is provided, add to the last row and last group + if (row === undefined) { + row = this._selectedValuesNew.length - 1; + } + + // if no group is provided, add to the last group in the row + if (group === undefined) { + group = this._selectedValuesNew[row].length - 1; + } + + // Add the item to the selectedValuesNew array + this._selectedValuesNew[row][group].push(item); + this.requestUpdate('_selectedValuesNew'); } - private onChange(event: CustomEvent) { - const checkbox = event.target as HTMLInputElement; - const alias = checkbox.value; + #addGroup(row: number) { + this._selectedValuesNew[row].push([]); + this.requestUpdate('_selectedValuesNew'); + } + + #addRow() { + this._selectedValuesNew.push([[]]); + this.requestUpdate('_selectedValuesNew'); + } - const value = this._toolbarConfig - .filter((t) => (t.alias !== alias && t.selected) || (t.alias === alias && checkbox.checked)) - .map((v) => v.alias); + #onChange = (item: ToolbarConfig) => { + const value = this._toolbarItems + .flatMap((group) => + group.items.map((i) => { + if (i.alias === item.alias) { + i.selected = !i.selected; + } + return i.selected ? i.alias : null; + }), + ) + .filter((v): v is string => v !== null); // Ensures we only keep non-null strings - this.value = value; + // If the value array is empty, set this.value to null, otherwise assign the array + this.value = value.length > 0 ? value : null; this.dispatchEvent(new UmbPropertyValueChangeEvent()); + }; + + #onDragStart = (event: DragEvent, alias: string) => { + event.dataTransfer!.setData('text/plain', alias); + event.dataTransfer!.dropEffect = 'move'; + event.dataTransfer!.effectAllowed = 'move'; + }; + + #onDragOver = (event: DragEvent) => { + event.preventDefault(); + const element = event.target as HTMLElement; + if (!element) return; + element.classList.add('drag-over'); + }; + + #onDragEnd(event: DragEvent) { + const element = event.target as HTMLElement; + if (!element) return; + element.classList.remove('drag-over'); + } + + #onDrop(event: DragEvent) { + event.preventDefault(); + const groupElement = event.target as HTMLElement; + if (!groupElement) return; + + groupElement.classList.remove('drag-over'); + + const alias = event.dataTransfer!.getData('text/plain'); + if (!alias) return; + + const item = this._toolbarConfig.find((v) => v.alias === alias); + if (!item) return; + + const rowAttribute = groupElement.getAttribute('umb-data-row'); + const rowIndex = rowAttribute ? Number.parseInt(rowAttribute) : null; + + const groupAttribute = groupElement.getAttribute('umb-data-group'); + const groupIndex = groupAttribute ? Number.parseInt(groupAttribute) : null; + + if (groupIndex === null || rowIndex === null) return; + + // remove alias from selectedValues + this._selectedValuesNew = this._selectedValuesNew.map((row) => + row.map((group) => group.filter((v) => v.alias !== alias)), + ); + + this.#onExtensionSelect(item, rowIndex, groupIndex); + } + + #renderRow(row: ToolbarConfig[][], rowIndex: number) { + return html`
    + ${row.map((group, index) => { + return this.#renderGroup(group, index, rowIndex); + })} + this.#addGroup(rowIndex)}>+ +
    `; + } + + #renderGroup(group: ToolbarConfig[], groupIndex: number, rowIndex: number) { + return html`
    + ${group.map((item) => { + return html` + this.#onDragStart(e, item.alias)} + compact + look="outline" + class=${item.selected ? 'selected' : ''} + label=${item.label} + .value=${item.alias} + @click=${() => this.#onChange(item)} + > + `; + })} +
    `; } override render() { - return html`
      - ${repeat( - this._toolbarConfig, - (v) => v.alias, - (v) => - html`
    • - - - ${v.label} - -
    • `, - )} -
    `; + console.log('RENDER'); + return html` +
    + ${repeat(this._selectedValuesNew, (row, index) => this.#renderRow(row, index))} + this.#addRow()}>+ +
    +
    + ${repeat( + this._toolbarItems, + (group) => html` +

    ${group.name}

    + ${repeat( + group.items, + (item) => + html` this.#onExtensionSelect(item)} + >`, + )} + `, + )} +
    + `; } static override styles = [ UmbTextStyles, css` - ul { - list-style: none; - padding: 0; - margin: 0; + uui-icon { + width: unset; + height: unset; + display: flex; + vertical-align: unset; + } + uui-button.selected { + --uui-button-border-color: var(--uui-color-selected); + --uui-button-border-width: 2px; + } + .selected-bar { + display: flex; + flex-direction: column; + gap: 12px; + } + .selected-row { + display: flex; + gap: 18px; + } + .selected-group { + padding: 6px; + min-width: 12px; + background-color: var(--uui-color-surface-alt); + border-radius: var(--uui-border-radius); + display: flex; + gap: 6px; + } + .selected-group.drag-over uui-button { + pointer-events: none; + } + .extensions { + display: grid; + grid-template-columns: repeat(auto-fit, 36px); + gap: 10px; + } + .group-name { + grid-column: 1 / -1; + margin-bottom: 0; + font-weight: bold; } `, ]; From e64349114fd386fa5cb9ded25ba9372c4cf38079 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 18 Sep 2024 08:09:05 +0200 Subject: [PATCH 035/241] fix: add tree handler for ancestors to avoid api error --- src/mocks/data/utils/entity/entity-tree.manager.ts | 12 ++++++++++++ src/mocks/handlers/document/tree.handlers.ts | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/src/mocks/data/utils/entity/entity-tree.manager.ts b/src/mocks/data/utils/entity/entity-tree.manager.ts index fc6a0c7334..61ebfea7a5 100644 --- a/src/mocks/data/utils/entity/entity-tree.manager.ts +++ b/src/mocks/data/utils/entity/entity-tree.manager.ts @@ -21,6 +21,18 @@ export class UmbMockEntityTreeManager { + const items = []; + let currentId: string | undefined = descendantId; + while (currentId) { + const item = this.#db.read(currentId); + if (!item) break; + items.push(item); + currentId = item.parent?.id; + } + return items.reverse(); + } + #pagedTreeResult({ items, skip, take }: { items: Array; skip: number; take: number }) { const paged = pagedResult(items, skip, take); const treeItems = paged.items.map((item) => this.#treeItemMapper(item)); diff --git a/src/mocks/handlers/document/tree.handlers.ts b/src/mocks/handlers/document/tree.handlers.ts index e148fc83da..df3400b392 100644 --- a/src/mocks/handlers/document/tree.handlers.ts +++ b/src/mocks/handlers/document/tree.handlers.ts @@ -1,5 +1,6 @@ const { rest } = window.MockServiceWorker; import { umbDocumentMockDb } from '../../data/document/document.db.js'; +import type { GetTreeDocumentAncestorsResponse } from '@umbraco-cms/backoffice/external/backend-api'; import { UMB_SLUG } from './slug.js'; import { umbracoPath } from '@umbraco-cms/backoffice/utils'; @@ -19,4 +20,11 @@ export const treeHandlers = [ const response = umbDocumentMockDb.tree.getChildrenOf({ parentId, skip, take }); return res(ctx.status(200), ctx.json(response)); }), + + rest.get(umbracoPath(`/tree${UMB_SLUG}/ancestors`), (req, res, ctx) => { + const descendantId = req.url.searchParams.get('descendantId'); + if (!descendantId) return; + const response = umbDocumentMockDb.tree.getAncestorsOf({ descendantId }); + return res(ctx.status(200), ctx.json(response)); + }), ]; From 02a0c6e724c532d544ac670d7bea66bf7a039162 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 18 Sep 2024 08:23:59 +0200 Subject: [PATCH 036/241] feat: adds readonly mode to the tiptap input element --- .../input-tiptap/input-tiptap.element.ts | 32 +++++++++++++++---- .../input-tiptap/tiptap-fixed-menu.element.ts | 8 +++++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index b5741d6c14..413111b5c4 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -35,13 +35,19 @@ import './tiptap-fixed-menu.element.js'; import './tiptap-hover-menu.element.js'; @customElement('umb-input-tiptap') -export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, '') { - @state() - private _extensions: Array = []; - +export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, undefined) { @property({ attribute: false }) configuration?: UmbPropertyEditorConfigCollection; + /** + * Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content. + */ + @property({ type: Boolean, reflect: true }) + readonly = false; + + @state() + private _extensions: Array = []; + @state() private _editor!: Editor; @@ -78,6 +84,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, '' this._editor = new Editor({ element: element, + editable: !this.readonly, extensions: [ // REQUIRED EXTENSIONS START Document, @@ -106,7 +113,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, '' Underline, ...extensions, ], - content: this.value.toString(), + content: this.value?.toString(), onUpdate: ({ editor }) => { this.value = editor.getHTML(); this.dispatchEvent(new UmbChangeEvent()); @@ -118,13 +125,24 @@ export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, '' if (!this._extensions?.length) return html``; return html` - +
    `; } - static override styles = [ + static override readonly styles = [ css` + :host([readonly]) { + pointer-events: none; + + #editor { + background-color: var(--uui-color-surface-alt); + } + } + #editor { border-radius: var(--uui-border-radius); border: 1px solid var(--uui-color-border); diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 4764ef5727..0fb04b34d6 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -6,6 +6,9 @@ import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; @customElement('umb-tiptap-fixed-menu') export class UmbTiptapFixedMenuElement extends UmbLitElement { + @property({ type: Boolean, reflect: true }) + readonly = false; + @state() actions: Array = [ // TODO: I don't think we need a paragraph button. It's the default state. @@ -196,6 +199,11 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { padding: 4px; } + :host([readonly]) { + pointer-events: none; + background-color: var(--uui-color-surface-alt); + } + button { color: var(--uui-color-interactive); width: 24px; From a7d1ada66dbd8440a2df9fe144e4f0816a5cc5ec Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 18 Sep 2024 08:25:47 +0200 Subject: [PATCH 037/241] chore: fix a couple of sonarcloud warnings --- .../tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts | 2 +- .../tiptap/components/input-tiptap/tiptap-hover-menu.element.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 0fb04b34d6..3675670ab3 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -184,7 +184,7 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { `; } - static override styles = css` + static override readonly styles = css` :host { border-radius: var(--uui-border-radius); border: 1px solid var(--uui-color-border); diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts index a280a832ed..28c9feb62e 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts @@ -38,7 +38,7 @@ export class UmbTiptapHoverMenuElement extends LitElement { return html``; } - static override styles = css` + static override readonly styles = css` :host { position: fixed; background-color: var(--uui-color-surface-alt); From 2bf462f1ca3fe8ad205ad4822810ac6cb7519e71 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:42:44 +0200 Subject: [PATCH 038/241] fix: set inline to true for Image --- .../rte/tiptap/components/input-tiptap/input-tiptap.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index b5741d6c14..07d0542e0c 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -94,7 +94,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, '' Code, CodeBlock, HorizontalRule, - Image, + Image.configure({ inline: true }), Italic, Link.configure({ openOnClick: false }), ListItem, // This is needed for BulletList and OrderedList. When moving to an umbraco-extension, how should we handle shared extensions? From b5f194cd07fec21198a26f4da3adaa3fc6d34c91 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:55:06 +0200 Subject: [PATCH 039/241] chore: add more attributes to mock image data --- src/mocks/data/document/document.data.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mocks/data/document/document.data.ts b/src/mocks/data/document/document.data.ts index 09ec5524e0..cbdce9da9f 100644 --- a/src/mocks/data/document/document.data.ts +++ b/src/mocks/data/document/document.data.ts @@ -843,7 +843,7 @@ export const data: Array = [ Some value for the RTE with an external link and an internal link foo foo

    - Jason + Jason

    End of test content

    `, @@ -861,7 +861,7 @@ export const data: Array = [

    Macro alias: TestMacro

    - Jason + Jason

    End of test content

    `, From f0ec09d831def8534e9a50976f55c88aa1235a2f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:55:24 +0200 Subject: [PATCH 040/241] feat: creates and uses an extension of Image that allows more attributes --- .../extensions/tiptap-umb-image.extension.ts | 25 +++++++++++++++++++ src/external/tiptap/index.ts | 2 ++ .../input-tiptap/input-tiptap.element.ts | 4 +-- 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 src/external/tiptap/extensions/tiptap-umb-image.extension.ts diff --git a/src/external/tiptap/extensions/tiptap-umb-image.extension.ts b/src/external/tiptap/extensions/tiptap-umb-image.extension.ts new file mode 100644 index 0000000000..899c775895 --- /dev/null +++ b/src/external/tiptap/extensions/tiptap-umb-image.extension.ts @@ -0,0 +1,25 @@ +import { nodeInputRule } from '@tiptap/core'; +import Image, { inputRegex } from '@tiptap/extension-image'; + +export const UmbImage = Image.extend({ + addAttributes() { + return { + ...this.parent?.(), + width: { + default: '100%', + }, + height: { + default: null, + }, + loading: { + default: null, + }, + srcset: { + default: null, + }, + sizes: { + default: null, + }, + }; + }, +}); diff --git a/src/external/tiptap/index.ts b/src/external/tiptap/index.ts index e050542465..5c6fabf333 100644 --- a/src/external/tiptap/index.ts +++ b/src/external/tiptap/index.ts @@ -23,3 +23,5 @@ export { Strike } from '@tiptap/extension-strike'; export { TextAlign } from '@tiptap/extension-text-align'; export { Underline } from '@tiptap/extension-underline'; export { Image } from '@tiptap/extension-image'; +// CUSTOM EXTENSIONS +export * from './extensions/tiptap-umb-image.extension.js'; diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 07d0542e0c..57d42f1cbe 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -15,7 +15,7 @@ import { HardBreak, History, HorizontalRule, - Image, + UmbImage, Italic, Link, ListItem, @@ -94,7 +94,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, '' Code, CodeBlock, HorizontalRule, - Image.configure({ inline: true }), + UmbImage.configure({ inline: true }), Italic, Link.configure({ openOnClick: false }), ListItem, // This is needed for BulletList and OrderedList. When moving to an umbraco-extension, how should we handle shared extensions? From 154b24cba52a3be675b8c3c8dbcd8f502c63ec4c Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:06:14 +0200 Subject: [PATCH 041/241] chore: cleanup imports --- src/external/tiptap/extensions/tiptap-umb-image.extension.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/external/tiptap/extensions/tiptap-umb-image.extension.ts b/src/external/tiptap/extensions/tiptap-umb-image.extension.ts index 899c775895..e2f1e7f630 100644 --- a/src/external/tiptap/extensions/tiptap-umb-image.extension.ts +++ b/src/external/tiptap/extensions/tiptap-umb-image.extension.ts @@ -1,5 +1,4 @@ -import { nodeInputRule } from '@tiptap/core'; -import Image, { inputRegex } from '@tiptap/extension-image'; +import Image from '@tiptap/extension-image'; export const UmbImage = Image.extend({ addAttributes() { From 6a145d178b3b264275c64513a6386ac633a5c841 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:06:32 +0200 Subject: [PATCH 042/241] feat: register UmbImage as an extension --- .../components/input-tiptap/input-tiptap.element.ts | 2 -- src/packages/rte/tiptap/extensions/manifests.ts | 8 ++++++++ .../rte/tiptap/extensions/tiptap-image.extension.ts | 12 ++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 src/packages/rte/tiptap/extensions/tiptap-image.extension.ts diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 57d42f1cbe..897355b90d 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -15,7 +15,6 @@ import { HardBreak, History, HorizontalRule, - UmbImage, Italic, Link, ListItem, @@ -94,7 +93,6 @@ export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, '' Code, CodeBlock, HorizontalRule, - UmbImage.configure({ inline: true }), Italic, Link.configure({ openOnClick: false }), ListItem, // This is needed for BulletList and OrderedList. When moving to an umbraco-extension, how should we handle shared extensions? diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index 4de8b54d84..1d192608da 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -1,10 +1,18 @@ import type { ManifestTiptapExtension } from './tiptap-extension.js'; export const manifests: Array = [ + { + type: 'tiptapExtension', + alias: 'Umb.Tiptap.Image', + name: 'Image Tiptap Extension', + weight: 1000, + api: () => import('./tiptap-image.extension.js'), + }, { type: 'tiptapExtension', alias: 'Umb.Tiptap.MediaPicker', name: 'Media Picker Tiptap Extension', + weight: 900, api: () => import('./tiptap-mediapicker.extension.js'), }, ]; diff --git a/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts b/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts new file mode 100644 index 0000000000..5e0f0f3b81 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts @@ -0,0 +1,12 @@ +import { UmbTiptapExtensionBase } from './types.js'; +import { UmbImage } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapImageExtension extends UmbTiptapExtensionBase { + getExtensions() { + return [UmbImage.configure({ inline: true })]; + } + + getToolbarButtons() { + return []; + } +} From 4eeed263cc49d4317da05b865a2f72f432cb6ed2 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 18 Sep 2024 14:25:04 +0200 Subject: [PATCH 043/241] feat: declare module for updated setImage function --- .../extensions/tiptap-umb-image.extension.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/external/tiptap/extensions/tiptap-umb-image.extension.ts b/src/external/tiptap/extensions/tiptap-umb-image.extension.ts index e2f1e7f630..2f0eceedd4 100644 --- a/src/external/tiptap/extensions/tiptap-umb-image.extension.ts +++ b/src/external/tiptap/extensions/tiptap-umb-image.extension.ts @@ -22,3 +22,28 @@ export const UmbImage = Image.extend({ }; }, }); + +declare module '@tiptap/core' { + interface Commands { + umbImage: { + /** + * Add an image + * @param options The image attributes + * @example + * editor + * .commands + * .setImage({ src: 'https://tiptap.dev/logo.png', alt: 'tiptap', title: 'tiptap logo' }) + */ + setImage: (options: { + src: string; + alt?: string; + title?: string; + width?: string; + height?: string; + loading?: string; + srcset?: string; + sizes?: string; + }) => ReturnType; + }; + } +} From 5b6b3bf0201c6ca9a5c88c09442b6c5549edd732 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Wed, 18 Sep 2024 14:45:52 +0200 Subject: [PATCH 044/241] work --- ...ui-tiptap-toolbar-configuration.element.ts | 105 +++++++++--------- .../property-editors/tiptap/manifests.ts | 40 +++---- 2 files changed, 73 insertions(+), 72 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index de1c6e67ab..b273529074 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -1,6 +1,6 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; -import { customElement, css, html, property, state, repeat, render } from '@umbraco-cms/backoffice/external/lit'; +import { customElement, css, html, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { @@ -47,21 +47,6 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement } } - this._selectedValuesNew = [ - [ - [ - { - alias: 'undo', - label: 'Undo', - icon: 'undo', - selected: false, - group: 'clipboard', - }, - ], - ], - [[]], - ]; - this.#selectedValues.forEach((alias) => { const row = Math.floor(Math.random() * 2); const group = Math.floor(Math.random() * 2); @@ -98,11 +83,13 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement @state() private _toolbarConfig: Array = []; - #selectedValues: string[] = []; - @state() _selectedValuesNew: ToolbarConfig[][][] = [[[]]]; + #selectedValues: string[] = []; + + #hoveredDropzone: HTMLElement | null = null; + protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); @@ -183,23 +170,26 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement #onDragOver = (event: DragEvent) => { event.preventDefault(); - const element = event.target as HTMLElement; - if (!element) return; - element.classList.add('drag-over'); }; - #onDragEnd(event: DragEvent) { - const element = event.target as HTMLElement; - if (!element) return; - element.classList.remove('drag-over'); - } + #onDragEnter = (event: DragEvent) => { + const dropzone = event + .composedPath() + .find((v) => v instanceof HTMLElement && v.classList.contains('selected-group') && v.hasAttribute('dropzone')); - #onDrop(event: DragEvent) { + this.#hoveredDropzone = (dropzone as HTMLElement) || null; + }; + + #onDrop = (event: DragEvent) => { event.preventDefault(); - const groupElement = event.target as HTMLElement; - if (!groupElement) return; - groupElement.classList.remove('drag-over'); + const groupElement = event + .composedPath() + .find( + (v) => v instanceof HTMLElement && v.classList.contains('selected-group') && v.hasAttribute('dropzone'), + ) as HTMLElement; + + if (!groupElement) return; const alias = event.dataTransfer!.getData('text/plain'); if (!alias) return; @@ -221,7 +211,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement ); this.#onExtensionSelect(item, rowIndex, groupIndex); - } + }; #renderRow(row: ToolbarConfig[][], rowIndex: number) { return html`
    @@ -234,11 +224,11 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement #renderGroup(group: ToolbarConfig[], groupIndex: number, rowIndex: number) { return html`
    ${group.map((item) => { @@ -260,7 +250,6 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement } override render() { - console.log('RENDER'); return html`
    ${repeat(this._selectedValuesNew, (row, index) => this.#renderRow(row, index))} @@ -269,20 +258,27 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement
    ${repeat( this._toolbarItems, - (group) => html` -

    ${group.name}

    + (category) => html` +

    + ${category.name} + Hide in toolbar +

    ${repeat( - group.items, + category.items, (item) => - html` this.#onExtensionSelect(item)} - >`, + html`
    + this.#onExtensionSelect(item)} + > + ${item.label} + +
    `, )} `, )} @@ -319,19 +315,24 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement border-radius: var(--uui-border-radius); display: flex; gap: 6px; - } - .selected-group.drag-over uui-button { - pointer-events: none; + position: relative; } .extensions { - display: grid; + /* display: grid; grid-template-columns: repeat(auto-fit, 36px); - gap: 10px; + gap: 10px; */ + max-width: 400px; + } + .extension-item { + display: grid; + grid-template-columns: 36px 1fr auto; + grid-template-rows: 1fr; } - .group-name { + .category-name { grid-column: 1 / -1; margin-bottom: 0; font-weight: bold; + display: flex; } `, ]; diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index eb0521df3f..d1a5464205 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -105,26 +105,26 @@ export const manifests: Array = [ }, ], defaultData: [ - { - alias: 'toolbar', - value: [ - 'styles', - 'bold', - 'italic', - 'alignleft', - 'aligncenter', - 'alignright', - 'bullist', - 'numlist', - 'outdent', - 'indent', - 'sourcecode', - 'link', - ], - }, - { alias: 'mode', value: 'Classic' }, - { alias: 'maxWidth', value: 800 }, - { alias: 'maxHeight', value: 500 }, + // { + // alias: 'toolbar', + // value: [ + // 'styles', + // 'bold', + // 'italic', + // 'alignleft', + // 'aligncenter', + // 'alignright', + // 'bullist', + // 'numlist', + // 'outdent', + // 'indent', + // 'sourcecode', + // 'link', + // ], + // }, + // { alias: 'mode', value: 'Classic' }, + // { alias: 'maxWidth', value: 800 }, + // { alias: 'maxHeight', value: 500 }, ], }, }, From ca652424c470addebea01a38bc5cf63d2429a927 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Wed, 18 Sep 2024 15:46:43 +0100 Subject: [PATCH 045/241] Adds "button" kind for Tiptap toolbar --- src/packages/rte/manifests.ts | 4 +-- .../rte/tiptap/extensions/manifests.ts | 24 ++++++++++++- .../rte/tiptap/extensions/tiptap-extension.ts | 25 +++++++++++-- .../extensions/tiptap-image.extension.ts | 10 ++---- .../tiptap-mediapicker.extension.ts | 35 ++++++++----------- .../tiptap-toolbar-button.element.ts | 34 ++++++++++++++++++ src/packages/rte/tiptap/extensions/types.ts | 7 ++-- src/packages/rte/tiptap/manifests.ts | 4 +-- 8 files changed, 104 insertions(+), 39 deletions(-) create mode 100644 src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts diff --git a/src/packages/rte/manifests.ts b/src/packages/rte/manifests.ts index 1a38fe7fe9..5e69aa24fd 100644 --- a/src/packages/rte/manifests.ts +++ b/src/packages/rte/manifests.ts @@ -1,4 +1,4 @@ import { manifests as tiptapManifests } from './tiptap/manifests.js'; -import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; +import type { ManifestTypes, UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; -export const manifests: Array = [...tiptapManifests]; +export const manifests: Array = [...tiptapManifests]; diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index 1d192608da..8afb03e629 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -1,18 +1,40 @@ import type { ManifestTiptapExtension } from './tiptap-extension.js'; +import type { ManifestTypes, UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; -export const manifests: Array = [ +const kinds: Array = [ + { + type: 'kind', + alias: 'Umb.Kind.Button', + matchKind: 'button', + matchType: 'tiptapExtension', + manifest: { + element: () => import('./tiptap-toolbar-button.element.js'), + }, + }, +]; + +const extensions: Array = [ { type: 'tiptapExtension', alias: 'Umb.Tiptap.Image', name: 'Image Tiptap Extension', weight: 1000, api: () => import('./tiptap-image.extension.js'), + meta: {}, }, { type: 'tiptapExtension', + kind: 'button', alias: 'Umb.Tiptap.MediaPicker', name: 'Media Picker Tiptap Extension', weight: 900, api: () => import('./tiptap-mediapicker.extension.js'), + meta: { + alias: 'umb-media', + icon: 'icon-picture', + label: 'Media picker', + }, }, ]; + +export const manifests: Array = [...kinds, ...extensions]; diff --git a/src/packages/rte/tiptap/extensions/tiptap-extension.ts b/src/packages/rte/tiptap/extensions/tiptap-extension.ts index d7fb3485b5..40c9fd6186 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-extension.ts @@ -1,8 +1,27 @@ -import type { UmbTiptapExtensionBase } from './types.js'; -import type { ManifestApi } from '@umbraco-cms/backoffice/extension-api'; +import type { UmbTiptapExtensionApi } from './types.js'; +import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import type { ManifestElementAndApi } from '@umbraco-cms/backoffice/extension-api'; -export interface ManifestTiptapExtension extends ManifestApi { +export interface ManifestTiptapExtension + extends ManifestElementAndApi { type: 'tiptapExtension'; + meta: MetaType; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export interface MetaTiptapExtension {} + +export interface ManifestTiptapExtensionButtonKind< + MetaType extends MetaTiptapExtensionButtonKind = MetaTiptapExtensionButtonKind, +> extends ManifestTiptapExtension { + type: 'tiptapExtension'; + kind: 'button'; +} + +export interface MetaTiptapExtensionButtonKind extends MetaTiptapExtension { + alias: string; + icon: string; + label: string; } declare global { diff --git a/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts b/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts index 5e0f0f3b81..f6d64e11a2 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts @@ -1,12 +1,8 @@ -import { UmbTiptapExtensionBase } from './types.js'; +import { UmbTiptapExtensionApi } from './types.js'; import { UmbImage } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapImageExtension extends UmbTiptapExtensionBase { - getExtensions() { +export default class UmbTiptapImageExtension extends UmbTiptapExtensionApi { + getTiptapExtensions() { return [UmbImage.configure({ inline: true })]; } - - getToolbarButtons() { - return []; - } } diff --git a/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts b/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts index 1b69751c54..7d96091761 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts @@ -1,23 +1,23 @@ -import { UmbTiptapExtensionBase } from './types.js'; +import { UmbTiptapExtensionApi } from './types.js'; import { mergeAttributes, Node } from '@umbraco-cms/backoffice/external/tiptap'; +import { UMB_MEDIA_PICKER_MODAL } from '@umbraco-cms/backoffice/media'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -import { UMB_MEDIA_PICKER_MODAL } from '@umbraco-cms/backoffice/media'; -export default class UmbTiptapMediaPickerPlugin extends UmbTiptapExtensionBase { - getExtensions() { +export default class UmbTiptapMediaPickerPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions() { return [ Node.create({ name: 'umbMediaPicker', + priority: 1000, group: 'block', marks: '', draggable: true, addNodeView() { return () => { //console.log('umb-media.addNodeView'); - const dom = document.createElement('umb-debug'); - dom.attributes.setNamedItem(document.createAttribute('visible')); - dom.attributes.setNamedItem(document.createAttribute('dialog')); + const dom = document.createElement('span'); + dom.innerText = '🖼️'; return { dom }; }; }, @@ -33,22 +33,15 @@ export default class UmbTiptapMediaPickerPlugin extends UmbTiptapExtensionBase { ]; } - getToolbarButtons() { - return [ - { - name: 'umb-media', - icon: 'icon-picture', - isActive: (editor?: Editor) => editor?.isActive('umbMediaPicker'), - command: async (editor?: Editor) => { - //console.log('umb-media.command', editor); + //isActive: (editor?: Editor) => editor?.isActive('umbMediaPicker') || editor?.isActive('image'), - const selection = await this.#openMediaPicker(); - if (!selection || !selection.length) return; + override async execute(editor?: Editor) { + console.log('umb-media.execute', editor); - editor?.chain().focus().insertContent(`${selection}`).run(); - }, - }, - ]; + const selection = await this.#openMediaPicker(); + if (!selection || !selection.length) return; + + editor?.chain().focus().insertContent(`${selection}`).run(); } async #openMediaPicker() { diff --git a/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts b/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts new file mode 100644 index 0000000000..3d1ad704a6 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts @@ -0,0 +1,34 @@ +import type { ManifestTiptapExtensionButtonKind } from './tiptap-extension.js'; +import type { UmbTiptapExtensionApi } from './types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; +import { customElement, html, ifDefined, when } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; + +const elementName = 'umb-tiptap-toolbar-button'; + +@customElement(elementName) +export class UmbTiptapToolbarButtonElement extends UmbLitElement { + public api?: UmbTiptapExtensionApi; + public editor?: Editor; + public manifest?: ManifestTiptapExtensionButtonKind; + + override render() { + return html` + this.api?.execute(this.editor)}> + ${when( + this.manifest?.meta.icon, + () => html``, + () => html`${this.manifest?.meta.label}`, + )} + + `; + } +} + +export { UmbTiptapToolbarButtonElement as element }; + +declare global { + interface HTMLElementTagNameMap { + [elementName]: UmbTiptapToolbarButtonElement; + } +} diff --git a/src/packages/rte/tiptap/extensions/types.ts b/src/packages/rte/tiptap/extensions/types.ts index aa01ea3420..5a2d19ccd4 100644 --- a/src/packages/rte/tiptap/extensions/types.ts +++ b/src/packages/rte/tiptap/extensions/types.ts @@ -4,14 +4,15 @@ import type { TemplateResult } from '@umbraco-cms/backoffice/external/lit'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -export abstract class UmbTiptapExtensionBase extends UmbControllerBase implements UmbApi { +export abstract class UmbTiptapExtensionApi extends UmbControllerBase implements UmbApi { constructor(host: UmbControllerHost) { super(host); } - abstract getExtensions(): Array; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + execute(editor?: Editor) {} - abstract getToolbarButtons(): Array; + abstract getTiptapExtensions(): Array; } export interface UmbTiptapToolbarButton { diff --git a/src/packages/rte/tiptap/manifests.ts b/src/packages/rte/tiptap/manifests.ts index 8e6836b5cc..a3e9604def 100644 --- a/src/packages/rte/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/manifests.ts @@ -1,5 +1,5 @@ import { manifests as extensions } from './extensions/manifests.js'; import { manifests as propertyEditors } from './property-editors/manifests.js'; -import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; +import type { ManifestTypes, UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; -export const manifests: Array = [...extensions, ...propertyEditors]; +export const manifests: Array = [...extensions, ...propertyEditors]; From 16c806caa58bbf8a5902b9db81dfe2307d7fc04d Mon Sep 17 00:00:00 2001 From: leekelleher Date: Wed, 18 Sep 2024 15:47:53 +0100 Subject: [PATCH 046/241] Updated `umb-input-tiptap` to use extensions --- .../input-tiptap/input-tiptap.element.ts | 52 +++++++++---------- .../input-tiptap/tiptap-fixed-menu.element.ts | 16 +++--- .../property-editor-ui-tiptap.element.ts | 12 +++-- 3 files changed, 39 insertions(+), 41 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index db32b07231..1aca063f49 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -1,5 +1,5 @@ -import type { UmbTiptapExtensionBase } from '../../extensions/types.js'; -import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; +import type { UmbTiptapExtensionApi } from '../../extensions/types.js'; +import { css, customElement, html, property, state, when } from '@umbraco-cms/backoffice/external/lit'; import { loadManifestApi } from '@umbraco-cms/backoffice/extension-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { @@ -13,6 +13,7 @@ import { Editor, Gapcursor, HardBreak, + Heading, History, HorizontalRule, Italic, @@ -34,7 +35,12 @@ import './tiptap-fixed-menu.element.js'; import './tiptap-hover-menu.element.js'; @customElement('umb-input-tiptap') -export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, undefined) { +export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement) { + #requiredExtensions = [Document, Dropcursor, Gapcursor, HardBreak, History, Paragraph, Text]; + + @state() + private _extensions: Array = []; + @property({ attribute: false }) configuration?: UmbPropertyEditorConfigCollection; @@ -44,9 +50,6 @@ export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, un @property({ type: Boolean, reflect: true }) readonly = false; - @state() - private _extensions: Array = []; - @state() private _editor!: Editor; @@ -58,7 +61,6 @@ export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, un await new Promise((resolve) => { this.observe(umbExtensionsRegistry.byType('tiptapExtension'), async (manifests) => { this._extensions = []; - for (const manifest of manifests) { if (manifest.api) { const extension = await loadManifestApi(manifest.api); @@ -67,38 +69,29 @@ export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, un } } } - - this.requestUpdate('_extensions'); - resolve(); }); }); } async #loadEditor() { + const element = this.shadowRoot?.querySelector('#editor'); if (!element) return; - const extensions = this._extensions.map((ext) => ext.getExtensions()).flat(); + const extensions = this._extensions.map((ext) => ext.getTiptapExtensions()).flat(); this._editor = new Editor({ element: element, editable: !this.readonly, extensions: [ - // REQUIRED EXTENSIONS START - Document, - Dropcursor, - Gapcursor, - HardBreak, - History, - Paragraph, - Text, - // REQUIRED EXTENSIONS END + ...this.#requiredExtensions, Blockquote, Bold, BulletList, Code, CodeBlock, + Heading, HorizontalRule, Italic, Link.configure({ openOnClick: false }), @@ -111,7 +104,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, un Underline, ...extensions, ], - content: this.value?.toString(), + content: this.value, onUpdate: ({ editor }) => { this.value = editor.getHTML(); this.dispatchEvent(new UmbChangeEvent()); @@ -120,13 +113,18 @@ export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement, un } override render() { - if (!this._extensions?.length) return html``; return html` - - + ${when( + !this._editor && !this._extensions?.length, + () => html``, + () => html` + + + `, + )}
    `; } diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 3675670ab3..1567febabb 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -1,4 +1,5 @@ -import type { UmbTiptapExtensionBase, UmbTiptapToolbarButton } from '../../extensions/types.js'; +import type { UmbTiptapToolbarButton } from '../../extensions/types.js'; +import type { ManifestTiptapExtension } from '../../extensions/tiptap-extension.js'; import * as icons from './icons.js'; import { css, customElement, html, property, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @@ -153,18 +154,10 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { } #editor?: Editor; - @property({ attribute: false }) - extensions: Array = []; - #onUpdate = () => { this.requestUpdate(); }; - protected override firstUpdated() { - const buttons = this.extensions.flatMap((ext) => ext.getToolbarButtons()); - this.actions.push(...buttons); - } - override render() { return html` ${this.actions.map( @@ -181,6 +174,11 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { `, )} + !!ext.kind || !!ext.element} + .elementProps=${{ editor: this.editor }}> + `; } diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index 7ab482cb77..60dedc1240 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -138,11 +138,13 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U } override render() { - return html``; + return html` + + `; } } From 23730bc34de1708d068b3a1915f9011ba244cf55 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Thu, 19 Sep 2024 08:28:34 +0100 Subject: [PATCH 047/241] Removed unused `extensions` attribute --- .../tiptap/components/input-tiptap/input-tiptap.element.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 1aca063f49..4bb0a53188 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -75,7 +75,6 @@ export class UmbInputTiptapElement extends UmbFormControlMixin html``, () => html` - + `, )}
    From c8e2ec6c845d841c9f29e9329f752d1013c30478 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:22:10 +0200 Subject: [PATCH 048/241] comment --- .../property-editor-ui-tiptap-toolbar-configuration.element.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index b273529074..cb9dc749f5 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -88,7 +88,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement #selectedValues: string[] = []; - #hoveredDropzone: HTMLElement | null = null; + #hoveredDropzone: HTMLElement | null = null; // Will be used to sort extensions in a group in the toolbar protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); @@ -178,6 +178,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement .find((v) => v instanceof HTMLElement && v.classList.contains('selected-group') && v.hasAttribute('dropzone')); this.#hoveredDropzone = (dropzone as HTMLElement) || null; + console.log('hovered dropzone', this.#hoveredDropzone); }; #onDrop = (event: DragEvent) => { From 3fb0bbee8e43f0d91a86c147c4fbe2db27660c4e Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:24:47 +0200 Subject: [PATCH 049/241] change name to category --- ...ui-tiptap-toolbar-configuration.element.ts | 4 +- .../property-editors/tiptap/manifests.ts | 66 +++++++++---------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index cb9dc749f5..6dc6643142 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -17,7 +17,7 @@ type ToolbarConfig = { label: string; icon?: string; selected: boolean; - group: string; + category: string; }; type ToolbarItems = Array<{ @@ -101,7 +101,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement }); const grouped = this._toolbarConfig.reduce((acc: any, item) => { - const group = item.group || 'miscellaneous'; // Assign to "miscellaneous" if no group + const group = item.category || 'miscellaneous'; // Assign to "miscellaneous" if no group if (!acc[group]) { acc[group] = []; diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index d1a5464205..9110cbbd33 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -24,59 +24,59 @@ export const manifests: Array = [ alias: 'toolbar', value: [ // Clipboard Group - { alias: 'undo', label: 'Undo', icon: 'undo', group: 'clipboard' }, - { alias: 'redo', label: 'Redo', icon: 'redo', group: 'clipboard' }, - { alias: 'cut', label: 'Cut', icon: 'cut', group: 'clipboard' }, - { alias: 'copy', label: 'Copy', icon: 'copy', group: 'clipboard' }, - { alias: 'paste', label: 'Paste', icon: 'paste', group: 'clipboard' }, + { alias: 'undo', label: 'Undo', icon: 'undo', category: 'clipboard' }, + { alias: 'redo', label: 'Redo', icon: 'redo', category: 'clipboard' }, + { alias: 'cut', label: 'Cut', icon: 'cut', category: 'clipboard' }, + { alias: 'copy', label: 'Copy', icon: 'copy', category: 'clipboard' }, + { alias: 'paste', label: 'Paste', icon: 'paste', category: 'clipboard' }, // Formatting Group - { alias: 'bold', label: 'Bold', icon: 'bold', group: 'formatting' }, - { alias: 'italic', label: 'Italic', icon: 'italic', group: 'formatting' }, - { alias: 'underline', label: 'Underline', icon: 'underline', group: 'formatting' }, - { alias: 'strikethrough', label: 'Strikethrough', icon: 'strike-through', group: 'formatting' }, - { alias: 'removeformat', label: 'Remove format', icon: 'remove-formatting', group: 'formatting' }, + { alias: 'bold', label: 'Bold', icon: 'bold', category: 'formatting' }, + { alias: 'italic', label: 'Italic', icon: 'italic', category: 'formatting' }, + { alias: 'underline', label: 'Underline', icon: 'underline', category: 'formatting' }, + { alias: 'strikethrough', label: 'Strikethrough', icon: 'strike-through', category: 'formatting' }, + { alias: 'removeformat', label: 'Remove format', icon: 'remove-formatting', category: 'formatting' }, // Color Group - { alias: 'forecolor', label: 'Text color', icon: 'text-color', group: 'color' }, - { alias: 'backcolor', label: 'Background color', icon: 'highlight-bg-color', group: 'color' }, + { alias: 'forecolor', label: 'Text color', icon: 'text-color', category: 'color' }, + { alias: 'backcolor', label: 'Background color', icon: 'highlight-bg-color', category: 'color' }, // Alignment Group - { alias: 'alignleft', label: 'Align left', icon: 'align-left', group: 'alignment' }, - { alias: 'aligncenter', label: 'Align center', icon: 'align-center', group: 'alignment' }, - { alias: 'alignright', label: 'Align right', icon: 'align-right', group: 'alignment' }, - { alias: 'alignjustify', label: 'Justify justify', icon: 'align-justify', group: 'alignment' }, + { alias: 'alignleft', label: 'Align left', icon: 'align-left', category: 'alignment' }, + { alias: 'aligncenter', label: 'Align center', icon: 'align-center', category: 'alignment' }, + { alias: 'alignright', label: 'Align right', icon: 'align-right', category: 'alignment' }, + { alias: 'alignjustify', label: 'Justify justify', icon: 'align-justify', category: 'alignment' }, // List Group - { alias: 'bullist', label: 'Bullet list', icon: 'unordered-list', group: 'list' }, - { alias: 'numlist', label: 'Numbered list', icon: 'ordered-list', group: 'list' }, + { alias: 'bullist', label: 'Bullet list', icon: 'unordered-list', category: 'list' }, + { alias: 'numlist', label: 'Numbered list', icon: 'ordered-list', category: 'list' }, // Indentation Group - { alias: 'outdent', label: 'Outdent', icon: 'outdent', group: 'indentation' }, - { alias: 'indent', label: 'Indent', icon: 'indent', group: 'indentation' }, + { alias: 'outdent', label: 'Outdent', icon: 'outdent', category: 'indentation' }, + { alias: 'indent', label: 'Indent', icon: 'indent', category: 'indentation' }, // Insert Elements Group - { alias: 'anchor', label: 'Anchor', icon: 'bookmark', group: 'insert' }, - { alias: 'table', label: 'Table', icon: 'table', group: 'insert' }, - { alias: 'hr', label: 'Horizontal rule', icon: 'horizontal-rule', group: 'insert' }, - { alias: 'charmap', label: 'Character map', icon: 'insert-character', group: 'insert' }, + { alias: 'anchor', label: 'Anchor', icon: 'bookmark', category: 'insert' }, + { alias: 'table', label: 'Table', icon: 'table', category: 'insert' }, + { alias: 'hr', label: 'Horizontal rule', icon: 'horizontal-rule', category: 'insert' }, + { alias: 'charmap', label: 'Character map', icon: 'insert-character', category: 'insert' }, // Direction Group - { alias: 'rtl', label: 'Right to left', icon: 'rtl', group: 'direction' }, - { alias: 'ltr', label: 'Left to right', icon: 'ltr', group: 'direction' }, + { alias: 'rtl', label: 'Right to left', icon: 'rtl', category: 'direction' }, + { alias: 'ltr', label: 'Left to right', icon: 'ltr', category: 'direction' }, // Text Transformation Group - { alias: 'subscript', label: 'Subscript', icon: 'subscript', group: 'text-transformation' }, - { alias: 'superscript', label: 'Superscript', icon: 'superscript', group: 'text-transformation' }, + { alias: 'subscript', label: 'Subscript', icon: 'subscript', category: 'text-transformation' }, + { alias: 'superscript', label: 'Superscript', icon: 'superscript', category: 'text-transformation' }, // Styling and Font Group - { alias: 'styles', label: 'Style select', icon: 'permanent-pen', group: 'styling' }, - { alias: 'fontname', label: 'Font select', icon: 'text-color', group: 'styling' }, - { alias: 'fontsize', label: 'Font size', icon: 'text-color', group: 'styling' }, + { alias: 'styles', label: 'Style select', icon: 'permanent-pen', category: 'styling' }, + { alias: 'fontname', label: 'Font select', icon: 'text-color', category: 'styling' }, + { alias: 'fontsize', label: 'Font size', icon: 'text-color', category: 'styling' }, // Block Element Group - { alias: 'blockquote', label: 'Blockquote', icon: 'quote', group: 'block-elements' }, - { alias: 'formatblock', label: 'Format block', icon: 'format', group: 'block-elements' }, + { alias: 'blockquote', label: 'Blockquote', icon: 'quote', category: 'block-elements' }, + { alias: 'formatblock', label: 'Format block', icon: 'format', category: 'block-elements' }, ], }, ], From a0e61142e7d058f469332a11e796221179efdb04 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:27:04 +0200 Subject: [PATCH 050/241] remove unused config --- .../property-editors/tiptap/manifests.ts | 31 +------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 9110cbbd33..471dd9dd5e 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -95,37 +95,8 @@ export const manifests: Array = [ propertyEditorUiAlias: 'Umb.PropertyEditorUi.Integer', weight: 30, }, - { - alias: 'mode', - label: 'Mode - NOT IMPLEMENTED', - description: 'Select the mode for the editor', - propertyEditorUiAlias: 'Umb.PropertyEditorUi.RadioButtonList', - config: [{ alias: 'items', value: ['Classic', 'Inline'] }], - weight: 50, - }, - ], - defaultData: [ - // { - // alias: 'toolbar', - // value: [ - // 'styles', - // 'bold', - // 'italic', - // 'alignleft', - // 'aligncenter', - // 'alignright', - // 'bullist', - // 'numlist', - // 'outdent', - // 'indent', - // 'sourcecode', - // 'link', - // ], - // }, - // { alias: 'mode', value: 'Classic' }, - // { alias: 'maxWidth', value: 800 }, - // { alias: 'maxHeight', value: 500 }, ], + defaultData: [], }, }, }, From 3ed0bdb6d6ad21471c1c7f268f698aa1bf99dbce Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:42:43 +0200 Subject: [PATCH 051/241] toolbar styling --- ...ui-tiptap-toolbar-configuration.element.ts | 88 +++++++++++-------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 6dc6643142..0abd2dc95e 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -175,7 +175,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement #onDragEnter = (event: DragEvent) => { const dropzone = event .composedPath() - .find((v) => v instanceof HTMLElement && v.classList.contains('selected-group') && v.hasAttribute('dropzone')); + .find((v) => v instanceof HTMLElement && v.classList.contains('toolbar-group') && v.hasAttribute('dropzone')); this.#hoveredDropzone = (dropzone as HTMLElement) || null; console.log('hovered dropzone', this.#hoveredDropzone); @@ -187,7 +187,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement const groupElement = event .composedPath() .find( - (v) => v instanceof HTMLElement && v.classList.contains('selected-group') && v.hasAttribute('dropzone'), + (v) => v instanceof HTMLElement && v.classList.contains('toolbar-group') && v.hasAttribute('dropzone'), ) as HTMLElement; if (!groupElement) return; @@ -215,17 +215,17 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement }; #renderRow(row: ToolbarConfig[][], rowIndex: number) { - return html`
    + return html`
    ${row.map((group, index) => { return this.#renderGroup(group, index, rowIndex); })} - this.#addGroup(rowIndex)}>+ + this.#addGroup(rowIndex)}>Add group
    `; } #renderGroup(group: ToolbarConfig[], groupIndex: number, rowIndex: number) { return html`
    +
    ${repeat(this._selectedValuesNew, (row, index) => this.#renderRow(row, index))} - this.#addRow()}>+ + this.#addRow()}>Add row
    ${repeat( this._toolbarItems, (category) => html` -

    - ${category.name} - Hide in toolbar -

    - ${repeat( - category.items, - (item) => - html`
    - this.#onExtensionSelect(item)} - > - ${item.label} - -
    `, - )} +
    +

    + ${category.name} + Hide in toolbar +

    + ${repeat( + category.items, + (item) => + html`
    + this.#onExtensionSelect(item)} + > + ${item.label} + +
    `, + )} +
    `, )} +
    `; } @@ -300,38 +303,49 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement --uui-button-border-color: var(--uui-color-selected); --uui-button-border-width: 2px; } - .selected-bar { + .toolbar { display: flex; flex-direction: column; gap: 12px; } - .selected-row { + .toolbar-row { display: flex; gap: 18px; } - .selected-group { + .toolbar-group { padding: 6px; min-width: 12px; background-color: var(--uui-color-surface-alt); border-radius: var(--uui-border-radius); display: flex; gap: 6px; - position: relative; + min-width: 24px; } .extensions { - /* display: grid; - grid-template-columns: repeat(auto-fit, 36px); - gap: 10px; */ - max-width: 400px; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 16px; + margin-top: 16px; } .extension-item { display: grid; grid-template-columns: 36px 1fr auto; grid-template-rows: 1fr; + align-items: center; + gap: 9px; + } + .category { + background-color: var(--uui-color-surface-alt); + padding: 12px; + border-radius: 6px; + display: flex; + flex-direction: column; + gap: 6px; + border: 1px solid var(--uui-color-border); } .category-name { grid-column: 1 / -1; - margin-bottom: 0; + margin: 0; font-weight: bold; display: flex; } From 62c80bae334d08a6afa9d56364f96c1efad1c35f Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:53:30 +0200 Subject: [PATCH 052/241] remove hover menu --- .../rte/tiptap/components/input-tiptap/input-tiptap.element.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index a8e9a0ed09..2041b8bcec 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -122,7 +122,6 @@ export class UmbInputTiptapElement extends UmbFormControlMixin html``, () => html` - `, )} From 1c1a33ec020a0df63ec0b65179783818be9e47e4 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 19 Sep 2024 11:08:12 +0200 Subject: [PATCH 053/241] remove selectedValuesNew from value update --- ...ui-tiptap-toolbar-configuration.element.ts | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 0abd2dc95e..1014ceda82 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -47,27 +47,6 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement } } - this.#selectedValues.forEach((alias) => { - const row = Math.floor(Math.random() * 2); - const group = Math.floor(Math.random() * 2); - const item = this._toolbarConfig.find((value) => value.alias === alias); - - if (!item) return; - - // Ensure the row exists - if (!this._selectedValuesNew[row]) { - this._selectedValuesNew[row] = []; // Initialize the row if it doesn't exist - } - - // Ensure the group exists within the row - if (!this._selectedValuesNew[row][group]) { - this._selectedValuesNew[row][group] = []; // Initialize the group if it doesn't exist - } - - // Add the item to the selectedValuesNew array - this._selectedValuesNew[row][group].push(item); - }); - this.requestUpdate('#selectedValuesNew'); } get value(): string[] { From 6fa94c2c1ca2eecb349ce5fe0dd959f29cc0a0bb Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 11:48:06 +0200 Subject: [PATCH 054/241] chore: optimise file manager class --- .../core/temporary-file/temporary-file-manager.class.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/packages/core/temporary-file/temporary-file-manager.class.ts b/src/packages/core/temporary-file/temporary-file-manager.class.ts index 9fcf08824d..4157d4cbea 100644 --- a/src/packages/core/temporary-file/temporary-file-manager.class.ts +++ b/src/packages/core/temporary-file/temporary-file-manager.class.ts @@ -20,16 +20,11 @@ export interface UmbTemporaryFileModel { export class UmbTemporaryFileManager< UploadableItem extends UmbTemporaryFileModel = UmbTemporaryFileModel, > extends UmbControllerBase { - #temporaryFileRepository; + #temporaryFileRepository = new UmbTemporaryFileRepository(this._host); #queue = new UmbArrayState([], (item) => item.temporaryUnique); public readonly queue = this.#queue.asObservable(); - constructor(host: UmbControllerHost) { - super(host); - this.#temporaryFileRepository = new UmbTemporaryFileRepository(host); - } - async uploadOne(uploadableItem: UploadableItem): Promise { this.#queue.setValue([]); From fb11b67098e0dcc964242ac2478ba7464ad5d9c3 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 11:48:19 +0200 Subject: [PATCH 055/241] fix: give UmbImage an extension name --- src/external/tiptap/extensions/tiptap-umb-image.extension.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/external/tiptap/extensions/tiptap-umb-image.extension.ts b/src/external/tiptap/extensions/tiptap-umb-image.extension.ts index 2f0eceedd4..6db61379c9 100644 --- a/src/external/tiptap/extensions/tiptap-umb-image.extension.ts +++ b/src/external/tiptap/extensions/tiptap-umb-image.extension.ts @@ -1,6 +1,7 @@ import Image from '@tiptap/extension-image'; export const UmbImage = Image.extend({ + name: 'umbImage', addAttributes() { return { ...this.parent?.(), From dc776449030eaa778053f4cb55a9f298f7371dca Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 11:48:53 +0200 Subject: [PATCH 056/241] feat: adds a media upload extension supporting drag and drop --- .../rte/tiptap/extensions/manifests.ts | 8 + .../tiptap-media-upload.extension.ts | 144 ++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index 8afb03e629..7e56bfe4f9 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -22,6 +22,14 @@ const extensions: Array = [ api: () => import('./tiptap-image.extension.js'), meta: {}, }, + { + type: 'tiptapExtension', + alias: 'Umb.Tiptap.MediaUpload', + name: 'Media Upload Tiptap Extension', + weight: 900, + api: () => import('./tiptap-media-upload.extension.js'), + meta: {}, + }, { type: 'tiptapExtension', kind: 'button', diff --git a/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts b/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts new file mode 100644 index 0000000000..d0cf48d79e --- /dev/null +++ b/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts @@ -0,0 +1,144 @@ +import { UmbTiptapExtensionApi } from './types.js'; +import { + TemporaryFileStatus, + UmbTemporaryFileManager, + type UmbTemporaryFileModel, +} from '@umbraco-cms/backoffice/temporary-file'; +import { type Editor, UmbImage } from '@umbraco-cms/backoffice/external/tiptap'; +import { UmbId } from '@umbraco-cms/backoffice/id'; +import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi { + /** + * TODO: Implement this method when the configuration is available to extensions + * @returns The maximum width of uploaded images + */ + get maxWidth() { + return 500; + } + + /** + * TODO: Implement this method when the configuration is available to extensions + * @returns The allowed file types for uploads + */ + get allowedFileTypes() { + return ['image/jpeg', 'image/png', 'image/gif']; + } + + #manager = new UmbTemporaryFileManager(this); + #notificationContext?: typeof UMB_NOTIFICATION_CONTEXT.TYPE; + + constructor(host: UmbControllerHost) { + super(host); + this.consumeContext(UMB_NOTIFICATION_CONTEXT, (instance) => { + this.#notificationContext = instance; + }); + } + + getTiptapExtensions() { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + return [ + UmbImage.extend({ + name: 'umbMediaUpload', + addAttributes() { + return { + ...this.parent?.(), + dataTmpimg: { default: null }, + }; + }, + onCreate() { + this.parent?.(); + const host = this.editor.view.dom; + + host.addEventListener('dragover', (event) => { + event.preventDefault(); + console.log('umb-media-upload.dragover', event); + }); + + host.addEventListener('drop', (event) => { + event.preventDefault(); + console.log('umb-media-upload.drop', event); + + const files = event.dataTransfer?.files; + if (!files) return; + + self.#uploadTemporaryFile(files, this.editor); + }); + console.log('umb-media-upload.onCreate'); + }, + }), + ]; + } + + async #uploadTemporaryFile(files: FileList, editor: Editor) { + const filteredFiles = this.#filterFiles(files); + const fileModels = filteredFiles.map((file) => this.#mapFileToTemporaryFile(file)); + + this.dispatchEvent(new CustomEvent('rte.file.uploading', { bubbles: true, detail: fileModels })); + + const uploads = await this.#manager.upload(fileModels); + + uploads.forEach((upload) => { + if (upload.status !== TemporaryFileStatus.SUCCESS) { + this.#notificationContext?.peek('danger', { + data: { + headline: 'Rich Text Editor', + message: `Failed to upload file ${upload.file.name}`, + }, + }); + return; + } + + editor + .chain() + .focus() + .setImage({ + src: URL.createObjectURL(upload.file), + width: this.maxWidth.toString(), + dataTmpimg: upload.temporaryUnique, + }) + .run(); + }); + + this.dispatchEvent(new CustomEvent('rte.file.uploaded', { bubbles: true, detail: uploads })); + } + + #mapFileToTemporaryFile(file: File): UmbTemporaryFileModel { + return { + file, + temporaryUnique: UmbId.new(), + }; + } + + #filterFiles(files: FileList): File[] { + return Array.from(files).filter((file) => this.allowedFileTypes.includes(file.type)); + } +} + +declare module '@tiptap/core' { + interface Commands { + umbMediaUpload: { + /** + * Add an image + * @param options The image attributes + * @example + * editor + * .commands + * .setImage({ src: 'https://tiptap.dev/logo.png', alt: 'tiptap', title: 'tiptap logo' }) + */ + setImage: (options: { + src: string; + alt?: string; + title?: string; + width?: string; + height?: string; + loading?: string; + srcset?: string; + sizes?: string; + dataTmpimg?: string; + }) => ReturnType; + }; + } +} From d3710f3574a40eebe1e26a0c86866fcb1c007e6d Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 13:07:38 +0200 Subject: [PATCH 057/241] chore: add mock handlers for "temporary-file" --- src/mocks/browser-handlers.ts | 2 ++ src/mocks/handlers/temporary-file/index.ts | 1 + .../temporary-file/temporary-file.handlers.ts | 12 ++++++++++++ 3 files changed, 15 insertions(+) create mode 100644 src/mocks/handlers/temporary-file/index.ts create mode 100644 src/mocks/handlers/temporary-file/temporary-file.handlers.ts diff --git a/src/mocks/browser-handlers.ts b/src/mocks/browser-handlers.ts index c8847d1eef..f3a3749350 100644 --- a/src/mocks/browser-handlers.ts +++ b/src/mocks/browser-handlers.ts @@ -35,6 +35,7 @@ import { handlers as userHandlers } from './handlers/user/index.js'; import * as manifestsHandlers from './handlers/manifests.handlers.js'; import * as serverHandlers from './handlers/server.handlers.js'; import { handlers as documentBlueprintHandlers } from './handlers/document-blueprint/index.js'; +import { handlers as temporaryFileHandlers } from './handlers/temporary-file/index.js'; const handlers = [ ...configHandlers, @@ -72,6 +73,7 @@ const handlers = [ ...userGroupsHandlers, ...userHandlers, ...documentBlueprintHandlers, + ...temporaryFileHandlers, ...serverHandlers.serverInformationHandlers, ]; diff --git a/src/mocks/handlers/temporary-file/index.ts b/src/mocks/handlers/temporary-file/index.ts new file mode 100644 index 0000000000..57daf6e209 --- /dev/null +++ b/src/mocks/handlers/temporary-file/index.ts @@ -0,0 +1 @@ +export { handlers } from './temporary-file.handlers.js'; diff --git a/src/mocks/handlers/temporary-file/temporary-file.handlers.ts b/src/mocks/handlers/temporary-file/temporary-file.handlers.ts new file mode 100644 index 0000000000..a4cb1c7692 --- /dev/null +++ b/src/mocks/handlers/temporary-file/temporary-file.handlers.ts @@ -0,0 +1,12 @@ +import { rest } from 'msw'; +import { umbracoPath } from '@umbraco-cms/backoffice/utils'; +import type { PostTemporaryFileResponse } from '@umbraco-cms/backoffice/external/backend-api'; +import { UmbId } from '@umbraco-cms/backoffice/id'; + +const UMB_SLUG = 'temporary-file'; + +export const handlers = [ + rest.post(umbracoPath(`/${UMB_SLUG}`), async (_req, res, ctx) => { + return res(ctx.status(201), ctx.text(UmbId.new())); + }), +]; From 2fa644acd9a537e8939c306116c1d7f33a3f4f4d Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 13:18:38 +0200 Subject: [PATCH 058/241] chore: add delay to file handler --- src/mocks/handlers/temporary-file/temporary-file.handlers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mocks/handlers/temporary-file/temporary-file.handlers.ts b/src/mocks/handlers/temporary-file/temporary-file.handlers.ts index a4cb1c7692..0c1eeb3c05 100644 --- a/src/mocks/handlers/temporary-file/temporary-file.handlers.ts +++ b/src/mocks/handlers/temporary-file/temporary-file.handlers.ts @@ -7,6 +7,6 @@ const UMB_SLUG = 'temporary-file'; export const handlers = [ rest.post(umbracoPath(`/${UMB_SLUG}`), async (_req, res, ctx) => { - return res(ctx.status(201), ctx.text(UmbId.new())); + return res(ctx.delay(), ctx.status(201), ctx.text(UmbId.new())); }), ]; From 4779c15d84f590d530f15121a4d4b2226ec33e2c Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 13:18:58 +0200 Subject: [PATCH 059/241] feat: add localization and correct name of data-tmpimg --- .../extensions/tiptap-media-upload.extension.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts b/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts index d0cf48d79e..bdd1579ea2 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts @@ -8,6 +8,7 @@ import { type Editor, UmbImage } from '@umbraco-cms/backoffice/external/tiptap'; import { UmbId } from '@umbraco-cms/backoffice/id'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi { /** @@ -28,6 +29,7 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi #manager = new UmbTemporaryFileManager(this); #notificationContext?: typeof UMB_NOTIFICATION_CONTEXT.TYPE; + #localize = new UmbLocalizationController(this); constructor(host: UmbControllerHost) { super(host); @@ -45,7 +47,7 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi addAttributes() { return { ...this.parent?.(), - dataTmpimg: { default: null }, + 'data-tmpimg': { default: null }, }; }, onCreate() { @@ -76,7 +78,7 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi const filteredFiles = this.#filterFiles(files); const fileModels = filteredFiles.map((file) => this.#mapFileToTemporaryFile(file)); - this.dispatchEvent(new CustomEvent('rte.file.uploading', { bubbles: true, detail: fileModels })); + this.dispatchEvent(new CustomEvent('rte.file.uploading', { composed: true, bubbles: true, detail: fileModels })); const uploads = await this.#manager.upload(fileModels); @@ -84,8 +86,8 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi if (upload.status !== TemporaryFileStatus.SUCCESS) { this.#notificationContext?.peek('danger', { data: { - headline: 'Rich Text Editor', - message: `Failed to upload file ${upload.file.name}`, + headline: upload.file.name, + message: this.#localize.term('errors_dissallowedMediaType'), }, }); return; @@ -97,12 +99,12 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi .setImage({ src: URL.createObjectURL(upload.file), width: this.maxWidth.toString(), - dataTmpimg: upload.temporaryUnique, + 'data-tmpimg': upload.temporaryUnique, }) .run(); }); - this.dispatchEvent(new CustomEvent('rte.file.uploaded', { bubbles: true, detail: uploads })); + this.dispatchEvent(new CustomEvent('rte.file.uploaded', { composed: true, bubbles: true, detail: uploads })); } #mapFileToTemporaryFile(file: File): UmbTemporaryFileModel { @@ -137,7 +139,7 @@ declare module '@tiptap/core' { loading?: string; srcset?: string; sizes?: string; - dataTmpimg?: string; + 'data-tmpimg'?: string; }) => ReturnType; }; } From 199daa1f96be923e7f11931c8e47740f186a8f58 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 13:32:25 +0200 Subject: [PATCH 060/241] chore: remove debug data --- .../rte/tiptap/extensions/tiptap-media-upload.extension.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts b/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts index bdd1579ea2..c565dbb2b8 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts @@ -55,20 +55,18 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi const host = this.editor.view.dom; host.addEventListener('dragover', (event) => { + // Required to allow drop events event.preventDefault(); - console.log('umb-media-upload.dragover', event); }); host.addEventListener('drop', (event) => { event.preventDefault(); - console.log('umb-media-upload.drop', event); const files = event.dataTransfer?.files; if (!files) return; self.#uploadTemporaryFile(files, this.editor); }); - console.log('umb-media-upload.onCreate'); }, }), ]; From ce99b098eec17894f79569acc38a4b3f544b5b61 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:49:53 +0200 Subject: [PATCH 061/241] feat: make the datatype configuration available to extensions --- .../components/input-tiptap/input-tiptap.element.ts | 6 ++++-- src/packages/rte/tiptap/extensions/types.ts | 12 +++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 4bb0a53188..1dbd36c8d8 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -42,7 +42,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin = []; @property({ attribute: false }) - configuration?: UmbPropertyEditorConfigCollection; + configuration!: UmbPropertyEditorConfigCollection; /** * Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content. @@ -78,7 +78,9 @@ export class UmbInputTiptapElement extends UmbFormControlMixin ext.getTiptapExtensions()).flat(); + const extensions = this._extensions + .map((ext) => ext.getTiptapExtensions({ configuration: this.configuration })) + .flat(); this._editor = new Editor({ element: element, diff --git a/src/packages/rte/tiptap/extensions/types.ts b/src/packages/rte/tiptap/extensions/types.ts index 5a2d19ccd4..7881c11c05 100644 --- a/src/packages/rte/tiptap/extensions/types.ts +++ b/src/packages/rte/tiptap/extensions/types.ts @@ -3,6 +3,7 @@ import type { Editor, Extension, Mark, Node } from '@umbraco-cms/backoffice/exte import type { TemplateResult } from '@umbraco-cms/backoffice/external/lit'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; export abstract class UmbTiptapExtensionApi extends UmbControllerBase implements UmbApi { constructor(host: UmbControllerHost) { @@ -12,7 +13,16 @@ export abstract class UmbTiptapExtensionApi extends UmbControllerBase implements // eslint-disable-next-line @typescript-eslint/no-unused-vars execute(editor?: Editor) {} - abstract getTiptapExtensions(): Array; + abstract getTiptapExtensions(args?: UmbTiptapExtensionArgs): Array; +} + +export interface UmbTiptapExtensionArgs { + /** + * The data type configuration for the property editor that the editor is used for. + * You can populate this manually if you are using the editor outside of a property editor with the {@link UmbPropertyEditorConfigCollection} object. + * @remark This is only available when the editor is used in a property editor or populated manually. + */ + configuration?: UmbPropertyEditorConfigCollection; } export interface UmbTiptapToolbarButton { From 8359211f5819d2725dc52e3c2f985f7fc7ff7d77 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:50:18 +0200 Subject: [PATCH 062/241] feat: calculate image size for files added to the editor so that we can set `width` and `height` --- .../tiptap-media-upload.extension.ts | 79 +++++++++++++++---- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts b/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts index c565dbb2b8..7f4e34a2c9 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts @@ -1,4 +1,4 @@ -import { UmbTiptapExtensionApi } from './types.js'; +import { UmbTiptapExtensionApi, type UmbTiptapExtensionArgs } from './types.js'; import { TemporaryFileStatus, UmbTemporaryFileManager, @@ -9,27 +9,31 @@ import { UmbId } from '@umbraco-cms/backoffice/id'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; +import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi { + #configuration?: UmbPropertyEditorConfigCollection; + /** - * TODO: Implement this method when the configuration is available to extensions - * @returns The maximum width of uploaded images + * @returns {number} The maximum width of uploaded images */ - get maxWidth() { - return 500; + get maxWidth(): number { + const maxImageSize = parseInt(this.#configuration?.getValueByAlias('maxImageSize') ?? '', 10); + return isNaN(maxImageSize) ? 500 : maxImageSize; } /** - * TODO: Implement this method when the configuration is available to extensions - * @returns The allowed file types for uploads + * @returns {Array} The allowed mime types for uploads */ - get allowedFileTypes() { - return ['image/jpeg', 'image/png', 'image/gif']; + get allowedFileTypes(): string[] { + return ( + this.#configuration?.getValueByAlias('allowedFileTypes') ?? ['image/jpeg', 'image/png', 'image/gif'] + ); } #manager = new UmbTemporaryFileManager(this); - #notificationContext?: typeof UMB_NOTIFICATION_CONTEXT.TYPE; #localize = new UmbLocalizationController(this); + #notificationContext?: typeof UMB_NOTIFICATION_CONTEXT.TYPE; constructor(host: UmbControllerHost) { super(host); @@ -38,7 +42,9 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi }); } - getTiptapExtensions() { + getTiptapExtensions(args: UmbTiptapExtensionArgs) { + this.#configuration = args?.configuration; + // eslint-disable-next-line @typescript-eslint/no-this-alias const self = this; return [ @@ -72,15 +78,22 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi ]; } - async #uploadTemporaryFile(files: FileList, editor: Editor) { + /** + * Uploads the files to the server and inserts them into the editor as data URIs. + * The server will replace the data URI with a proper URL when the content is saved. + * @param {FileList} files The files to upload. + * @param {Editor} editor The editor to insert the images into. + */ + async #uploadTemporaryFile(files: FileList, editor: Editor): Promise { const filteredFiles = this.#filterFiles(files); const fileModels = filteredFiles.map((file) => this.#mapFileToTemporaryFile(file)); this.dispatchEvent(new CustomEvent('rte.file.uploading', { composed: true, bubbles: true, detail: fileModels })); const uploads = await this.#manager.upload(fileModels); + const maxImageSize = this.maxWidth; - uploads.forEach((upload) => { + uploads.forEach(async (upload) => { if (upload.status !== TemporaryFileStatus.SUCCESS) { this.#notificationContext?.peek('danger', { data: { @@ -91,12 +104,21 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi return; } + let { width, height } = await this.#imageSize(URL.createObjectURL(upload.file)); + + if (maxImageSize > 0 && width > maxImageSize) { + const ratio = maxImageSize / width; + width = maxImageSize; + height = Math.round(height * ratio); + } + editor .chain() .focus() .setImage({ src: URL.createObjectURL(upload.file), - width: this.maxWidth.toString(), + width: width.toString(), + height: height.toString(), 'data-tmpimg': upload.temporaryUnique, }) .run(); @@ -115,6 +137,35 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi #filterFiles(files: FileList): File[] { return Array.from(files).filter((file) => this.allowedFileTypes.includes(file.type)); } + + /** + * Get the dimensions of an image from a URL. + * @param {string} url The URL of the image. It can be a local file (blob url) or a remote file. + * @returns {Promise<{width: number, height: number}>} The width and height of the image as downloaded from the URL. + */ + #imageSize(url: string): Promise<{ width: number; height: number }> { + const img = new Image(); + + const promise = new Promise<{ width: number; height: number }>((resolve, reject) => { + img.onload = () => { + // Natural size is the actual image size regardless of rendering. + // The 'normal' `width`/`height` are for the **rendered** size. + const width = img.naturalWidth; + const height = img.naturalHeight; + + // Resolve promise with the width and height + resolve({ width, height }); + }; + + // Reject promise on error + img.onerror = reject; + }); + + // Setting the source makes it start downloading and eventually call `onload` + img.src = url; + + return promise; + } } declare module '@tiptap/core' { From fc47667b9f2447f2fdfc93ea3b553ed84ec42915 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 15:28:09 +0200 Subject: [PATCH 063/241] feat: update the contents if we ever get a new value --- .../tiptap/components/input-tiptap/input-tiptap.element.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 1dbd36c8d8..3881b8164c 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -57,6 +57,12 @@ export class UmbInputTiptapElement extends UmbFormControlMixin) { + if (changedProperties.has('value') && this._editor) { + this._editor.commands.setContent(this.value, true); + } + } + async #loadExtensions() { await new Promise((resolve) => { this.observe(umbExtensionsRegistry.byType('tiptapExtension'), async (manifests) => { From a85f2f7def429242468b3caa90863b867c04be6f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 15:28:54 +0200 Subject: [PATCH 064/241] feat: add a hasChanged checker to the value to check specifically if the markup has changed --- .../property-editor-ui-tiptap.element.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index 60dedc1240..f47d165a69 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -33,7 +33,13 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U this._config = config; } - @property({ attribute: false }) + @property({ + attribute: false, + type: Object, + hasChanged: (value?: UmbRichTextEditorValueType, oldValue?: UmbRichTextEditorValueType) => { + return value?.markup !== oldValue?.markup; + }, + }) public set value(value: UmbRichTextEditorValueType | undefined) { const buildUpValue: Partial = value ? { ...value } : {}; buildUpValue.markup ??= ''; @@ -42,10 +48,7 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U buildUpValue.blocks.contentData ??= []; buildUpValue.blocks.settingsData ??= []; this._value = buildUpValue as UmbRichTextEditorValueType; - - if (this._latestMarkup !== this._value.markup) { - this._markup = this._value.markup; - } + this._markup = buildUpValue.markup; this.#managerContext.setLayouts(buildUpValue.blocks.layout[UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS] ?? []); this.#managerContext.setContents(buildUpValue.blocks.contentData); @@ -72,10 +75,8 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U blocks: { layout: {}, contentData: [], settingsData: [] }, }; - // Separate state for markup, to avoid re-rendering/re-setting the value of the Tiptap editor when the value does not really change. @state() private _markup = ''; - private _latestMarkup = ''; // The latest value gotten from the Tiptap editor. #managerContext = new UmbBlockRteManagerContext(this); #entriesContext = new UmbBlockRteEntriesContext(this); @@ -111,7 +112,6 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U #onChange(event: CustomEvent & { target: UmbInputTiptapElement }) { const value = event.target.value as string; - this._latestMarkup = value; // TODO: Validate blocks // Loop through used, to remove the classes on these. @@ -131,7 +131,7 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U this._value = { ...this._value, - markup: this._latestMarkup, + markup: value, }; this.#fireChangeEvent(); From 1349f9d97e08db8becfce34d091082b4931b37bf Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 15:40:43 +0200 Subject: [PATCH 065/241] feat: only update value when changed --- .../property-editor-ui-tiptap.element.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index f47d165a69..bf15ff95fd 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -36,7 +36,7 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U @property({ attribute: false, type: Object, - hasChanged: (value?: UmbRichTextEditorValueType, oldValue?: UmbRichTextEditorValueType) => { + hasChanged(value?: UmbRichTextEditorValueType, oldValue?: UmbRichTextEditorValueType) { return value?.markup !== oldValue?.markup; }, }) @@ -48,7 +48,11 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U buildUpValue.blocks.contentData ??= []; buildUpValue.blocks.settingsData ??= []; this._value = buildUpValue as UmbRichTextEditorValueType; - this._markup = buildUpValue.markup; + + // Only update the actual editor markup if it is not the same as the value. + if (this._markup !== buildUpValue.markup) { + this._markup = buildUpValue.markup; + } this.#managerContext.setLayouts(buildUpValue.blocks.layout[UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS] ?? []); this.#managerContext.setContents(buildUpValue.blocks.contentData); @@ -113,6 +117,11 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U #onChange(event: CustomEvent & { target: UmbInputTiptapElement }) { const value = event.target.value as string; + this._value = { + ...this._value, + markup: value, + }; + // TODO: Validate blocks // Loop through used, to remove the classes on these. /*const blockEls = div.querySelectorAll(`umb-rte-block, umb-rte-block-inline`); @@ -129,11 +138,6 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U this.#managerContext.removeOneLayout(blockLayout.contentUdi); });*/ - this._value = { - ...this._value, - markup: value, - }; - this.#fireChangeEvent(); } From 7f16ff534962982e75b3f1b6defa41f7c715315c Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 15:59:17 +0200 Subject: [PATCH 066/241] fix: assume any to avoid linting error --- .../tiptap/property-editor-ui-tiptap.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index bf15ff95fd..fff49220dc 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -145,7 +145,7 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U return html` `; From 99c5cb13d4c7e5106b619910d4ac85fb51e7f285 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:08:39 +0200 Subject: [PATCH 067/241] fix: move options back to UmbImage element to ensure everything is available when the editor loads --- .../extensions/tiptap-umb-image.extension.ts | 3 ++ .../tiptap-media-upload.extension.ts | 32 ------------------- 2 files changed, 3 insertions(+), 32 deletions(-) diff --git a/src/external/tiptap/extensions/tiptap-umb-image.extension.ts b/src/external/tiptap/extensions/tiptap-umb-image.extension.ts index 6db61379c9..5b3737576e 100644 --- a/src/external/tiptap/extensions/tiptap-umb-image.extension.ts +++ b/src/external/tiptap/extensions/tiptap-umb-image.extension.ts @@ -20,6 +20,8 @@ export const UmbImage = Image.extend({ sizes: { default: null, }, + 'data-tmpimg': { default: null }, + 'data-udi': { default: null }, }; }, }); @@ -44,6 +46,7 @@ declare module '@tiptap/core' { loading?: string; srcset?: string; sizes?: string; + 'data-tmpimg'?: string; }) => ReturnType; }; } diff --git a/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts b/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts index 7f4e34a2c9..a56d7184f7 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts @@ -50,12 +50,6 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi return [ UmbImage.extend({ name: 'umbMediaUpload', - addAttributes() { - return { - ...this.parent?.(), - 'data-tmpimg': { default: null }, - }; - }, onCreate() { this.parent?.(); const host = this.editor.view.dom; @@ -167,29 +161,3 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi return promise; } } - -declare module '@tiptap/core' { - interface Commands { - umbMediaUpload: { - /** - * Add an image - * @param options The image attributes - * @example - * editor - * .commands - * .setImage({ src: 'https://tiptap.dev/logo.png', alt: 'tiptap', title: 'tiptap logo' }) - */ - setImage: (options: { - src: string; - alt?: string; - title?: string; - width?: string; - height?: string; - loading?: string; - srcset?: string; - sizes?: string; - 'data-tmpimg'?: string; - }) => ReturnType; - }; - } -} From 3ac8db5636f76914c6a0b7b805554b0cbcf0234c Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:26:10 +0200 Subject: [PATCH 068/241] store value in another variable --- .../tiptap/property-editor-ui-tiptap.element.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index fff49220dc..fffb7fdcc5 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -50,7 +50,7 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U this._value = buildUpValue as UmbRichTextEditorValueType; // Only update the actual editor markup if it is not the same as the value. - if (this._markup !== buildUpValue.markup) { + if (this._latestMarkup !== buildUpValue.markup) { this._markup = buildUpValue.markup; } @@ -81,6 +81,7 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U @state() private _markup = ''; + private _latestMarkup = ''; #managerContext = new UmbBlockRteManagerContext(this); #entriesContext = new UmbBlockRteEntriesContext(this); @@ -117,9 +118,11 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U #onChange(event: CustomEvent & { target: UmbInputTiptapElement }) { const value = event.target.value as string; + this._latestMarkup = value; + this._value = { ...this._value, - markup: value, + markup: this._latestMarkup, }; // TODO: Validate blocks From 4ea698378c8bcf511655276ca27955b42300661e Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:26:22 +0200 Subject: [PATCH 069/241] feat: override value property to interact with tiptap --- .../input-tiptap/input-tiptap.element.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 3881b8164c..95280f1bd2 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -41,6 +41,14 @@ export class UmbInputTiptapElement extends UmbFormControlMixin = []; + @property({ type: String }) + override set value(value: string) { + this._editor?.commands.setContent(value); + } + override get value() { + return this._editor?.getHTML() ?? ''; + } + @property({ attribute: false }) configuration!: UmbPropertyEditorConfigCollection; @@ -57,12 +65,6 @@ export class UmbInputTiptapElement extends UmbFormControlMixin) { - if (changedProperties.has('value') && this._editor) { - this._editor.commands.setContent(this.value, true); - } - } - async #loadExtensions() { await new Promise((resolve) => { this.observe(umbExtensionsRegistry.byType('tiptapExtension'), async (manifests) => { @@ -112,8 +114,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin { - this.value = editor.getHTML(); + onUpdate: () => { this.dispatchEvent(new UmbChangeEvent()); }, }); From 64390a1228031e34467afc24544cd65e931b6b8c Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:31:05 +0200 Subject: [PATCH 070/241] feat: introduce a backing field for markup to be able to add markup from the beginning --- .../components/input-tiptap/input-tiptap.element.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 95280f1bd2..0c5ee68662 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -43,12 +43,19 @@ export class UmbInputTiptapElement extends UmbFormControlMixin Date: Thu, 19 Sep 2024 16:32:28 +0200 Subject: [PATCH 071/241] feat: use #markup backing field --- .../rte/tiptap/components/input-tiptap/input-tiptap.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 0c5ee68662..b1ea239182 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -120,7 +120,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin { this.dispatchEvent(new UmbChangeEvent()); }, From 9642bebba6f05515ad5ef15e7cf178d4fcb77513 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:35:03 +0200 Subject: [PATCH 072/241] feat: only calculate html on updates --- .../tiptap/components/input-tiptap/input-tiptap.element.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index b1ea239182..e5358e8387 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -43,6 +43,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin { + this.#markup = this._editor.getHTML(); this.dispatchEvent(new UmbChangeEvent()); }, }); From b37c2c2b2bd8ec9a23a03df4a74be8226131e890 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:36:15 +0200 Subject: [PATCH 073/241] chore: remove clog --- .../rte/tiptap/components/input-tiptap/input-tiptap.element.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index e5358e8387..5baeb98148 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -43,7 +43,6 @@ export class UmbInputTiptapElement extends UmbFormControlMixin Date: Thu, 19 Sep 2024 16:37:24 +0200 Subject: [PATCH 074/241] fix: allow configuration to be undefined --- .../rte/tiptap/components/input-tiptap/input-tiptap.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 5baeb98148..9d8f1e57fd 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -57,7 +57,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin Date: Thu, 19 Sep 2024 16:38:09 +0200 Subject: [PATCH 075/241] fix: use local editor --- .../tiptap/components/input-tiptap/input-tiptap.element.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 9d8f1e57fd..28946adece 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -121,8 +121,8 @@ export class UmbInputTiptapElement extends UmbFormControlMixin { - this.#markup = this._editor.getHTML(); + onUpdate: ({ editor }) => { + this.#markup = editor.getHTML(); this.dispatchEvent(new UmbChangeEvent()); }, }); From 5da5e0f8e3dc786e81441274636f074c4b4b7477 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:39:26 +0200 Subject: [PATCH 076/241] chore: sort imports --- src/packages/core/temporary-file/temporary-file-manager.class.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/packages/core/temporary-file/temporary-file-manager.class.ts b/src/packages/core/temporary-file/temporary-file-manager.class.ts index 4157d4cbea..bade3a5086 100644 --- a/src/packages/core/temporary-file/temporary-file-manager.class.ts +++ b/src/packages/core/temporary-file/temporary-file-manager.class.ts @@ -1,6 +1,5 @@ import { UmbTemporaryFileRepository } from './temporary-file.repository.js'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; ///export type TemporaryFileStatus = 'success' | 'waiting' | 'error'; From 0c5f54be79126e11c8047a46df72ad1ef7b144bc Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:41:07 +0200 Subject: [PATCH 077/241] chore: restore comments --- .../tiptap/property-editor-ui-tiptap.element.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index fffb7fdcc5..2698adfdce 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -50,8 +50,8 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U this._value = buildUpValue as UmbRichTextEditorValueType; // Only update the actual editor markup if it is not the same as the value. - if (this._latestMarkup !== buildUpValue.markup) { - this._markup = buildUpValue.markup; + if (this._latestMarkup !== this._value.markup) { + this._markup = this._value.markup; } this.#managerContext.setLayouts(buildUpValue.blocks.layout[UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS] ?? []); @@ -79,9 +79,10 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U blocks: { layout: {}, contentData: [], settingsData: [] }, }; + // Separate state for markup, to avoid re-rendering/re-setting the value of the Tiptap editor when the value does not really change. @state() private _markup = ''; - private _latestMarkup = ''; + private _latestMarkup = ''; // The latest value gotten from the Tiptap editor. #managerContext = new UmbBlockRteManagerContext(this); #entriesContext = new UmbBlockRteEntriesContext(this); From 6a2f38fed82dfbba33794b86487143785a78f3ef Mon Sep 17 00:00:00 2001 From: leekelleher Date: Wed, 18 Sep 2024 17:45:07 +0100 Subject: [PATCH 078/241] Creates extensions for core Tiptap formats - Adds icon registry for custom icons - Adds placeholder extensions for Umbraco's Embed and URL Picker features --- .../components/input-tiptap/icon.registry.ts | 81 ++++++ .../tiptap/components/input-tiptap/icons.ts | 268 ------------------ .../input-tiptap/input-tiptap.element.ts | 35 +-- .../input-tiptap/tiptap-fixed-menu.element.ts | 155 +--------- .../input-tiptap/tiptap-hover-menu.element.ts | 2 - .../extensions/core/blockquote.extension.ts | 26 ++ .../tiptap/extensions/core/bold.extension.ts | 26 ++ .../extensions/core/bullet-list.extension.ts | 26 ++ .../extensions/core/code-block.extension.ts | 27 ++ .../extensions/core/heading1.extension.ts | 26 ++ .../extensions/core/heading2.extension.ts | 26 ++ .../extensions/core/heading3.extension.ts | 26 ++ .../core/horizontal-rule.extension.ts | 26 ++ .../tiptap/extensions/core/image.extension.ts | 17 ++ .../extensions/core/italic.extension.ts | 26 ++ .../rte/tiptap/extensions/core/manifests.ts | 38 +++ .../extensions/core/ordered-list.extension.ts | 26 ++ .../extensions/core/strike.extension.ts | 26 ++ .../core/text-align-center.extension.ts | 30 ++ .../core/text-align-justify.extension.ts | 30 ++ .../core/text-align-left.extension.ts | 30 ++ .../core/text-align-right.extension.ts | 30 ++ .../extensions/core/underline.extension.ts | 26 ++ .../rte/tiptap/extensions/embed.extension.ts | 25 ++ .../rte/tiptap/extensions/manifests.ts | 28 +- ....extension.ts => mediapicker.extension.ts} | 14 + .../extensions/tiptap-image.extension.ts | 8 - .../tiptap/extensions/urlpicker.extension.ts | 41 +++ 28 files changed, 664 insertions(+), 481 deletions(-) create mode 100644 src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts delete mode 100644 src/packages/rte/tiptap/components/input-tiptap/icons.ts create mode 100644 src/packages/rte/tiptap/extensions/core/blockquote.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/bold.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/code-block.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/heading1.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/heading2.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/heading3.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/image.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/italic.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/manifests.ts create mode 100644 src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/strike.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/underline.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/embed.extension.ts rename src/packages/rte/tiptap/extensions/{tiptap-mediapicker.extension.ts => mediapicker.extension.ts} (83%) delete mode 100644 src/packages/rte/tiptap/extensions/tiptap-image.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/urlpicker.extension.ts diff --git a/src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts b/src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts new file mode 100644 index 0000000000..996fc4482b --- /dev/null +++ b/src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts @@ -0,0 +1,81 @@ +import { UmbIconRegistry } from '@umbraco-cms/backoffice/icon'; + +export class UmbTiptapIconRegistry extends UmbIconRegistry { + constructor() { + super(); + + this.defineIcon( + 'bold', + ``, + ); + + this.defineIcon( + 'italic', + ``, + ); + this.defineIcon( + 'underline', + ``, + ); + this.defineIcon( + 'strike', + ``, + ); + this.defineIcon( + 'heading1', + ``, + ); + this.defineIcon( + 'heading2', + ``, + ); + this.defineIcon( + 'heading3', + ``, + ); + this.defineIcon( + 'blockquote', + ``, + ); + this.defineIcon( + 'code-block', + ``, + ); + this.defineIcon( + 'bullet-list', + ``, + ); + this.defineIcon( + 'ordered-list', + ``, + ); + this.defineIcon( + 'horizontal-rule', + ``, + ); + this.defineIcon( + 'text-align-left', + ``, + ); + this.defineIcon( + 'text-align-center', + ``, + ); + this.defineIcon( + 'text-align-right', + ``, + ); + this.defineIcon( + 'text-align-justify', + ``, + ); + this.defineIcon( + 'link', + ``, + ); + this.defineIcon( + 'umbraco', + ``, + ); + } +} diff --git a/src/packages/rte/tiptap/components/input-tiptap/icons.ts b/src/packages/rte/tiptap/components/input-tiptap/icons.ts deleted file mode 100644 index 2676404411..0000000000 --- a/src/packages/rte/tiptap/components/input-tiptap/icons.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { html } from '@umbraco-cms/backoffice/external/lit'; - -const iconSize = '16px'; -export const bold = html` - -`; - -export const italic = html` - - - -`; - -export const underline = html` - - -`; - -export const strikethrough = html` - - - -`; -export const heading1 = html` - - - - -`; -export const heading2 = html` - - - - -`; -export const heading3 = html` - - - - - -`; -export const blockquote = html` - - - - -`; -export const code = html` - - -`; -export const bulletList = html` - - - - - - -`; -export const orderedList = html` - - - - - - -`; -export const horizontalRule = html` - - - -`; -export const alignLeft = html` - - - -`; -export const alignCenter = html` - - - -`; -export const alignRight = html` - - - -`; -export const alignJustify = html` - - - -`; - -export const link = html` - - -`; diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 4bb0a53188..095567fd41 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -3,28 +3,14 @@ import { css, customElement, html, property, state, when } from '@umbraco-cms/ba import { loadManifestApi } from '@umbraco-cms/backoffice/extension-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { - Blockquote, - Bold, - BulletList, - Code, - CodeBlock, Document, Dropcursor, Editor, Gapcursor, HardBreak, - Heading, History, - HorizontalRule, - Italic, - Link, - ListItem, - OrderedList, Paragraph, - Strike, Text, - TextAlign, - Underline, } from '@umbraco-cms/backoffice/external/tiptap'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @@ -83,26 +69,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin { this.value = editor.getHTML(); diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 1567febabb..39e75dc1b8 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -1,7 +1,6 @@ -import type { UmbTiptapToolbarButton } from '../../extensions/types.js'; import type { ManifestTiptapExtension } from '../../extensions/tiptap-extension.js'; -import * as icons from './icons.js'; -import { css, customElement, html, property, state, when } from '@umbraco-cms/backoffice/external/lit'; +import { UmbTiptapIconRegistry } from './icon.registry.js'; +import { css, customElement, html, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; @@ -10,135 +9,6 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { @property({ type: Boolean, reflect: true }) readonly = false; - @state() - actions: Array = [ - // TODO: I don't think we need a paragraph button. It's the default state. - // { - // name: 'paragraph', - // icon: html` - // - // - // `, - // command: (editor) => editor?.chain().focus().setParagraph().run(), - // }, - { - name: 'bold', - icon: icons.bold, - isActive: (editor) => editor?.isActive('bold'), - command: (editor) => editor?.chain().focus().toggleBold().run(), - }, - { - name: 'italic', - icon: icons.italic, - isActive: (editor) => editor?.isActive('italic'), - command: (editor) => editor?.chain().focus().toggleItalic().run(), - }, - { - name: 'underline', - icon: icons.underline, - isActive: (editor) => editor?.isActive('underline'), - command: (editor) => editor?.chain().focus().toggleUnderline().run(), - }, - { - name: 'strikethrough', - icon: icons.strikethrough, - isActive: (editor) => editor?.isActive('strike'), - command: (editor) => editor?.chain().focus().toggleStrike().run(), - }, - { - name: 'h1', - icon: icons.heading1, - isActive: (editor) => editor?.isActive('heading', { level: 1 }), - command: (editor) => editor?.chain().focus().toggleHeading({ level: 1 }).run(), - }, - { - name: 'h2', - icon: icons.heading2, - isActive: (editor) => editor?.isActive('heading', { level: 2 }), - command: (editor) => editor?.chain().focus().toggleHeading({ level: 2 }).run(), - }, - { - name: 'h3', - icon: icons.heading3, - isActive: (editor) => editor?.isActive('heading', { level: 3 }), - command: (editor) => editor?.chain().focus().toggleHeading({ level: 3 }).run(), - }, - { - name: 'blockquote', - icon: icons.blockquote, - isActive: (editor) => editor?.isActive('blockquote'), - command: (editor) => editor?.chain().focus().toggleBlockquote().run(), - }, - { - name: 'code', - icon: icons.code, - isActive: (editor) => editor?.isActive('codeBlock'), - command: (editor) => editor?.chain().focus().toggleCodeBlock().run(), - }, - { - name: 'bullet-list', - icon: icons.bulletList, - isActive: (editor) => editor?.isActive('bulletList'), - command: (editor) => editor?.chain().focus().toggleBulletList().run(), - }, - { - name: 'ordered-list', - icon: icons.orderedList, - isActive: (editor) => editor?.isActive('orderedList'), - command: (editor) => editor?.chain().focus().toggleOrderedList().run(), - }, - { - name: 'horizontal-rule', - icon: icons.horizontalRule, - isActive: (editor) => editor?.isActive('horizontalRule'), - command: (editor) => editor?.chain().focus().setHorizontalRule().run(), - }, - { - name: 'align-left', - icon: icons.alignLeft, - isActive: (editor) => editor?.isActive({ textAlign: 'left' }), - command: (editor) => editor?.chain().focus().setTextAlign('left').run(), - }, - { - name: 'align-center', - icon: icons.alignCenter, - isActive: (editor) => editor?.isActive({ textAlign: 'center' }), - command: (editor) => editor?.chain().focus().setTextAlign('center').run(), - }, - { - name: 'align-right', - icon: icons.alignRight, - isActive: (editor) => editor?.isActive({ textAlign: 'right' }), - command: (editor) => editor?.chain().focus().setTextAlign('right').run(), - }, - { - name: 'align-justify', - icon: icons.alignJustify, - isActive: (editor) => editor?.isActive({ textAlign: 'justify' }), - command: (editor) => editor?.chain().focus().setTextAlign('justify').run(), - }, - { - name: 'link', - icon: icons.link, - isActive: (editor) => editor?.isActive('link'), - command: () => { - const text = prompt('Enter the text'); - const url = prompt('Enter the URL'); - - if (url && text && this.editor) { - const { from } = this.editor.state.selection; - this.editor - .chain() - .focus() - .insertContent(text) - .setTextSelection({ from: from, to: from + text.length }) - .setLink({ href: url, target: '_blank' }) - .run(); - } - }, - }, - ]; - @property({ attribute: false }) set editor(value) { const oldValue = this.#editor; @@ -158,22 +28,15 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { this.requestUpdate(); }; + #registry = new UmbTiptapIconRegistry(); + + constructor() { + super(); + this.#registry.attach(this); + } + override render() { return html` - ${this.actions.map( - (action) => html` - - `, - )} !!ext.kind || !!ext.element} diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts index 28c9feb62e..30f1458cb7 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts @@ -24,7 +24,6 @@ export class UmbTiptapHoverMenuElement extends LitElement { } #onUpdate = () => { - console.log('LINK ACTIVE'); if (this.editor?.isActive('link')) { // show the popover this.showPopover(); @@ -34,7 +33,6 @@ export class UmbTiptapHoverMenuElement extends LitElement { }; override render() { - console.log('RENDER HOVER MENU'); return html``; } diff --git a/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts b/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts new file mode 100644 index 0000000000..101299b5fb --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Blockquote } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Blockquote', + name: 'Blockquote Tiptap Extension', + api: () => import('./blockquote.extension.js'), + weight: 995, + meta: { + alias: 'blockquote', + icon: 'blockquote', + label: 'Blockquote', + }, +}; + +export default class UmbTiptapBlockquotePlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Blockquote]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleBlockquote().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/bold.extension.ts b/src/packages/rte/tiptap/extensions/core/bold.extension.ts new file mode 100644 index 0000000000..6524e15b04 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/bold.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Bold } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Bold', + name: 'Bold Tiptap Extension', + api: () => import('./bold.extension.js'), + weight: 999, + meta: { + alias: 'bold', + icon: 'bold', + label: 'Bold', + }, +}; + +export default class UmbTiptapBoldPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Bold]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleBold().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts b/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts new file mode 100644 index 0000000000..853cbb202f --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { BulletList, ListItem } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.BulletList', + name: 'Bullet List Tiptap Extension', + api: () => import('./bullet-list.extension.js'), + weight: 993, + meta: { + alias: 'bullet-list', + icon: 'bullet-list', + label: 'Bullet List', + }, +}; + +export default class UmbTiptapBulletListPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [BulletList, ListItem]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleBulletList().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/code-block.extension.ts b/src/packages/rte/tiptap/extensions/core/code-block.extension.ts new file mode 100644 index 0000000000..42dea9c02f --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/code-block.extension.ts @@ -0,0 +1,27 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Code, CodeBlock } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.CodeBlock', + name: 'Code Block Tiptap Extension', + api: () => import('./code-block.extension.js'), + weight: 994, + meta: { + alias: 'code-block', + icon: 'code-block', + label: 'Code Block', + }, +}; + +export default class UmbTiptapCodePlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Code, CodeBlock]; + + override execute(editor?: Editor) { + // editor.chain().focus().toggleCode().run(); + editor?.chain().focus().toggleCodeBlock().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/heading1.extension.ts b/src/packages/rte/tiptap/extensions/core/heading1.extension.ts new file mode 100644 index 0000000000..6f1ac09400 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/heading1.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Heading1', + name: 'Heading 1 Tiptap Extension', + api: () => import('./heading1.extension.js'), + weight: 949, + meta: { + alias: 'heading1', + icon: 'heading1', + label: 'Heading 1', + }, +}; + +export default class UmbTiptapHeading1Plugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Heading]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleHeading({ level: 1 }).run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/heading2.extension.ts b/src/packages/rte/tiptap/extensions/core/heading2.extension.ts new file mode 100644 index 0000000000..8f7ad839b2 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/heading2.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Heading2', + name: 'Heading 2 Tiptap Extension', + api: () => import('./heading2.extension.js'), + weight: 948, + meta: { + alias: 'heading2', + icon: 'heading2', + label: 'Heading 2', + }, +}; + +export default class UmbTiptapHeading1Plugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Heading]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleHeading({ level: 2 }).run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/heading3.extension.ts b/src/packages/rte/tiptap/extensions/core/heading3.extension.ts new file mode 100644 index 0000000000..f1d11a912e --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/heading3.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Heading3', + name: 'Heading 3 Tiptap Extension', + api: () => import('./heading3.extension.js'), + weight: 947, + meta: { + alias: 'heading3', + icon: 'heading3', + label: 'Heading 3', + }, +}; + +export default class UmbTiptapHeading1Plugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Heading]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleHeading({ level: 3 }).run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts b/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts new file mode 100644 index 0000000000..4e29557f2a --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { HorizontalRule } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.HorizontalRule', + name: 'Horizontal Rule Tiptap Extension', + api: () => import('./horizontal-rule.extension.js'), + weight: 991, + meta: { + alias: 'horizontal-rule', + icon: 'horizontal-rule', + label: 'Horizontal Rule', + }, +}; + +export default class UmbTiptapHorizontalRulePlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [HorizontalRule]; + + override execute(editor?: Editor) { + editor?.chain().focus().setHorizontalRule().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/image.extension.ts b/src/packages/rte/tiptap/extensions/core/image.extension.ts new file mode 100644 index 0000000000..1bd069617f --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/image.extension.ts @@ -0,0 +1,17 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbImage } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + alias: 'Umb.Tiptap.Image', + name: 'Image Tiptap Extension', + api: () => import('./image.extension.js'), + meta: {}, +}; + +export default class UmbTiptapImageExtension extends UmbTiptapExtensionApi { + getTiptapExtensions() { + return [UmbImage.configure({ inline: true })]; + } +} diff --git a/src/packages/rte/tiptap/extensions/core/italic.extension.ts b/src/packages/rte/tiptap/extensions/core/italic.extension.ts new file mode 100644 index 0000000000..2343355c08 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/italic.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Italic } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Italic', + name: 'Italic Tiptap Extension', + api: () => import('./italic.extension.js'), + weight: 998, + meta: { + alias: 'italic', + icon: 'italic', + label: 'Italic', + }, +}; + +export default class UmbTiptapItalicPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Italic]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleItalic().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/packages/rte/tiptap/extensions/core/manifests.ts new file mode 100644 index 0000000000..f45ce80e1b --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/manifests.ts @@ -0,0 +1,38 @@ +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { manifest as blockquote } from './blockquote.extension.js'; +import { manifest as bold } from './bold.extension.js'; +import { manifest as bulletList } from './bullet-list.extension.js'; +import { manifest as codeBlock } from './code-block.extension.js'; +import { manifest as image } from './image.extension.js'; +import { manifest as italic } from './italic.extension.js'; +import { manifest as heading1 } from './heading1.extension.js'; +import { manifest as heading2 } from './heading2.extension.js'; +import { manifest as heading3 } from './heading3.extension.js'; +import { manifest as horizontalRule } from './horizontal-rule.extension.js'; +import { manifest as orderedList } from './ordered-list.extension.js'; +import { manifest as strike } from './strike.extension.js'; +import { manifest as textAlignLeft } from './text-align-left.extension.js'; +import { manifest as textAlignCenter } from './text-align-center.extension.js'; +import { manifest as textAlignRight } from './text-align-right.extension.js'; +import { manifest as textAlignJustify } from './text-align-justify.extension.js'; +import { manifest as underline } from './underline.extension.js'; + +export const manifests: Array = [ + blockquote, + bold, + bulletList, + codeBlock, + image, + italic, + heading1, + heading2, + heading3, + horizontalRule, + orderedList, + strike, + textAlignLeft, + textAlignCenter, + textAlignRight, + textAlignJustify, + underline, +]; diff --git a/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts b/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts new file mode 100644 index 0000000000..85e650e273 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { OrderedList, ListItem } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.OrderedList', + name: 'Ordered List Tiptap Extension', + api: () => import('./ordered-list.extension.js'), + weight: 992, + meta: { + alias: 'ordered-list', + icon: 'ordered-list', + label: 'Ordered List', + }, +}; + +export default class UmbTiptapOrderedListPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [OrderedList, ListItem]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleOrderedList().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/strike.extension.ts b/src/packages/rte/tiptap/extensions/core/strike.extension.ts new file mode 100644 index 0000000000..a3e5feb0a9 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/strike.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Strike } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Strike', + name: 'Strike Tiptap Extension', + api: () => import('./strike.extension.js'), + weight: 996, + meta: { + alias: 'strike', + icon: 'strike', + label: 'Strike', + }, +}; + +export default class UmbTiptapStrikePlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Strike]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleStrike().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts new file mode 100644 index 0000000000..5e6caad8cd --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts @@ -0,0 +1,30 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.TextAlignCenter', + name: 'Text Align Center Tiptap Extension', + api: () => import('./text-align-center.extension.js'), + weight: 918, + meta: { + alias: 'text-align-center', + icon: 'text-align-center', + label: 'Text Align Center', + }, +}; + +export default class UmbTiptapTextAlignCenterPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [ + TextAlign.configure({ + types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], + }), + ]; + + override execute(editor?: Editor) { + editor?.chain().focus().setTextAlign('center').run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts new file mode 100644 index 0000000000..0c021383b5 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts @@ -0,0 +1,30 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.TextAlignJustify', + name: 'Text Align Justify Tiptap Extension', + api: () => import('./text-align-justify.extension.js'), + weight: 916, + meta: { + alias: 'text-align-justify', + icon: 'text-align-justify', + label: 'Text Align Justify', + }, +}; + +export default class UmbTiptapTextAlignJustifyPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [ + TextAlign.configure({ + types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], + }), + ]; + + override execute(editor?: Editor) { + editor?.chain().focus().setTextAlign('justify').run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts new file mode 100644 index 0000000000..aedfbc2d85 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts @@ -0,0 +1,30 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.TextAlignLeft', + name: 'Text Align Left Tiptap Extension', + api: () => import('./text-align-left.extension.js'), + weight: 919, + meta: { + alias: 'text-align-left', + icon: 'text-align-left', + label: 'Text Align Left', + }, +}; + +export default class UmbTiptapTextAlignLeftPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [ + TextAlign.configure({ + types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], + }), + ]; + + override execute(editor?: Editor) { + editor?.chain().focus().setTextAlign('left').run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts new file mode 100644 index 0000000000..049781dd1e --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts @@ -0,0 +1,30 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.TextAlignRight', + name: 'Text Align Right Tiptap Extension', + api: () => import('./text-align-right.extension.js'), + weight: 917, + meta: { + alias: 'text-align-right', + icon: 'text-align-right', + label: 'Text Align Right', + }, +}; + +export default class UmbTiptapTextAlignRightPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [ + TextAlign.configure({ + types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], + }), + ]; + + override execute(editor?: Editor) { + editor?.chain().focus().setTextAlign('right').run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/underline.extension.ts b/src/packages/rte/tiptap/extensions/core/underline.extension.ts new file mode 100644 index 0000000000..52387b9b62 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/underline.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Underline } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Underline', + name: 'Underline Tiptap Extension', + api: () => import('./underline.extension.js'), + weight: 997, + meta: { + alias: 'underline', + icon: 'underline', + label: 'Underline', + }, +}; + +export default class UmbTiptapItalicPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Underline]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleUnderline().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/embed.extension.ts b/src/packages/rte/tiptap/extensions/embed.extension.ts new file mode 100644 index 0000000000..4967051267 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/embed.extension.ts @@ -0,0 +1,25 @@ +import { UmbTiptapExtensionApi } from './types.js'; +import type { ManifestTiptapExtension } from './tiptap-extension.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Embed', + name: 'Embed Tiptap Extension', + api: () => import('./embed.extension.js'), + meta: { + alias: 'umb-embed', + icon: 'umbraco', + label: 'Embed', + }, +}; + +export default class UmbTiptapEmbedPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => []; + + override async execute(editor?: Editor) { + console.log('umb-embed.execute', editor); + // Research: https://github.com/ueberdosis/tiptap/tree/main/packages/extension-youtube + } +} diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index 8afb03e629..754fb3dcf1 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -1,4 +1,8 @@ import type { ManifestTiptapExtension } from './tiptap-extension.js'; +import { manifests as core } from './core/manifests.js'; +import { manifest as embed } from './embed.extension.js'; +import { manifest as mediaPicker } from './mediapicker.extension.js'; +import { manifest as urlPicker } from './urlpicker.extension.js'; import type { ManifestTypes, UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; const kinds: Array = [ @@ -13,28 +17,6 @@ const kinds: Array = [ }, ]; -const extensions: Array = [ - { - type: 'tiptapExtension', - alias: 'Umb.Tiptap.Image', - name: 'Image Tiptap Extension', - weight: 1000, - api: () => import('./tiptap-image.extension.js'), - meta: {}, - }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.MediaPicker', - name: 'Media Picker Tiptap Extension', - weight: 900, - api: () => import('./tiptap-mediapicker.extension.js'), - meta: { - alias: 'umb-media', - icon: 'icon-picture', - label: 'Media picker', - }, - }, -]; +const extensions: Array = [...core, embed, mediaPicker, urlPicker]; export const manifests: Array = [...kinds, ...extensions]; diff --git a/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts b/src/packages/rte/tiptap/extensions/mediapicker.extension.ts similarity index 83% rename from src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts rename to src/packages/rte/tiptap/extensions/mediapicker.extension.ts index 7d96091761..ee3923cfa9 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/mediapicker.extension.ts @@ -1,9 +1,23 @@ import { UmbTiptapExtensionApi } from './types.js'; +import type { ManifestTiptapExtension } from './tiptap-extension.js'; import { mergeAttributes, Node } from '@umbraco-cms/backoffice/external/tiptap'; import { UMB_MEDIA_PICKER_MODAL } from '@umbraco-cms/backoffice/media'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.MediaPicker', + name: 'Media Picker Tiptap Extension', + api: () => import('./mediapicker.extension.js'), + meta: { + alias: 'umb-media', + icon: 'umbraco', + label: 'Media picker', + }, +}; + export default class UmbTiptapMediaPickerPlugin extends UmbTiptapExtensionApi { getTiptapExtensions() { return [ diff --git a/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts b/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts deleted file mode 100644 index f6d64e11a2..0000000000 --- a/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { UmbTiptapExtensionApi } from './types.js'; -import { UmbImage } from '@umbraco-cms/backoffice/external/tiptap'; - -export default class UmbTiptapImageExtension extends UmbTiptapExtensionApi { - getTiptapExtensions() { - return [UmbImage.configure({ inline: true })]; - } -} diff --git a/src/packages/rte/tiptap/extensions/urlpicker.extension.ts b/src/packages/rte/tiptap/extensions/urlpicker.extension.ts new file mode 100644 index 0000000000..6f01aa8bcd --- /dev/null +++ b/src/packages/rte/tiptap/extensions/urlpicker.extension.ts @@ -0,0 +1,41 @@ +import { UmbTiptapExtensionApi } from './types.js'; +import type { ManifestTiptapExtension } from './tiptap-extension.js'; +import { Link } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.UrlPicker', + name: 'URL Picker Tiptap Extension', + api: () => import('./urlpicker.extension.js'), + meta: { + alias: 'umb-link', + icon: 'umbraco', + label: 'URL picker', + }, +}; + +export default class UmbTiptapUrlPickerPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions() { + return [Link.extend({ openOnClick: false })]; + } + + override async execute(editor?: Editor) { + console.log('umb-link.execute', editor); + + const text = prompt('Enter the text'); + const url = prompt('Enter the URL'); + + if (url && text && editor) { + const { from } = editor.state.selection; + editor + .chain() + .focus() + .insertContent(text) + .setTextSelection({ from: from, to: from + text.length }) + .setLink({ href: url, target: '_blank' }) + .run(); + } + } +} From 4a11fd940e711f28e46ec42ac27fe5289b957fa1 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Wed, 18 Sep 2024 19:34:55 +0100 Subject: [PATCH 079/241] Renamed class name suffix from `Plugin` to `ExtensionApi` Also corrected some misnamed classes (copypasta) --- src/packages/rte/tiptap/extensions/core/blockquote.extension.ts | 2 +- src/packages/rte/tiptap/extensions/core/bold.extension.ts | 2 +- .../rte/tiptap/extensions/core/bullet-list.extension.ts | 2 +- src/packages/rte/tiptap/extensions/core/code-block.extension.ts | 2 +- src/packages/rte/tiptap/extensions/core/heading1.extension.ts | 2 +- src/packages/rte/tiptap/extensions/core/heading2.extension.ts | 2 +- src/packages/rte/tiptap/extensions/core/heading3.extension.ts | 2 +- .../rte/tiptap/extensions/core/horizontal-rule.extension.ts | 2 +- src/packages/rte/tiptap/extensions/core/image.extension.ts | 2 +- src/packages/rte/tiptap/extensions/core/italic.extension.ts | 2 +- .../rte/tiptap/extensions/core/ordered-list.extension.ts | 2 +- src/packages/rte/tiptap/extensions/core/strike.extension.ts | 2 +- .../rte/tiptap/extensions/core/text-align-center.extension.ts | 2 +- .../rte/tiptap/extensions/core/text-align-justify.extension.ts | 2 +- .../rte/tiptap/extensions/core/text-align-left.extension.ts | 2 +- .../rte/tiptap/extensions/core/text-align-right.extension.ts | 2 +- src/packages/rte/tiptap/extensions/core/underline.extension.ts | 2 +- src/packages/rte/tiptap/extensions/embed.extension.ts | 2 +- src/packages/rte/tiptap/extensions/mediapicker.extension.ts | 2 +- src/packages/rte/tiptap/extensions/urlpicker.extension.ts | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts b/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts index 101299b5fb..eca4ee1558 100644 --- a/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapBlockquotePlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapBlockquoteExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [Blockquote]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/bold.extension.ts b/src/packages/rte/tiptap/extensions/core/bold.extension.ts index 6524e15b04..7e2637c589 100644 --- a/src/packages/rte/tiptap/extensions/core/bold.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/bold.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapBoldPlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapBoldExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [Bold]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts b/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts index 853cbb202f..c29233cc26 100644 --- a/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapBulletListPlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapBulletListExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [BulletList, ListItem]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/code-block.extension.ts b/src/packages/rte/tiptap/extensions/core/code-block.extension.ts index 42dea9c02f..91ae3254b8 100644 --- a/src/packages/rte/tiptap/extensions/core/code-block.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/code-block.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapCodePlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapCodeBlockExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [Code, CodeBlock]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/heading1.extension.ts b/src/packages/rte/tiptap/extensions/core/heading1.extension.ts index 6f1ac09400..9dd8170117 100644 --- a/src/packages/rte/tiptap/extensions/core/heading1.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/heading1.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapHeading1Plugin extends UmbTiptapExtensionApi { +export default class UmbTiptapHeading1ExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [Heading]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/heading2.extension.ts b/src/packages/rte/tiptap/extensions/core/heading2.extension.ts index 8f7ad839b2..f4f20cc56f 100644 --- a/src/packages/rte/tiptap/extensions/core/heading2.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/heading2.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapHeading1Plugin extends UmbTiptapExtensionApi { +export default class UmbTiptapHeading2ExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [Heading]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/heading3.extension.ts b/src/packages/rte/tiptap/extensions/core/heading3.extension.ts index f1d11a912e..7f9ff33aef 100644 --- a/src/packages/rte/tiptap/extensions/core/heading3.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/heading3.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapHeading1Plugin extends UmbTiptapExtensionApi { +export default class UmbTiptapHeading3ExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [Heading]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts b/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts index 4e29557f2a..41905a33ca 100644 --- a/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapHorizontalRulePlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapHorizontalRuleExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [HorizontalRule]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/image.extension.ts b/src/packages/rte/tiptap/extensions/core/image.extension.ts index 1bd069617f..caa62dd7fc 100644 --- a/src/packages/rte/tiptap/extensions/core/image.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/image.extension.ts @@ -10,7 +10,7 @@ export const manifest: ManifestTiptapExtension = { meta: {}, }; -export default class UmbTiptapImageExtension extends UmbTiptapExtensionApi { +export default class UmbTiptapImageExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions() { return [UmbImage.configure({ inline: true })]; } diff --git a/src/packages/rte/tiptap/extensions/core/italic.extension.ts b/src/packages/rte/tiptap/extensions/core/italic.extension.ts index 2343355c08..c73aecc53a 100644 --- a/src/packages/rte/tiptap/extensions/core/italic.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/italic.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapItalicPlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapItalicExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [Italic]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts b/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts index 85e650e273..6ba8804acb 100644 --- a/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapOrderedListPlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapOrderedListExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [OrderedList, ListItem]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/strike.extension.ts b/src/packages/rte/tiptap/extensions/core/strike.extension.ts index a3e5feb0a9..91967c14ca 100644 --- a/src/packages/rte/tiptap/extensions/core/strike.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/strike.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapStrikePlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapStrikeExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [Strike]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts index 5e6caad8cd..472a77b8a2 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapTextAlignCenterPlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapTextAlignCenterExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [ TextAlign.configure({ types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], diff --git a/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts index 0c021383b5..c7c54b8d43 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapTextAlignJustifyPlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapTextAlignJustifyExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [ TextAlign.configure({ types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], diff --git a/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts index aedfbc2d85..543e2389be 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapTextAlignLeftPlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapTextAlignLeftExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [ TextAlign.configure({ types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], diff --git a/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts index 049781dd1e..a83d7b8a80 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapTextAlignRightPlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapTextAlignRightExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [ TextAlign.configure({ types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], diff --git a/src/packages/rte/tiptap/extensions/core/underline.extension.ts b/src/packages/rte/tiptap/extensions/core/underline.extension.ts index 52387b9b62..d2a0c98244 100644 --- a/src/packages/rte/tiptap/extensions/core/underline.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/underline.extension.ts @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapItalicPlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapUnderlineExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => [Underline]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/embed.extension.ts b/src/packages/rte/tiptap/extensions/embed.extension.ts index 4967051267..33de910ce6 100644 --- a/src/packages/rte/tiptap/extensions/embed.extension.ts +++ b/src/packages/rte/tiptap/extensions/embed.extension.ts @@ -15,7 +15,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapEmbedPlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapEmbedExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions = () => []; override async execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/mediapicker.extension.ts b/src/packages/rte/tiptap/extensions/mediapicker.extension.ts index ee3923cfa9..40bbf7222a 100644 --- a/src/packages/rte/tiptap/extensions/mediapicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/mediapicker.extension.ts @@ -18,7 +18,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapMediaPickerPlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapMediaPickerExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions() { return [ Node.create({ diff --git a/src/packages/rte/tiptap/extensions/urlpicker.extension.ts b/src/packages/rte/tiptap/extensions/urlpicker.extension.ts index 6f01aa8bcd..c711f68d46 100644 --- a/src/packages/rte/tiptap/extensions/urlpicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/urlpicker.extension.ts @@ -16,7 +16,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapUrlPickerPlugin extends UmbTiptapExtensionApi { +export default class UmbTiptapUrlPickerExtensionApi extends UmbTiptapExtensionApi { getTiptapExtensions() { return [Link.extend({ openOnClick: false })]; } From 78cadb23b5f50b908b69e732947cb5e1c564823f Mon Sep 17 00:00:00 2001 From: leekelleher Date: Thu, 19 Sep 2024 08:40:58 +0100 Subject: [PATCH 080/241] Element class tidy-up --- .../input-tiptap/input-tiptap.element.ts | 74 ++++++++++--------- .../input-tiptap/tiptap-fixed-menu.element.ts | 39 ++-------- .../property-editor-ui-tiptap.element.ts | 27 +++---- 3 files changed, 56 insertions(+), 84 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 095567fd41..8474cbc62d 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -20,7 +20,9 @@ import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/ import './tiptap-fixed-menu.element.js'; import './tiptap-hover-menu.element.js'; -@customElement('umb-input-tiptap') +const elementName = 'umb-input-tiptap'; + +@customElement(elementName) export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement) { #requiredExtensions = [Document, Dropcursor, Gapcursor, HardBreak, History, Paragraph, Text]; @@ -102,6 +104,14 @@ export class UmbInputTiptapElement extends UmbFormControlMixin code) { - background-color: var(--uui-color-surface-alt); - padding: var(--uui-size-space-1) var(--uui-size-space-2); - border-radius: calc(var(--uui-border-radius) * 2); - } + code:not(pre > code) { + background-color: var(--uui-color-surface-alt); + padding: var(--uui-size-space-1) var(--uui-size-space-2); + border-radius: calc(var(--uui-border-radius) * 2); + } - #editor code { - font-family: 'Roboto Mono', monospace; - background: none; - color: inherit; - font-size: 0.8rem; - padding: 0; - } - .tiptap { - height: 100%; - width: 100%; - outline: none; - white-space: pre-wrap; - min-width: 0; - } - #editor p, - #editor h1, - #editor h2, - #editor h3 { - margin-top: 0; - margin-bottom: 0.5em; + code { + font-family: 'Roboto Mono', monospace; + background: none; + color: inherit; + font-size: 0.8rem; + padding: 0; + } + + h1, + h2, + h3, + p { + margin-top: 0; + margin-bottom: 0.5em; + } } `, ]; } -export default UmbInputTiptapElement; - declare global { interface HTMLElementTagNameMap { - 'umb-input-tiptap': UmbInputTiptapElement; + [elementName]: UmbInputTiptapElement; } } diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 39e75dc1b8..fdc846296e 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -4,7 +4,9 @@ import { css, customElement, html, property } from '@umbraco-cms/backoffice/exte import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -@customElement('umb-tiptap-fixed-menu') +const elementName = 'umb-tiptap-fixed-menu'; + +@customElement(elementName) export class UmbTiptapFixedMenuElement extends UmbLitElement { @property({ type: Boolean, reflect: true }) readonly = false; @@ -49,6 +51,8 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { :host { border-radius: var(--uui-border-radius); border: 1px solid var(--uui-color-border); + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; background-color: var(--uui-color-surface); color: var(--color-text); display: grid; @@ -64,42 +68,11 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { pointer-events: none; background-color: var(--uui-color-surface-alt); } - - button { - color: var(--uui-color-interactive); - width: 24px; - height: 24px; - padding: 4px; - border: none; - background: none; - cursor: pointer; - margin: 0; - border-radius: 4px; - box-sizing: border-box; - } - - button:hover { - color: var(--uui-color-interactive-emphasis); - background-color: var(--uui-color-surface-alt); - } - - button.active { - background-color: var(--uui-color-selected); - color: var(--uui-color-selected-contrast); - } - button.active:hover { - background-color: var(--uui-color-selected-emphasis); - } - - button img { - width: 100%; - height: 100%; - } `; } declare global { interface HTMLElementTagNameMap { - 'umb-tiptap-fixed-menu': UmbTiptapFixedMenuElement; + [elementName]: UmbTiptapFixedMenuElement; } } diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index 60dedc1240..d1dee314ae 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -1,18 +1,13 @@ -import type UmbInputTiptapElement from '../../components/input-tiptap/input-tiptap.element.js'; +import type { UmbInputTiptapElement } from '../../components/input-tiptap/input-tiptap.element.js'; import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbBlockRteEntriesContext, UmbBlockRteManagerContext } from '@umbraco-cms/backoffice/block-rte'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { - UmbPropertyValueChangeEvent, - type UmbPropertyEditorConfigCollection, -} from '@umbraco-cms/backoffice/property-editor'; +import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; +import type { UmbBlockRteLayoutModel } from '@umbraco-cms/backoffice/block-rte'; +import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import '../../components/input-tiptap/input-tiptap.element.js'; -import { - UmbBlockRteEntriesContext, - UmbBlockRteManagerContext, - type UmbBlockRteLayoutModel, -} from '@umbraco-cms/backoffice/block-rte'; import type { UmbBlockValueType } from '@umbraco-cms/backoffice/block'; // Look at Tiny for correct types @@ -23,11 +18,13 @@ export interface UmbRichTextEditorValueType { const UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS = 'Umbraco.RichText'; +const elementName = 'umb-property-editor-ui-tiptap'; + /** * @element umb-property-editor-ui-tiptap */ -@customElement('umb-property-editor-ui-tiptap') -export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements UmbPropertyEditorUiElement { +@customElement(elementName) +export class UmbPropertyEditorUiTiptapElement extends UmbLitElement implements UmbPropertyEditorUiElement { // public set config(config: UmbPropertyEditorConfigCollection | undefined) { this._config = config; @@ -110,7 +107,7 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U } #onChange(event: CustomEvent & { target: UmbInputTiptapElement }) { - const value = event.target.value as string; + const value = event.target.value; this._latestMarkup = value; // TODO: Validate blocks @@ -148,10 +145,10 @@ export class UmbPropertyEditorUITiptapElement extends UmbLitElement implements U } } -export default UmbPropertyEditorUITiptapElement; +export { UmbPropertyEditorUiTiptapElement as element }; declare global { interface HTMLElementTagNameMap { - 'umb-property-editor-ui-tiptap': UmbPropertyEditorUITiptapElement; + [elementName]: UmbPropertyEditorUiTiptapElement; } } From fecf9b6b61b56616b973d3fc92e5acc5ccf4e029 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Thu, 19 Sep 2024 09:14:05 +0100 Subject: [PATCH 081/241] Re-implemented the `isActive` state for toolbar elements --- .../input-tiptap/tiptap-fixed-menu.element.ts | 6 --- .../extensions/core/blockquote.extension.ts | 8 ++-- .../tiptap/extensions/core/bold.extension.ts | 8 ++-- .../extensions/core/bullet-list.extension.ts | 10 ++--- .../extensions/core/code-block.extension.ts | 10 ++--- .../extensions/core/heading1.extension.ts | 12 ++++-- .../extensions/core/heading2.extension.ts | 12 ++++-- .../extensions/core/heading3.extension.ts | 12 ++++-- .../core/horizontal-rule.extension.ts | 10 ++--- .../tiptap/extensions/core/image.extension.ts | 8 ++-- .../extensions/core/italic.extension.ts | 8 ++-- .../extensions/core/ordered-list.extension.ts | 10 ++--- .../extensions/core/strike.extension.ts | 8 ++-- .../core/text-align-center.extension.ts | 12 ++++-- .../core/text-align-justify.extension.ts | 12 ++++-- .../core/text-align-left.extension.ts | 12 ++++-- .../core/text-align-right.extension.ts | 12 ++++-- .../extensions/core/underline.extension.ts | 8 ++-- .../rte/tiptap/extensions/embed.extension.ts | 8 ++-- .../extensions/mediapicker.extension.ts | 14 ++++--- .../rte/tiptap/extensions/tiptap-extension.ts | 8 ++-- .../tiptap-toolbar-button.element.ts | 37 +++++++++++++++++-- src/packages/rte/tiptap/extensions/types.ts | 33 ++++++++++++----- .../tiptap/extensions/urlpicker.extension.ts | 8 ++-- 24 files changed, 177 insertions(+), 109 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index fdc846296e..ede9344903 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -18,18 +18,12 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { return; } this.#editor = value; - this.#editor?.on('selectionUpdate', this.#onUpdate); - this.#editor?.on('update', this.#onUpdate); } get editor() { return this.#editor; } #editor?: Editor; - #onUpdate = () => { - this.requestUpdate(); - }; - #registry = new UmbTiptapIconRegistry(); constructor() { diff --git a/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts b/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts index eca4ee1558..94089225fc 100644 --- a/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Blockquote } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.Blockquote', @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapBlockquoteExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapBlockquoteExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Blockquote]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/bold.extension.ts b/src/packages/rte/tiptap/extensions/core/bold.extension.ts index 7e2637c589..c29bd5408c 100644 --- a/src/packages/rte/tiptap/extensions/core/bold.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/bold.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Bold } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.Bold', @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapBoldExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapBoldExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Bold]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts b/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts index c29233cc26..7915fc7b5b 100644 --- a/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { BulletList, ListItem } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.BulletList', @@ -11,13 +11,13 @@ export const manifest: ManifestTiptapExtension = { api: () => import('./bullet-list.extension.js'), weight: 993, meta: { - alias: 'bullet-list', + alias: 'bulletList', icon: 'bullet-list', label: 'Bullet List', }, }; -export default class UmbTiptapBulletListExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapBulletListExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [BulletList, ListItem]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/code-block.extension.ts b/src/packages/rte/tiptap/extensions/core/code-block.extension.ts index 91ae3254b8..b91bc201b5 100644 --- a/src/packages/rte/tiptap/extensions/core/code-block.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/code-block.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Code, CodeBlock } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.CodeBlock', @@ -11,13 +11,13 @@ export const manifest: ManifestTiptapExtension = { api: () => import('./code-block.extension.js'), weight: 994, meta: { - alias: 'code-block', + alias: 'codeBlock', icon: 'code-block', label: 'Code Block', }, }; -export default class UmbTiptapCodeBlockExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapCodeBlockExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Code, CodeBlock]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/heading1.extension.ts b/src/packages/rte/tiptap/extensions/core/heading1.extension.ts index 9dd8170117..62a542e753 100644 --- a/src/packages/rte/tiptap/extensions/core/heading1.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/heading1.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.Heading1', @@ -17,9 +17,13 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapHeading1ExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapHeading1ExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Heading]; + override isActive(editor?: Editor) { + return editor?.isActive('heading', { level: 1 }) === true; + } + override execute(editor?: Editor) { editor?.chain().focus().toggleHeading({ level: 1 }).run(); } diff --git a/src/packages/rte/tiptap/extensions/core/heading2.extension.ts b/src/packages/rte/tiptap/extensions/core/heading2.extension.ts index f4f20cc56f..1d144bdfc4 100644 --- a/src/packages/rte/tiptap/extensions/core/heading2.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/heading2.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.Heading2', @@ -17,9 +17,13 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapHeading2ExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapHeading2ExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Heading]; + override isActive(editor?: Editor) { + return editor?.isActive('heading', { level: 2 }) === true; + } + override execute(editor?: Editor) { editor?.chain().focus().toggleHeading({ level: 2 }).run(); } diff --git a/src/packages/rte/tiptap/extensions/core/heading3.extension.ts b/src/packages/rte/tiptap/extensions/core/heading3.extension.ts index 7f9ff33aef..99683dbc7b 100644 --- a/src/packages/rte/tiptap/extensions/core/heading3.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/heading3.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.Heading3', @@ -17,9 +17,13 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapHeading3ExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapHeading3ExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Heading]; + override isActive(editor?: Editor) { + return editor?.isActive('heading', { level: 3 }) === true; + } + override execute(editor?: Editor) { editor?.chain().focus().toggleHeading({ level: 3 }).run(); } diff --git a/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts b/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts index 41905a33ca..2cea4a9f3d 100644 --- a/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { HorizontalRule } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.HorizontalRule', @@ -11,13 +11,13 @@ export const manifest: ManifestTiptapExtension = { api: () => import('./horizontal-rule.extension.js'), weight: 991, meta: { - alias: 'horizontal-rule', + alias: 'horizontalRule', icon: 'horizontal-rule', label: 'Horizontal Rule', }, }; -export default class UmbTiptapHorizontalRuleExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapHorizontalRuleExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [HorizontalRule]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/image.extension.ts b/src/packages/rte/tiptap/extensions/core/image.extension.ts index caa62dd7fc..f71f01ebd2 100644 --- a/src/packages/rte/tiptap/extensions/core/image.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/image.extension.ts @@ -1,4 +1,4 @@ -import { UmbTiptapExtensionApi } from '../types.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { ManifestTiptapExtension } from '../tiptap-extension.js'; import { UmbImage } from '@umbraco-cms/backoffice/external/tiptap'; @@ -7,10 +7,12 @@ export const manifest: ManifestTiptapExtension = { alias: 'Umb.Tiptap.Image', name: 'Image Tiptap Extension', api: () => import('./image.extension.js'), - meta: {}, + meta: { + alias: 'image', + }, }; -export default class UmbTiptapImageExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapImageExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions() { return [UmbImage.configure({ inline: true })]; } diff --git a/src/packages/rte/tiptap/extensions/core/italic.extension.ts b/src/packages/rte/tiptap/extensions/core/italic.extension.ts index c73aecc53a..2bc8b8a2fa 100644 --- a/src/packages/rte/tiptap/extensions/core/italic.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/italic.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Italic } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.Italic', @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapItalicExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapItalicExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Italic]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts b/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts index 6ba8804acb..878f199d26 100644 --- a/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { OrderedList, ListItem } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.OrderedList', @@ -11,13 +11,13 @@ export const manifest: ManifestTiptapExtension = { api: () => import('./ordered-list.extension.js'), weight: 992, meta: { - alias: 'ordered-list', + alias: 'orderedList', icon: 'ordered-list', label: 'Ordered List', }, }; -export default class UmbTiptapOrderedListExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapOrderedListExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [OrderedList, ListItem]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/strike.extension.ts b/src/packages/rte/tiptap/extensions/core/strike.extension.ts index 91967c14ca..bfef2a61fd 100644 --- a/src/packages/rte/tiptap/extensions/core/strike.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/strike.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Strike } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.Strike', @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapStrikeExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapStrikeExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Strike]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts index 472a77b8a2..6fefff51b2 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.TextAlignCenter', @@ -17,13 +17,17 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapTextAlignCenterExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapTextAlignCenterExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [ TextAlign.configure({ types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], }), ]; + override isActive(editor?: Editor) { + return editor?.isActive({ textAlign: 'center' }) === true; + } + override execute(editor?: Editor) { editor?.chain().focus().setTextAlign('center').run(); } diff --git a/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts index c7c54b8d43..a073705b6c 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.TextAlignJustify', @@ -17,13 +17,17 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapTextAlignJustifyExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapTextAlignJustifyExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [ TextAlign.configure({ types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], }), ]; + override isActive(editor?: Editor) { + return editor?.isActive({ textAlign: 'justify' }) === true; + } + override execute(editor?: Editor) { editor?.chain().focus().setTextAlign('justify').run(); } diff --git a/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts index 543e2389be..3f814bc8ad 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.TextAlignLeft', @@ -17,13 +17,17 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapTextAlignLeftExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapTextAlignLeftExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [ TextAlign.configure({ types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], }), ]; + override isActive(editor?: Editor) { + return editor?.isActive({ textAlign: 'left' }) === true; + } + override execute(editor?: Editor) { editor?.chain().focus().setTextAlign('left').run(); } diff --git a/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts index a83d7b8a80..dcef9a523b 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.TextAlignRight', @@ -17,13 +17,17 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapTextAlignRightExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapTextAlignRightExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [ TextAlign.configure({ types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], }), ]; + override isActive(editor?: Editor) { + return editor?.isActive({ textAlign: 'right' }) === true; + } + override execute(editor?: Editor) { editor?.chain().focus().setTextAlign('right').run(); } diff --git a/src/packages/rte/tiptap/extensions/core/underline.extension.ts b/src/packages/rte/tiptap/extensions/core/underline.extension.ts index d2a0c98244..e538e850c1 100644 --- a/src/packages/rte/tiptap/extensions/core/underline.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/underline.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Underline } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.Underline', @@ -17,7 +17,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapUnderlineExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapUnderlineExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Underline]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/embed.extension.ts b/src/packages/rte/tiptap/extensions/embed.extension.ts index 33de910ce6..3750438e5f 100644 --- a/src/packages/rte/tiptap/extensions/embed.extension.ts +++ b/src/packages/rte/tiptap/extensions/embed.extension.ts @@ -1,8 +1,8 @@ -import { UmbTiptapExtensionApi } from './types.js'; -import type { ManifestTiptapExtension } from './tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from './types.js'; +import type { ManifestTiptapExtensionButtonKind } from './tiptap-extension.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.Embed', @@ -15,7 +15,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapEmbedExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapEmbedExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => []; override async execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/mediapicker.extension.ts b/src/packages/rte/tiptap/extensions/mediapicker.extension.ts index 40bbf7222a..7e14c797dc 100644 --- a/src/packages/rte/tiptap/extensions/mediapicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/mediapicker.extension.ts @@ -1,11 +1,11 @@ -import { UmbTiptapExtensionApi } from './types.js'; -import type { ManifestTiptapExtension } from './tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from './types.js'; +import type { ManifestTiptapExtensionButtonKind } from './tiptap-extension.js'; import { mergeAttributes, Node } from '@umbraco-cms/backoffice/external/tiptap'; import { UMB_MEDIA_PICKER_MODAL } from '@umbraco-cms/backoffice/media'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.MediaPicker', @@ -18,11 +18,11 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapMediaPickerExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapMediaPickerExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions() { return [ Node.create({ - name: 'umbMediaPicker', + name: 'umb-media', priority: 1000, group: 'block', marks: '', @@ -47,7 +47,9 @@ export default class UmbTiptapMediaPickerExtensionApi extends UmbTiptapExtension ]; } - //isActive: (editor?: Editor) => editor?.isActive('umbMediaPicker') || editor?.isActive('image'), + override isActive(editor?: Editor) { + return editor?.isActive('umb-media') === true || editor?.isActive('image') === true; + } override async execute(editor?: Editor) { console.log('umb-media.execute', editor); diff --git a/src/packages/rte/tiptap/extensions/tiptap-extension.ts b/src/packages/rte/tiptap/extensions/tiptap-extension.ts index 40c9fd6186..5ecb61da5d 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-extension.ts @@ -8,8 +8,9 @@ export interface ManifestTiptapExtension { + if (this.api && this.editor && this.manifest) { + this._isActive = this.api.isActive(this.editor); + } + }; + override render() { return html` - this.api?.execute(this.editor)}> + this.api?.execute(this.editor)}> ${when( this.manifest?.meta.icon, () => html``, diff --git a/src/packages/rte/tiptap/extensions/types.ts b/src/packages/rte/tiptap/extensions/types.ts index 5a2d19ccd4..949d73f056 100644 --- a/src/packages/rte/tiptap/extensions/types.ts +++ b/src/packages/rte/tiptap/extensions/types.ts @@ -1,23 +1,36 @@ +import type { ManifestTiptapExtension } from './tiptap-extension.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { Editor, Extension, Mark, Node } from '@umbraco-cms/backoffice/external/tiptap'; -import type { TemplateResult } from '@umbraco-cms/backoffice/external/lit'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -export abstract class UmbTiptapExtensionApi extends UmbControllerBase implements UmbApi { +export interface UmbTiptapExtensionApi extends UmbApi { + getTiptapExtensions(): Array; +} + +export abstract class UmbTiptapExtensionApiBase extends UmbControllerBase implements UmbApi { + public manifest?: ManifestTiptapExtension; + constructor(host: UmbControllerHost) { super(host); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - execute(editor?: Editor) {} - abstract getTiptapExtensions(): Array; } -export interface UmbTiptapToolbarButton { - name: string; - icon: string | TemplateResult; - isActive: (editor?: Editor) => boolean | undefined; - command: (editor?: Editor) => boolean | undefined | void | Promise | Promise | Promise; +export interface UmbTiptapToolbarElementApi extends UmbTiptapExtensionApi { + execute(editor?: Editor): void; + isActive(editor?: Editor): boolean; +} + +export abstract class UmbTiptapToolbarElementApiBase + extends UmbTiptapExtensionApiBase + implements UmbTiptapToolbarElementApi +{ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public execute(editor?: Editor) {} + + public isActive(editor?: Editor) { + return editor && this.manifest?.meta.alias ? editor?.isActive(this.manifest.meta.alias) : false; + } } diff --git a/src/packages/rte/tiptap/extensions/urlpicker.extension.ts b/src/packages/rte/tiptap/extensions/urlpicker.extension.ts index c711f68d46..dec5d64d05 100644 --- a/src/packages/rte/tiptap/extensions/urlpicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/urlpicker.extension.ts @@ -1,9 +1,9 @@ -import { UmbTiptapExtensionApi } from './types.js'; -import type { ManifestTiptapExtension } from './tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from './types.js'; +import type { ManifestTiptapExtensionButtonKind } from './tiptap-extension.js'; import { Link } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { +export const manifest: ManifestTiptapExtensionButtonKind = { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.UrlPicker', @@ -16,7 +16,7 @@ export const manifest: ManifestTiptapExtension = { }, }; -export default class UmbTiptapUrlPickerExtensionApi extends UmbTiptapExtensionApi { +export default class UmbTiptapUrlPickerExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions() { return [Link.extend({ openOnClick: false })]; } From e6352c52011a1c3661d9fc3520746ec86ef8cc6a Mon Sep 17 00:00:00 2001 From: leekelleher Date: Thu, 19 Sep 2024 10:00:00 +0100 Subject: [PATCH 082/241] Removed custom icon registry Added Lucide icons for RTE toolbar --- .../core/icon-registry/icon-dictionary.json | 56 +++++++++++++ src/packages/core/icon-registry/icons.ts | 56 +++++++++++++ .../icon-registry/icons/icon-blockquote.ts | 17 ++++ .../core/icon-registry/icons/icon-bold.ts | 14 ++++ .../core/icon-registry/icons/icon-embed.ts | 17 ++++ .../icon-registry/icons/icon-heading-1.ts | 17 ++++ .../icon-registry/icons/icon-heading-2.ts | 17 ++++ .../icon-registry/icons/icon-heading-3.ts | 18 +++++ .../icons/icon-horizontal-rule.ts | 16 ++++ .../core/icon-registry/icons/icon-italic.ts | 16 ++++ .../icon-registry/icons/icon-strikethrough.ts | 16 ++++ .../icons/icon-text-align-center.ts | 16 ++++ .../icons/icon-text-align-justify.ts | 16 ++++ .../icons/icon-text-align-left.ts | 16 ++++ .../icons/icon-text-align-right.ts | 16 ++++ .../icon-registry/icons/icon-underline.ts | 15 ++++ .../components/input-tiptap/icon.registry.ts | 81 ------------------- .../input-tiptap/tiptap-fixed-menu.element.ts | 8 -- .../extensions/core/blockquote.extension.ts | 2 +- .../tiptap/extensions/core/bold.extension.ts | 2 +- .../extensions/core/bullet-list.extension.ts | 2 +- .../extensions/core/code-block.extension.ts | 2 +- .../extensions/core/heading1.extension.ts | 2 +- .../extensions/core/heading2.extension.ts | 2 +- .../extensions/core/heading3.extension.ts | 2 +- .../core/horizontal-rule.extension.ts | 2 +- .../extensions/core/italic.extension.ts | 2 +- .../extensions/core/ordered-list.extension.ts | 2 +- .../extensions/core/strike.extension.ts | 2 +- .../core/text-align-center.extension.ts | 2 +- .../core/text-align-justify.extension.ts | 2 +- .../core/text-align-left.extension.ts | 2 +- .../core/text-align-right.extension.ts | 2 +- .../extensions/core/underline.extension.ts | 2 +- .../rte/tiptap/extensions/embed.extension.ts | 2 +- .../extensions/mediapicker.extension.ts | 2 +- .../tiptap-toolbar-button.element.ts | 8 +- .../tiptap/extensions/urlpicker.extension.ts | 2 +- 38 files changed, 363 insertions(+), 111 deletions(-) create mode 100644 src/packages/core/icon-registry/icons/icon-blockquote.ts create mode 100644 src/packages/core/icon-registry/icons/icon-bold.ts create mode 100644 src/packages/core/icon-registry/icons/icon-embed.ts create mode 100644 src/packages/core/icon-registry/icons/icon-heading-1.ts create mode 100644 src/packages/core/icon-registry/icons/icon-heading-2.ts create mode 100644 src/packages/core/icon-registry/icons/icon-heading-3.ts create mode 100644 src/packages/core/icon-registry/icons/icon-horizontal-rule.ts create mode 100644 src/packages/core/icon-registry/icons/icon-italic.ts create mode 100644 src/packages/core/icon-registry/icons/icon-strikethrough.ts create mode 100644 src/packages/core/icon-registry/icons/icon-text-align-center.ts create mode 100644 src/packages/core/icon-registry/icons/icon-text-align-justify.ts create mode 100644 src/packages/core/icon-registry/icons/icon-text-align-left.ts create mode 100644 src/packages/core/icon-registry/icons/icon-text-align-right.ts create mode 100644 src/packages/core/icon-registry/icons/icon-underline.ts delete mode 100644 src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts diff --git a/src/packages/core/icon-registry/icon-dictionary.json b/src/packages/core/icon-registry/icon-dictionary.json index 4fae5c0ec7..f8dd3ed1b4 100644 --- a/src/packages/core/icon-registry/icon-dictionary.json +++ b/src/packages/core/icon-registry/icon-dictionary.json @@ -233,6 +233,10 @@ "name": "icon-block", "file": "ban.svg" }, + { + "name": "icon-blockquote", + "file": "text-quote.svg" + }, { "_name": "icon-blueprint", "____file": "blueprint.svg" @@ -245,6 +249,10 @@ "name": "icon-boat-shipping", "file": "ship.svg" }, + { + "name": "icon-bold", + "file": "bold.svg" + }, { "_name": "icon-bomb", "____file": "bomb.svg" @@ -797,6 +805,10 @@ "_name": "icon-eject", "____file": "eject.svg" }, + { + "name": "icon-embed", + "file": "monitor-play.svg" + }, { "name": "icon-employee", "file": "user.svg", @@ -1071,6 +1083,18 @@ "_name": "icon-hd", "____file": "hd.svg" }, + { + "name": "icon-heading-1", + "file": "heading-1.svg" + }, + { + "name": "icon-heading-2", + "file": "heading-2.svg" + }, + { + "name": "icon-heading-3", + "file": "heading-3.svg" + }, { "name": "icon-headphones", "file": "headphones.svg" @@ -1104,6 +1128,10 @@ "name": "icon-home", "file": "house.svg" }, + { + "name": "icon-horizontal-rule", + "file": "separator-horizontal.svg" + }, { "name": "icon-hourglass", "file": "hourglass.svg" @@ -1153,6 +1181,10 @@ "file": "smartphone.svg", "legacy": true }, + { + "name": "icon-italic", + "file": "italic.svg" + }, { "name": "icon-item-arrangement", "file": "table-properties.svg", @@ -2128,6 +2160,10 @@ "file": "square-activity.svg", "legacy": true }, + { + "name": "icon-strikethrough", + "file": "strikethrough.svg" + }, { "name": "icon-sunny", "file": "sun.svg" @@ -2178,6 +2214,22 @@ "name": "icon-terminal", "file": "square-terminal.svg" }, + { + "name": "icon-text-align-center", + "file": "align-center.svg" + }, + { + "name": "icon-text-align-justify", + "file": "align-justify.svg" + }, + { + "name": "icon-text-align-left", + "file": "align-left.svg" + }, + { + "name": "icon-text-align-right", + "file": "align-right.svg" + }, { "name": "icon-theater", "file": "drama.svg" @@ -2308,6 +2360,10 @@ "name": "icon-undo", "file": "undo-2.svg" }, + { + "name": "icon-underline", + "file": "underline.svg" + }, { "name": "icon-unlocked", "file": "lock-open.svg" diff --git a/src/packages/core/icon-registry/icons.ts b/src/packages/core/icon-registry/icons.ts index ff42d07bc6..acc2b0e7c6 100644 --- a/src/packages/core/icon-registry/icons.ts +++ b/src/packages/core/icon-registry/icons.ts @@ -159,6 +159,10 @@ name: "icon-block", path: () => import("./icons/icon-block.js"), },{ +name: "icon-blockquote", + +path: () => import("./icons/icon-blockquote.js"), +},{ name: "icon-bluetooth", path: () => import("./icons/icon-bluetooth.js"), @@ -167,6 +171,10 @@ name: "icon-boat-shipping", path: () => import("./icons/icon-boat-shipping.js"), },{ +name: "icon-bold", + +path: () => import("./icons/icon-bold.js"), +},{ name: "icon-bones", path: () => import("./icons/icon-bones.js"), @@ -627,6 +635,10 @@ name: "icon-edit", path: () => import("./icons/icon-edit.js"), },{ +name: "icon-embed", + +path: () => import("./icons/icon-embed.js"), +},{ name: "icon-employee", legacy: true, path: () => import("./icons/icon-employee.js"), @@ -855,6 +867,18 @@ name: "icon-hard-drive", legacy: true, path: () => import("./icons/icon-hard-drive.js"), },{ +name: "icon-heading-1", + +path: () => import("./icons/icon-heading-1.js"), +},{ +name: "icon-heading-2", + +path: () => import("./icons/icon-heading-2.js"), +},{ +name: "icon-heading-3", + +path: () => import("./icons/icon-heading-3.js"), +},{ name: "icon-headphones", path: () => import("./icons/icon-headphones.js"), @@ -887,6 +911,10 @@ name: "icon-home", path: () => import("./icons/icon-home.js"), },{ +name: "icon-horizontal-rule", + +path: () => import("./icons/icon-horizontal-rule.js"), +},{ name: "icon-hourglass", path: () => import("./icons/icon-hourglass.js"), @@ -927,6 +955,10 @@ name: "icon-iphone", legacy: true, path: () => import("./icons/icon-iphone.js"), },{ +name: "icon-italic", + +path: () => import("./icons/icon-italic.js"), +},{ name: "icon-item-arrangement", legacy: true, path: () => import("./icons/icon-item-arrangement.js"), @@ -1803,6 +1835,10 @@ name: "icon-stream", legacy: true, path: () => import("./icons/icon-stream.js"), },{ +name: "icon-strikethrough", + +path: () => import("./icons/icon-strikethrough.js"), +},{ name: "icon-sunny", path: () => import("./icons/icon-sunny.js"), @@ -1851,6 +1887,22 @@ name: "icon-terminal", path: () => import("./icons/icon-terminal.js"), },{ +name: "icon-text-align-center", + +path: () => import("./icons/icon-text-align-center.js"), +},{ +name: "icon-text-align-justify", + +path: () => import("./icons/icon-text-align-justify.js"), +},{ +name: "icon-text-align-left", + +path: () => import("./icons/icon-text-align-left.js"), +},{ +name: "icon-text-align-right", + +path: () => import("./icons/icon-text-align-right.js"), +},{ name: "icon-theater", path: () => import("./icons/icon-theater.js"), @@ -1967,6 +2019,10 @@ name: "icon-undo", path: () => import("./icons/icon-undo.js"), },{ +name: "icon-underline", + +path: () => import("./icons/icon-underline.js"), +},{ name: "icon-unlocked", path: () => import("./icons/icon-unlocked.js"), diff --git a/src/packages/core/icon-registry/icons/icon-blockquote.ts b/src/packages/core/icon-registry/icons/icon-blockquote.ts new file mode 100644 index 0000000000..7d4802defc --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-blockquote.ts @@ -0,0 +1,17 @@ +export default ` + + + + + + +`; \ No newline at end of file diff --git a/src/packages/core/icon-registry/icons/icon-bold.ts b/src/packages/core/icon-registry/icons/icon-bold.ts new file mode 100644 index 0000000000..6b4b1d986b --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-bold.ts @@ -0,0 +1,14 @@ +export default ` + + + +`; \ No newline at end of file diff --git a/src/packages/core/icon-registry/icons/icon-embed.ts b/src/packages/core/icon-registry/icons/icon-embed.ts new file mode 100644 index 0000000000..22fdef036f --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-embed.ts @@ -0,0 +1,17 @@ +export default ` + + + + + + +`; \ No newline at end of file diff --git a/src/packages/core/icon-registry/icons/icon-heading-1.ts b/src/packages/core/icon-registry/icons/icon-heading-1.ts new file mode 100644 index 0000000000..20fe0c5dd1 --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-heading-1.ts @@ -0,0 +1,17 @@ +export default ` + + + + + + +`; \ No newline at end of file diff --git a/src/packages/core/icon-registry/icons/icon-heading-2.ts b/src/packages/core/icon-registry/icons/icon-heading-2.ts new file mode 100644 index 0000000000..f062a49ca9 --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-heading-2.ts @@ -0,0 +1,17 @@ +export default ` + + + + + + +`; \ No newline at end of file diff --git a/src/packages/core/icon-registry/icons/icon-heading-3.ts b/src/packages/core/icon-registry/icons/icon-heading-3.ts new file mode 100644 index 0000000000..cb237bb4a2 --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-heading-3.ts @@ -0,0 +1,18 @@ +export default ` + + + + + + + +`; \ No newline at end of file diff --git a/src/packages/core/icon-registry/icons/icon-horizontal-rule.ts b/src/packages/core/icon-registry/icons/icon-horizontal-rule.ts new file mode 100644 index 0000000000..424f13bb2f --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-horizontal-rule.ts @@ -0,0 +1,16 @@ +export default ` + + + + + +`; \ No newline at end of file diff --git a/src/packages/core/icon-registry/icons/icon-italic.ts b/src/packages/core/icon-registry/icons/icon-italic.ts new file mode 100644 index 0000000000..d70979bfe6 --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-italic.ts @@ -0,0 +1,16 @@ +export default ` + + + + + +`; \ No newline at end of file diff --git a/src/packages/core/icon-registry/icons/icon-strikethrough.ts b/src/packages/core/icon-registry/icons/icon-strikethrough.ts new file mode 100644 index 0000000000..9af35d7e81 --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-strikethrough.ts @@ -0,0 +1,16 @@ +export default ` + + + + + +`; \ No newline at end of file diff --git a/src/packages/core/icon-registry/icons/icon-text-align-center.ts b/src/packages/core/icon-registry/icons/icon-text-align-center.ts new file mode 100644 index 0000000000..5d985fd584 --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-text-align-center.ts @@ -0,0 +1,16 @@ +export default ` + + + + + +`; \ No newline at end of file diff --git a/src/packages/core/icon-registry/icons/icon-text-align-justify.ts b/src/packages/core/icon-registry/icons/icon-text-align-justify.ts new file mode 100644 index 0000000000..7279356fc8 --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-text-align-justify.ts @@ -0,0 +1,16 @@ +export default ` + + + + + +`; \ No newline at end of file diff --git a/src/packages/core/icon-registry/icons/icon-text-align-left.ts b/src/packages/core/icon-registry/icons/icon-text-align-left.ts new file mode 100644 index 0000000000..43f4ebf794 --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-text-align-left.ts @@ -0,0 +1,16 @@ +export default ` + + + + + +`; \ No newline at end of file diff --git a/src/packages/core/icon-registry/icons/icon-text-align-right.ts b/src/packages/core/icon-registry/icons/icon-text-align-right.ts new file mode 100644 index 0000000000..a03f55eec2 --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-text-align-right.ts @@ -0,0 +1,16 @@ +export default ` + + + + + +`; \ No newline at end of file diff --git a/src/packages/core/icon-registry/icons/icon-underline.ts b/src/packages/core/icon-registry/icons/icon-underline.ts new file mode 100644 index 0000000000..84f133257c --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-underline.ts @@ -0,0 +1,15 @@ +export default ` + + + + +`; \ No newline at end of file diff --git a/src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts b/src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts deleted file mode 100644 index 996fc4482b..0000000000 --- a/src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { UmbIconRegistry } from '@umbraco-cms/backoffice/icon'; - -export class UmbTiptapIconRegistry extends UmbIconRegistry { - constructor() { - super(); - - this.defineIcon( - 'bold', - ``, - ); - - this.defineIcon( - 'italic', - ``, - ); - this.defineIcon( - 'underline', - ``, - ); - this.defineIcon( - 'strike', - ``, - ); - this.defineIcon( - 'heading1', - ``, - ); - this.defineIcon( - 'heading2', - ``, - ); - this.defineIcon( - 'heading3', - ``, - ); - this.defineIcon( - 'blockquote', - ``, - ); - this.defineIcon( - 'code-block', - ``, - ); - this.defineIcon( - 'bullet-list', - ``, - ); - this.defineIcon( - 'ordered-list', - ``, - ); - this.defineIcon( - 'horizontal-rule', - ``, - ); - this.defineIcon( - 'text-align-left', - ``, - ); - this.defineIcon( - 'text-align-center', - ``, - ); - this.defineIcon( - 'text-align-right', - ``, - ); - this.defineIcon( - 'text-align-justify', - ``, - ); - this.defineIcon( - 'link', - ``, - ); - this.defineIcon( - 'umbraco', - ``, - ); - } -} diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index ede9344903..88f211e458 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -1,5 +1,4 @@ import type { ManifestTiptapExtension } from '../../extensions/tiptap-extension.js'; -import { UmbTiptapIconRegistry } from './icon.registry.js'; import { css, customElement, html, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; @@ -24,13 +23,6 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { } #editor?: Editor; - #registry = new UmbTiptapIconRegistry(); - - constructor() { - super(); - this.#registry.attach(this); - } - override render() { return html` import('./embed.extension.js'), meta: { alias: 'umb-embed', - icon: 'umbraco', + icon: 'icon-embed', label: 'Embed', }, }; diff --git a/src/packages/rte/tiptap/extensions/mediapicker.extension.ts b/src/packages/rte/tiptap/extensions/mediapicker.extension.ts index 7e14c797dc..1c649a472a 100644 --- a/src/packages/rte/tiptap/extensions/mediapicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/mediapicker.extension.ts @@ -13,7 +13,7 @@ export const manifest: ManifestTiptapExtensionButtonKind = { api: () => import('./mediapicker.extension.js'), meta: { alias: 'umb-media', - icon: 'umbraco', + icon: 'icon-picture', label: 'Media picker', }, }; diff --git a/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts b/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts index 2a0fd2273e..3695f22d77 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts @@ -19,7 +19,8 @@ export class UmbTiptapToolbarButtonElement extends UmbLitElement { super.connectedCallback(); if (this.editor) { - this.editor.on('selectionUpdate', this.#onSelectionUpdate); + this.editor.on('selectionUpdate', this.#onEditorUpdate); + this.editor.on('update', this.#onEditorUpdate); } } @@ -27,11 +28,12 @@ export class UmbTiptapToolbarButtonElement extends UmbLitElement { super.disconnectedCallback(); if (this.editor) { - this.editor.off('selectionUpdate', this.#onSelectionUpdate); + this.editor.off('selectionUpdate', this.#onEditorUpdate); + this.editor.off('update', this.#onEditorUpdate); } } - #onSelectionUpdate = () => { + #onEditorUpdate = () => { if (this.api && this.editor && this.manifest) { this._isActive = this.api.isActive(this.editor); } diff --git a/src/packages/rte/tiptap/extensions/urlpicker.extension.ts b/src/packages/rte/tiptap/extensions/urlpicker.extension.ts index dec5d64d05..2781a41f1d 100644 --- a/src/packages/rte/tiptap/extensions/urlpicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/urlpicker.extension.ts @@ -11,7 +11,7 @@ export const manifest: ManifestTiptapExtensionButtonKind = { api: () => import('./urlpicker.extension.js'), meta: { alias: 'umb-link', - icon: 'umbraco', + icon: 'icon-link', label: 'URL picker', }, }; From 98cb2a586dd912267f58f405acee3c54509659d9 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Thu, 19 Sep 2024 15:48:49 +0100 Subject: [PATCH 083/241] Tiptap extension: Code Editor to edit the HTML source of the RTE --- .../extensions/code-editor.extension.ts | 43 +++++++++++++++++++ .../rte/tiptap/extensions/manifests.ts | 3 +- .../tiptap-toolbar-button.element.ts | 1 + 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 src/packages/rte/tiptap/extensions/code-editor.extension.ts diff --git a/src/packages/rte/tiptap/extensions/code-editor.extension.ts b/src/packages/rte/tiptap/extensions/code-editor.extension.ts new file mode 100644 index 0000000000..92ed2a4022 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/code-editor.extension.ts @@ -0,0 +1,43 @@ +import { UmbTiptapToolbarElementApiBase } from './types.js'; +import type { ManifestTiptapExtensionButtonKind } from './tiptap-extension.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; +import { UMB_CODE_EDITOR_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; + +export const manifest: ManifestTiptapExtensionButtonKind = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.CodeEditor', + name: 'Code Editor Tiptap Extension', + api: () => import('./code-editor.extension.js'), + weight: 1000, + meta: { + alias: 'umb-code-editor', + icon: 'icon-code', + label: '#general_viewSourceCode', + }, +}; + +export default class UmbTiptapCodeEditorExtensionApi extends UmbTiptapToolbarElementApiBase { + getTiptapExtensions = () => []; + + override async execute(editor?: Editor) { + console.log('umb-code-editor.execute', editor); + if (!editor) return; + + const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const modal = modalManager.open(this, UMB_CODE_EDITOR_MODAL, { + data: { + headline: 'Edit source code', + content: editor?.getHTML() ?? '', + language: 'html', + }, + }); + + if (!modal) return; + + const data = await modal.onSubmit().catch(() => undefined); + if (!data) return; + + editor?.commands.setContent(data.content, true); + } +} diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index 754fb3dcf1..d6fca8368f 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -1,5 +1,6 @@ import type { ManifestTiptapExtension } from './tiptap-extension.js'; import { manifests as core } from './core/manifests.js'; +import { manifest as codeEditor } from './code-editor.extension.js'; import { manifest as embed } from './embed.extension.js'; import { manifest as mediaPicker } from './mediapicker.extension.js'; import { manifest as urlPicker } from './urlpicker.extension.js'; @@ -17,6 +18,6 @@ const kinds: Array = [ }, ]; -const extensions: Array = [...core, embed, mediaPicker, urlPicker]; +const extensions: Array = [...core, codeEditor, embed, mediaPicker, urlPicker]; export const manifests: Array = [...kinds, ...extensions]; diff --git a/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts b/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts index 3695f22d77..17a418e613 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts @@ -45,6 +45,7 @@ export class UmbTiptapToolbarButtonElement extends UmbLitElement { compact look=${this._isActive ? 'outline' : 'default'} label=${ifDefined(this.manifest?.meta.label)} + title=${this.manifest?.meta.label ? this.localize.term(this.manifest.meta.label) : ''} @click=${() => this.api?.execute(this.editor)}> ${when( this.manifest?.meta.icon, From 28d7cbfc264e1d6fdc9e790d1fb30b220f382fd3 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:58:05 +0200 Subject: [PATCH 084/241] fix: types after merge --- .../tiptap/extensions/tiptap-media-upload.extension.ts | 4 ++-- src/packages/rte/tiptap/extensions/types.ts | 9 ++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts b/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts index a56d7184f7..a27f019fb6 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts @@ -1,4 +1,4 @@ -import { UmbTiptapExtensionApi, type UmbTiptapExtensionArgs } from './types.js'; +import { UmbTiptapExtensionApiBase, type UmbTiptapExtensionArgs } from './types.js'; import { TemporaryFileStatus, UmbTemporaryFileManager, @@ -11,7 +11,7 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; -export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi { +export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApiBase { #configuration?: UmbPropertyEditorConfigCollection; /** diff --git a/src/packages/rte/tiptap/extensions/types.ts b/src/packages/rte/tiptap/extensions/types.ts index da03130245..b10b98f2a3 100644 --- a/src/packages/rte/tiptap/extensions/types.ts +++ b/src/packages/rte/tiptap/extensions/types.ts @@ -2,20 +2,15 @@ import type { ManifestTiptapExtension } from './tiptap-extension.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { Editor, Extension, Mark, Node } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; export interface UmbTiptapExtensionApi extends UmbApi { - getTiptapExtensions(): Array; + getTiptapExtensions(args?: UmbTiptapExtensionArgs): Array; } -export abstract class UmbTiptapExtensionApiBase extends UmbControllerBase implements UmbApi { +export abstract class UmbTiptapExtensionApiBase extends UmbControllerBase implements UmbTiptapExtensionApi { public manifest?: ManifestTiptapExtension; - constructor(host: UmbControllerHost) { - super(host); - } - abstract getTiptapExtensions(args?: UmbTiptapExtensionArgs): Array; } From 6232a8bee07207a2baeb97bc5989ec412f8262fb Mon Sep 17 00:00:00 2001 From: leekelleher Date: Thu, 19 Sep 2024 16:26:25 +0100 Subject: [PATCH 085/241] Moved all the Tiptap extension manifests into a combined/single manifest --- .../input-tiptap/tiptap-fixed-menu.element.ts | 1 + .../toolbar}/tiptap-toolbar-button.element.ts | 4 +- .../extensions/core/blockquote.extension.ts | 15 -- .../tiptap/extensions/core/bold.extension.ts | 15 -- .../extensions/core/bullet-list.extension.ts | 15 -- .../extensions/core/code-block.extension.ts | 15 -- .../extensions/core/heading1.extension.ts | 15 -- .../extensions/core/heading2.extension.ts | 15 -- .../extensions/core/heading3.extension.ts | 15 -- .../core/horizontal-rule.extension.ts | 15 -- .../tiptap/extensions/core/image.extension.ts | 11 - .../extensions/core/italic.extension.ts | 15 -- .../rte/tiptap/extensions/core/manifests.ts | 255 +++++++++++++++--- .../extensions/core/ordered-list.extension.ts | 15 -- .../extensions/core/strike.extension.ts | 15 -- .../core/text-align-center.extension.ts | 15 -- .../core/text-align-justify.extension.ts | 15 -- .../core/text-align-left.extension.ts | 15 -- .../core/text-align-right.extension.ts | 15 -- .../extensions/core/underline.extension.ts | 15 -- .../rte/tiptap/extensions/embed.extension.ts | 25 -- .../rte/tiptap/extensions/manifests.ts | 62 ++++- .../{ => umb}/code-editor.extension.ts | 17 +- .../tiptap/extensions/umb/embed.extension.ts | 11 + .../{ => umb}/mediapicker.extension.ts | 16 +- .../{ => umb}/urlpicker.extension.ts | 16 +- 26 files changed, 291 insertions(+), 367 deletions(-) rename src/packages/rte/tiptap/{extensions => components/toolbar}/tiptap-toolbar-button.element.ts (91%) delete mode 100644 src/packages/rte/tiptap/extensions/embed.extension.ts rename src/packages/rte/tiptap/extensions/{ => umb}/code-editor.extension.ts (63%) create mode 100644 src/packages/rte/tiptap/extensions/umb/embed.extension.ts rename src/packages/rte/tiptap/extensions/{ => umb}/mediapicker.extension.ts (80%) rename src/packages/rte/tiptap/extensions/{ => umb}/urlpicker.extension.ts (62%) diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 88f211e458..5b879b5c4a 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -43,6 +43,7 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { color: var(--color-text); display: grid; grid-template-columns: repeat(auto-fill, minmax(24px, 1fr)); + gap: 4px; position: sticky; top: -25px; left: 0px; diff --git a/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts similarity index 91% rename from src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts rename to src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts index 17a418e613..fc3c7219bd 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts +++ b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts @@ -1,5 +1,5 @@ -import type { ManifestTiptapExtensionButtonKind } from './tiptap-extension.js'; -import type { UmbTiptapToolbarElementApi } from './types.js'; +import type { ManifestTiptapExtensionButtonKind } from '../../extensions/tiptap-extension.js'; +import type { UmbTiptapToolbarElementApi } from '../../extensions/types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import { customElement, html, ifDefined, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; diff --git a/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts b/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts index 22c2144b4e..d1ecfdfe19 100644 --- a/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Blockquote } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Blockquote', - name: 'Blockquote Tiptap Extension', - api: () => import('./blockquote.extension.js'), - weight: 995, - meta: { - alias: 'blockquote', - icon: 'icon-blockquote', - label: 'Blockquote', - }, -}; - export default class UmbTiptapBlockquoteExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Blockquote]; diff --git a/src/packages/rte/tiptap/extensions/core/bold.extension.ts b/src/packages/rte/tiptap/extensions/core/bold.extension.ts index c0d2d8485f..e65d167551 100644 --- a/src/packages/rte/tiptap/extensions/core/bold.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/bold.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Bold } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Bold', - name: 'Bold Tiptap Extension', - api: () => import('./bold.extension.js'), - weight: 999, - meta: { - alias: 'bold', - icon: 'icon-bold', - label: 'Bold', - }, -}; - export default class UmbTiptapBoldExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Bold]; diff --git a/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts b/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts index b73532a98c..8dd956279c 100644 --- a/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { BulletList, ListItem } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.BulletList', - name: 'Bullet List Tiptap Extension', - api: () => import('./bullet-list.extension.js'), - weight: 993, - meta: { - alias: 'bulletList', - icon: 'icon-bulleted-list', - label: 'Bullet List', - }, -}; - export default class UmbTiptapBulletListExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [BulletList, ListItem]; diff --git a/src/packages/rte/tiptap/extensions/core/code-block.extension.ts b/src/packages/rte/tiptap/extensions/core/code-block.extension.ts index 722c6c1c83..7cfa5861b2 100644 --- a/src/packages/rte/tiptap/extensions/core/code-block.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/code-block.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Code, CodeBlock } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.CodeBlock', - name: 'Code Block Tiptap Extension', - api: () => import('./code-block.extension.js'), - weight: 994, - meta: { - alias: 'codeBlock', - icon: 'icon-code', - label: 'Code Block', - }, -}; - export default class UmbTiptapCodeBlockExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Code, CodeBlock]; diff --git a/src/packages/rte/tiptap/extensions/core/heading1.extension.ts b/src/packages/rte/tiptap/extensions/core/heading1.extension.ts index 5872321e07..7543e321fb 100644 --- a/src/packages/rte/tiptap/extensions/core/heading1.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/heading1.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Heading1', - name: 'Heading 1 Tiptap Extension', - api: () => import('./heading1.extension.js'), - weight: 949, - meta: { - alias: 'heading1', - icon: 'icon-heading-1', - label: 'Heading 1', - }, -}; - export default class UmbTiptapHeading1ExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Heading]; diff --git a/src/packages/rte/tiptap/extensions/core/heading2.extension.ts b/src/packages/rte/tiptap/extensions/core/heading2.extension.ts index 22ca614266..3edcf7b57a 100644 --- a/src/packages/rte/tiptap/extensions/core/heading2.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/heading2.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Heading2', - name: 'Heading 2 Tiptap Extension', - api: () => import('./heading2.extension.js'), - weight: 948, - meta: { - alias: 'heading2', - icon: 'icon-heading-2', - label: 'Heading 2', - }, -}; - export default class UmbTiptapHeading2ExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Heading]; diff --git a/src/packages/rte/tiptap/extensions/core/heading3.extension.ts b/src/packages/rte/tiptap/extensions/core/heading3.extension.ts index 5662dd9636..9def84dc2c 100644 --- a/src/packages/rte/tiptap/extensions/core/heading3.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/heading3.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Heading3', - name: 'Heading 3 Tiptap Extension', - api: () => import('./heading3.extension.js'), - weight: 947, - meta: { - alias: 'heading3', - icon: 'icon-heading-3', - label: 'Heading 3', - }, -}; - export default class UmbTiptapHeading3ExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Heading]; diff --git a/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts b/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts index e796db3b8e..0219f45673 100644 --- a/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { HorizontalRule } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.HorizontalRule', - name: 'Horizontal Rule Tiptap Extension', - api: () => import('./horizontal-rule.extension.js'), - weight: 991, - meta: { - alias: 'horizontalRule', - icon: 'icon-horizontal-rule', - label: 'Horizontal Rule', - }, -}; - export default class UmbTiptapHorizontalRuleExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [HorizontalRule]; diff --git a/src/packages/rte/tiptap/extensions/core/image.extension.ts b/src/packages/rte/tiptap/extensions/core/image.extension.ts index f71f01ebd2..bf8b9956e9 100644 --- a/src/packages/rte/tiptap/extensions/core/image.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/image.extension.ts @@ -1,17 +1,6 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; import { UmbImage } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtension = { - type: 'tiptapExtension', - alias: 'Umb.Tiptap.Image', - name: 'Image Tiptap Extension', - api: () => import('./image.extension.js'), - meta: { - alias: 'image', - }, -}; - export default class UmbTiptapImageExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions() { return [UmbImage.configure({ inline: true })]; diff --git a/src/packages/rte/tiptap/extensions/core/italic.extension.ts b/src/packages/rte/tiptap/extensions/core/italic.extension.ts index 04de717a74..ff122f81e4 100644 --- a/src/packages/rte/tiptap/extensions/core/italic.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/italic.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Italic } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Italic', - name: 'Italic Tiptap Extension', - api: () => import('./italic.extension.js'), - weight: 998, - meta: { - alias: 'italic', - icon: 'icon-italic', - label: 'Italic', - }, -}; - export default class UmbTiptapItalicExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Italic]; diff --git a/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/packages/rte/tiptap/extensions/core/manifests.ts index f45ce80e1b..9ef58cd081 100644 --- a/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/packages/rte/tiptap/extensions/core/manifests.ts @@ -1,38 +1,221 @@ -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; -import { manifest as blockquote } from './blockquote.extension.js'; -import { manifest as bold } from './bold.extension.js'; -import { manifest as bulletList } from './bullet-list.extension.js'; -import { manifest as codeBlock } from './code-block.extension.js'; -import { manifest as image } from './image.extension.js'; -import { manifest as italic } from './italic.extension.js'; -import { manifest as heading1 } from './heading1.extension.js'; -import { manifest as heading2 } from './heading2.extension.js'; -import { manifest as heading3 } from './heading3.extension.js'; -import { manifest as horizontalRule } from './horizontal-rule.extension.js'; -import { manifest as orderedList } from './ordered-list.extension.js'; -import { manifest as strike } from './strike.extension.js'; -import { manifest as textAlignLeft } from './text-align-left.extension.js'; -import { manifest as textAlignCenter } from './text-align-center.extension.js'; -import { manifest as textAlignRight } from './text-align-right.extension.js'; -import { manifest as textAlignJustify } from './text-align-justify.extension.js'; -import { manifest as underline } from './underline.extension.js'; +import type { ManifestTiptapExtension, ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; -export const manifests: Array = [ - blockquote, - bold, - bulletList, - codeBlock, - image, - italic, - heading1, - heading2, - heading3, - horizontalRule, - orderedList, - strike, - textAlignLeft, - textAlignCenter, - textAlignRight, - textAlignJustify, - underline, +export const manifests: Array = [ + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Blockquote', + name: 'Blockquote Tiptap Extension', + api: () => import('./blockquote.extension.js'), + weight: 995, + meta: { + alias: 'blockquote', + icon: 'icon-blockquote', + label: 'Blockquote', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Bold', + name: 'Bold Tiptap Extension', + api: () => import('./bold.extension.js'), + weight: 999, + meta: { + alias: 'bold', + icon: 'icon-bold', + label: 'Bold', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.BulletList', + name: 'Bullet List Tiptap Extension', + api: () => import('./bullet-list.extension.js'), + weight: 993, + meta: { + alias: 'bulletList', + icon: 'icon-bulleted-list', + label: 'Bullet List', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.CodeBlock', + name: 'Code Block Tiptap Extension', + api: () => import('./code-block.extension.js'), + weight: 994, + meta: { + alias: 'codeBlock', + icon: 'icon-code', + label: 'Code Block', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Heading1', + name: 'Heading 1 Tiptap Extension', + api: () => import('./heading1.extension.js'), + weight: 949, + meta: { + alias: 'heading1', + icon: 'icon-heading-1', + label: 'Heading 1', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Heading2', + name: 'Heading 2 Tiptap Extension', + api: () => import('./heading2.extension.js'), + weight: 948, + meta: { + alias: 'heading2', + icon: 'icon-heading-2', + label: 'Heading 2', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Heading3', + name: 'Heading 3 Tiptap Extension', + api: () => import('./heading3.extension.js'), + weight: 947, + meta: { + alias: 'heading3', + icon: 'icon-heading-3', + label: 'Heading 3', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.HorizontalRule', + name: 'Horizontal Rule Tiptap Extension', + api: () => import('./horizontal-rule.extension.js'), + weight: 991, + meta: { + alias: 'horizontalRule', + icon: 'icon-horizontal-rule', + label: 'Horizontal Rule', + }, + }, + { + type: 'tiptapExtension', + alias: 'Umb.Tiptap.Image', + name: 'Image Tiptap Extension', + api: () => import('./image.extension.js'), + meta: { + alias: 'image', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Italic', + name: 'Italic Tiptap Extension', + api: () => import('./italic.extension.js'), + weight: 998, + meta: { + alias: 'italic', + icon: 'icon-italic', + label: 'Italic', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.OrderedList', + name: 'Ordered List Tiptap Extension', + api: () => import('./ordered-list.extension.js'), + weight: 992, + meta: { + alias: 'orderedList', + icon: 'icon-ordered-list', + label: 'Ordered List', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Strike', + name: 'Strike Tiptap Extension', + api: () => import('./strike.extension.js'), + weight: 996, + meta: { + alias: 'strike', + icon: 'icon-strikethrough', + label: 'Strike', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.TextAlignCenter', + name: 'Text Align Center Tiptap Extension', + api: () => import('./text-align-center.extension.js'), + weight: 918, + meta: { + alias: 'text-align-center', + icon: 'icon-text-align-center', + label: 'Text Align Center', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.TextAlignJustify', + name: 'Text Align Justify Tiptap Extension', + api: () => import('./text-align-justify.extension.js'), + weight: 916, + meta: { + alias: 'text-align-justify', + icon: 'icon-text-align-justify', + label: 'Text Align Justify', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.TextAlignLeft', + name: 'Text Align Left Tiptap Extension', + api: () => import('./text-align-left.extension.js'), + weight: 919, + meta: { + alias: 'text-align-left', + icon: 'icon-text-align-left', + label: 'Text Align Left', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.TextAlignRight', + name: 'Text Align Right Tiptap Extension', + api: () => import('./text-align-right.extension.js'), + weight: 917, + meta: { + alias: 'text-align-right', + icon: 'icon-text-align-right', + label: 'Text Align Right', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Underline', + name: 'Underline Tiptap Extension', + api: () => import('./underline.extension.js'), + weight: 997, + meta: { + alias: 'underline', + icon: 'icon-underline', + label: 'Underline', + }, + }, ]; diff --git a/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts b/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts index afa3433551..471d794e1c 100644 --- a/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { OrderedList, ListItem } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.OrderedList', - name: 'Ordered List Tiptap Extension', - api: () => import('./ordered-list.extension.js'), - weight: 992, - meta: { - alias: 'orderedList', - icon: 'icon-ordered-list', - label: 'Ordered List', - }, -}; - export default class UmbTiptapOrderedListExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [OrderedList, ListItem]; diff --git a/src/packages/rte/tiptap/extensions/core/strike.extension.ts b/src/packages/rte/tiptap/extensions/core/strike.extension.ts index b00939b028..b073c12dd9 100644 --- a/src/packages/rte/tiptap/extensions/core/strike.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/strike.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Strike } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Strike', - name: 'Strike Tiptap Extension', - api: () => import('./strike.extension.js'), - weight: 996, - meta: { - alias: 'strike', - icon: 'icon-strikethrough', - label: 'Strike', - }, -}; - export default class UmbTiptapStrikeExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Strike]; diff --git a/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts index c495cd77fd..fa9c90855c 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.TextAlignCenter', - name: 'Text Align Center Tiptap Extension', - api: () => import('./text-align-center.extension.js'), - weight: 918, - meta: { - alias: 'text-align-center', - icon: 'icon-text-align-center', - label: 'Text Align Center', - }, -}; - export default class UmbTiptapTextAlignCenterExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [ TextAlign.configure({ diff --git a/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts index d9818b16c0..03e197654f 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.TextAlignJustify', - name: 'Text Align Justify Tiptap Extension', - api: () => import('./text-align-justify.extension.js'), - weight: 916, - meta: { - alias: 'text-align-justify', - icon: 'icon-text-align-justify', - label: 'Text Align Justify', - }, -}; - export default class UmbTiptapTextAlignJustifyExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [ TextAlign.configure({ diff --git a/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts index 584a4dd5f4..2f35da46d2 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.TextAlignLeft', - name: 'Text Align Left Tiptap Extension', - api: () => import('./text-align-left.extension.js'), - weight: 919, - meta: { - alias: 'text-align-left', - icon: 'icon-text-align-left', - label: 'Text Align Left', - }, -}; - export default class UmbTiptapTextAlignLeftExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [ TextAlign.configure({ diff --git a/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts index 1fddf39ae0..62de9a54f9 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.TextAlignRight', - name: 'Text Align Right Tiptap Extension', - api: () => import('./text-align-right.extension.js'), - weight: 917, - meta: { - alias: 'text-align-right', - icon: 'icon-text-align-right', - label: 'Text Align Right', - }, -}; - export default class UmbTiptapTextAlignRightExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [ TextAlign.configure({ diff --git a/src/packages/rte/tiptap/extensions/core/underline.extension.ts b/src/packages/rte/tiptap/extensions/core/underline.extension.ts index 6b40dc881a..4e1bac6d6a 100644 --- a/src/packages/rte/tiptap/extensions/core/underline.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/underline.extension.ts @@ -1,22 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; import { Underline } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Underline', - name: 'Underline Tiptap Extension', - api: () => import('./underline.extension.js'), - weight: 997, - meta: { - alias: 'underline', - icon: 'icon-underline', - label: 'Underline', - }, -}; - export default class UmbTiptapUnderlineExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [Underline]; diff --git a/src/packages/rte/tiptap/extensions/embed.extension.ts b/src/packages/rte/tiptap/extensions/embed.extension.ts deleted file mode 100644 index d3aca48d5b..0000000000 --- a/src/packages/rte/tiptap/extensions/embed.extension.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { UmbTiptapToolbarElementApiBase } from './types.js'; -import type { ManifestTiptapExtensionButtonKind } from './tiptap-extension.js'; -import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; - -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Embed', - name: 'Embed Tiptap Extension', - api: () => import('./embed.extension.js'), - meta: { - alias: 'umb-embed', - icon: 'icon-embed', - label: 'Embed', - }, -}; - -export default class UmbTiptapEmbedExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions = () => []; - - override async execute(editor?: Editor) { - console.log('umb-embed.execute', editor); - // Research: https://github.com/ueberdosis/tiptap/tree/main/packages/extension-youtube - } -} diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index d6fca8368f..4b9394020d 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -1,9 +1,5 @@ -import type { ManifestTiptapExtension } from './tiptap-extension.js'; +import type { ManifestTiptapExtension, ManifestTiptapExtensionButtonKind } from './tiptap-extension.js'; import { manifests as core } from './core/manifests.js'; -import { manifest as codeEditor } from './code-editor.extension.js'; -import { manifest as embed } from './embed.extension.js'; -import { manifest as mediaPicker } from './mediapicker.extension.js'; -import { manifest as urlPicker } from './urlpicker.extension.js'; import type { ManifestTypes, UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; const kinds: Array = [ @@ -13,11 +9,63 @@ const kinds: Array = [ matchKind: 'button', matchType: 'tiptapExtension', manifest: { - element: () => import('./tiptap-toolbar-button.element.js'), + element: () => import('../components/toolbar/tiptap-toolbar-button.element.js'), }, }, ]; -const extensions: Array = [...core, codeEditor, embed, mediaPicker, urlPicker]; +const umbExtensions: Array = [ + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.CodeEditor', + name: 'Code Editor Tiptap Extension', + api: () => import('./umb/code-editor.extension.js'), + weight: 1000, + meta: { + alias: 'umb-code-editor', + icon: 'icon-code', + label: '#general_viewSourceCode', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Embed', + name: 'Embed Tiptap Extension', + api: () => import('./umb/embed.extension.js'), + meta: { + alias: 'umb-embed', + icon: 'icon-embed', + label: 'Embed', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.MediaPicker', + name: 'Media Picker Tiptap Extension', + api: () => import('./umb/mediapicker.extension.js'), + meta: { + alias: 'umb-media', + icon: 'icon-picture', + label: 'Media picker', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.UrlPicker', + name: 'URL Picker Tiptap Extension', + api: () => import('./umb/urlpicker.extension.js'), + meta: { + alias: 'umb-link', + icon: 'icon-link', + label: 'URL picker', + }, + }, +]; + +const extensions: Array = [...core, ...umbExtensions]; export const manifests: Array = [...kinds, ...extensions]; diff --git a/src/packages/rte/tiptap/extensions/code-editor.extension.ts b/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts similarity index 63% rename from src/packages/rte/tiptap/extensions/code-editor.extension.ts rename to src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts index 92ed2a4022..be55c65dbe 100644 --- a/src/packages/rte/tiptap/extensions/code-editor.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts @@ -1,22 +1,7 @@ -import { UmbTiptapToolbarElementApiBase } from './types.js'; -import type { ManifestTiptapExtensionButtonKind } from './tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import { UMB_CODE_EDITOR_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.CodeEditor', - name: 'Code Editor Tiptap Extension', - api: () => import('./code-editor.extension.js'), - weight: 1000, - meta: { - alias: 'umb-code-editor', - icon: 'icon-code', - label: '#general_viewSourceCode', - }, -}; - export default class UmbTiptapCodeEditorExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => []; diff --git a/src/packages/rte/tiptap/extensions/umb/embed.extension.ts b/src/packages/rte/tiptap/extensions/umb/embed.extension.ts new file mode 100644 index 0000000000..f09ecfd084 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/umb/embed.extension.ts @@ -0,0 +1,11 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapEmbedExtensionApi extends UmbTiptapToolbarElementApiBase { + getTiptapExtensions = () => []; + + override async execute(editor?: Editor) { + console.log('umb-embed.execute', editor); + // Research: https://github.com/ueberdosis/tiptap/tree/main/packages/extension-youtube + } +} diff --git a/src/packages/rte/tiptap/extensions/mediapicker.extension.ts b/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts similarity index 80% rename from src/packages/rte/tiptap/extensions/mediapicker.extension.ts rename to src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts index 1c649a472a..6947d772b8 100644 --- a/src/packages/rte/tiptap/extensions/mediapicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts @@ -1,23 +1,9 @@ -import { UmbTiptapToolbarElementApiBase } from './types.js'; -import type { ManifestTiptapExtensionButtonKind } from './tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; import { mergeAttributes, Node } from '@umbraco-cms/backoffice/external/tiptap'; import { UMB_MEDIA_PICKER_MODAL } from '@umbraco-cms/backoffice/media'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.MediaPicker', - name: 'Media Picker Tiptap Extension', - api: () => import('./mediapicker.extension.js'), - meta: { - alias: 'umb-media', - icon: 'icon-picture', - label: 'Media picker', - }, -}; - export default class UmbTiptapMediaPickerExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions() { return [ diff --git a/src/packages/rte/tiptap/extensions/urlpicker.extension.ts b/src/packages/rte/tiptap/extensions/umb/urlpicker.extension.ts similarity index 62% rename from src/packages/rte/tiptap/extensions/urlpicker.extension.ts rename to src/packages/rte/tiptap/extensions/umb/urlpicker.extension.ts index 2781a41f1d..7bedab735b 100644 --- a/src/packages/rte/tiptap/extensions/urlpicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/urlpicker.extension.ts @@ -1,21 +1,7 @@ -import { UmbTiptapToolbarElementApiBase } from './types.js'; -import type { ManifestTiptapExtensionButtonKind } from './tiptap-extension.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; import { Link } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export const manifest: ManifestTiptapExtensionButtonKind = { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.UrlPicker', - name: 'URL Picker Tiptap Extension', - api: () => import('./urlpicker.extension.js'), - meta: { - alias: 'umb-link', - icon: 'icon-link', - label: 'URL picker', - }, -}; - export default class UmbTiptapUrlPickerExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions() { return [Link.extend({ openOnClick: false })]; From f979669bb9da96a444b46f6e00c07c7ada77195f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:04:21 +0200 Subject: [PATCH 086/241] feat: move imageSize function to util file --- .../extensions/umb/media-upload.extension.ts | 32 ++----------------- src/packages/rte/tiptap/index.ts | 1 + src/packages/rte/tiptap/utils/index.ts | 28 ++++++++++++++++ 3 files changed, 31 insertions(+), 30 deletions(-) create mode 100644 src/packages/rte/tiptap/utils/index.ts diff --git a/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts b/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts index 57d02b8ccf..a1412bdc07 100644 --- a/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts @@ -1,3 +1,4 @@ +import { imageSize } from '../../utils/index.js'; import { UmbTiptapExtensionApiBase, type UmbTiptapExtensionArgs } from '../types.js'; import { TemporaryFileStatus, @@ -98,7 +99,7 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi return; } - let { width, height } = await this.#imageSize(URL.createObjectURL(upload.file)); + let { width, height } = await imageSize(URL.createObjectURL(upload.file)); if (maxImageSize > 0 && width > maxImageSize) { const ratio = maxImageSize / width; @@ -131,33 +132,4 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi #filterFiles(files: FileList): File[] { return Array.from(files).filter((file) => this.allowedFileTypes.includes(file.type)); } - - /** - * Get the dimensions of an image from a URL. - * @param {string} url The URL of the image. It can be a local file (blob url) or a remote file. - * @returns {Promise<{width: number, height: number}>} The width and height of the image as downloaded from the URL. - */ - #imageSize(url: string): Promise<{ width: number; height: number }> { - const img = new Image(); - - const promise = new Promise<{ width: number; height: number }>((resolve, reject) => { - img.onload = () => { - // Natural size is the actual image size regardless of rendering. - // The 'normal' `width`/`height` are for the **rendered** size. - const width = img.naturalWidth; - const height = img.naturalHeight; - - // Resolve promise with the width and height - resolve({ width, height }); - }; - - // Reject promise on error - img.onerror = reject; - }); - - // Setting the source makes it start downloading and eventually call `onload` - img.src = url; - - return promise; - } } diff --git a/src/packages/rte/tiptap/index.ts b/src/packages/rte/tiptap/index.ts index f0f1ade33d..5a6a198bdb 100644 --- a/src/packages/rte/tiptap/index.ts +++ b/src/packages/rte/tiptap/index.ts @@ -1,2 +1,3 @@ export * from './components/index.js'; export * from './extensions/index.js'; +export * from './utils/index.js'; diff --git a/src/packages/rte/tiptap/utils/index.ts b/src/packages/rte/tiptap/utils/index.ts new file mode 100644 index 0000000000..cbd93b58fe --- /dev/null +++ b/src/packages/rte/tiptap/utils/index.ts @@ -0,0 +1,28 @@ +/** + * Get the dimensions of an image from a URL. + * @param {string} url The URL of the image. It can be a local file (blob url) or a remote file. + * @returns {Promise<{width: number, height: number}>} The width and height of the image as downloaded from the URL. + */ +export function imageSize(url: string): Promise<{ width: number; height: number }> { + const img = new Image(); + + const promise = new Promise<{ width: number; height: number }>((resolve, reject) => { + img.onload = () => { + // Natural size is the actual image size regardless of rendering. + // The 'normal' `width`/`height` are for the **rendered** size. + const width = img.naturalWidth; + const height = img.naturalHeight; + + // Resolve promise with the width and height + resolve({ width, height }); + }; + + // Reject promise on error + img.onerror = reject; + }); + + // Setting the source makes it start downloading and eventually call `onload` + img.src = url; + + return promise; +} From 8fe308ef508bf05211da773d9891481f3e8abc7d Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:09:39 +0200 Subject: [PATCH 087/241] feat: move imageSize function to general utils --- src/packages/core/utils/index.ts | 1 + .../utils/index.ts => core/utils/media/image-size.function.ts} | 0 .../rte/tiptap/extensions/umb/media-upload.extension.ts | 3 ++- src/packages/rte/tiptap/index.ts | 1 - 4 files changed, 3 insertions(+), 2 deletions(-) rename src/packages/{rte/tiptap/utils/index.ts => core/utils/media/image-size.function.ts} (100%) diff --git a/src/packages/core/utils/index.ts b/src/packages/core/utils/index.ts index 483767c50a..abbc560792 100644 --- a/src/packages/core/utils/index.ts +++ b/src/packages/core/utils/index.ts @@ -3,6 +3,7 @@ export * from './direction/index.js'; export * from './download/blob-download.function.js'; export * from './get-processed-image-url.function.js'; export * from './math/math.js'; +export * from './media/image-size.function.js'; export * from './object/deep-merge.function.js'; export * from './pagination-manager/pagination.manager.js'; export * from './path/ensure-local-path.function.js'; diff --git a/src/packages/rte/tiptap/utils/index.ts b/src/packages/core/utils/media/image-size.function.ts similarity index 100% rename from src/packages/rte/tiptap/utils/index.ts rename to src/packages/core/utils/media/image-size.function.ts diff --git a/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts b/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts index a1412bdc07..ef23e3c0ed 100644 --- a/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts @@ -1,10 +1,10 @@ -import { imageSize } from '../../utils/index.js'; import { UmbTiptapExtensionApiBase, type UmbTiptapExtensionArgs } from '../types.js'; import { TemporaryFileStatus, UmbTemporaryFileManager, type UmbTemporaryFileModel, } from '@umbraco-cms/backoffice/temporary-file'; +import { imageSize } from '@umbraco-cms/backoffice/utils'; import { type Editor, UmbImage } from '@umbraco-cms/backoffice/external/tiptap'; import { UmbId } from '@umbraco-cms/backoffice/id'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; @@ -85,6 +85,7 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi this.dispatchEvent(new CustomEvent('rte.file.uploading', { composed: true, bubbles: true, detail: fileModels })); + // TODO: Image upload folder?? const uploads = await this.#manager.upload(fileModels); const maxImageSize = this.maxWidth; diff --git a/src/packages/rte/tiptap/index.ts b/src/packages/rte/tiptap/index.ts index 5a6a198bdb..f0f1ade33d 100644 --- a/src/packages/rte/tiptap/index.ts +++ b/src/packages/rte/tiptap/index.ts @@ -1,3 +1,2 @@ export * from './components/index.js'; export * from './extensions/index.js'; -export * from './utils/index.js'; From fc5808e7c0f044f3791ca0b2e625bfe49b07889c Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:13:45 +0200 Subject: [PATCH 088/241] chore: remove comment --- src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts b/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts index ef23e3c0ed..2a2278597f 100644 --- a/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts @@ -85,7 +85,6 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi this.dispatchEvent(new CustomEvent('rte.file.uploading', { composed: true, bubbles: true, detail: fileModels })); - // TODO: Image upload folder?? const uploads = await this.#manager.upload(fileModels); const maxImageSize = this.maxWidth; From e8d2015b65f5030cdca06162bff4437dea7e256f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:20:27 +0200 Subject: [PATCH 089/241] feat: create a pure extension rather than depend on the UmbImage extension as we don't need all of that --- .../rte/tiptap/extensions/umb/media-upload.extension.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts b/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts index 2a2278597f..ec9e7cfd5d 100644 --- a/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts @@ -5,7 +5,7 @@ import { type UmbTemporaryFileModel, } from '@umbraco-cms/backoffice/temporary-file'; import { imageSize } from '@umbraco-cms/backoffice/utils'; -import { type Editor, UmbImage } from '@umbraco-cms/backoffice/external/tiptap'; +import { type Editor, Extension } from '@umbraco-cms/backoffice/external/tiptap'; import { UmbId } from '@umbraco-cms/backoffice/id'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -49,7 +49,7 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi // eslint-disable-next-line @typescript-eslint/no-this-alias const self = this; return [ - UmbImage.extend({ + Extension.create({ name: 'umbMediaUpload', onCreate() { this.parent?.(); From bd66bdd03a16bd48fccdf71d5ab798f6c0018cb3 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 20 Sep 2024 12:07:14 +0200 Subject: [PATCH 090/241] feat: add maxImageSize --- .../rte/tiptap/property-editors/tiptap/manifests.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 471dd9dd5e..10051e7b93 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -95,6 +95,14 @@ export const manifests: Array = [ propertyEditorUiAlias: 'Umb.PropertyEditorUi.Integer', weight: 30, }, + { + alias: 'maxImageSize', + label: 'Maximum size for inserted images', + description: 'Maximum width or height - enter 0 to disable resizing', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.Integer', + weight: 40, + config: [{ alias: 'min', value: 0 }], + }, ], defaultData: [], }, From 6159a8d4195739ddfbea07cf02ea98f158210f49 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:41:35 +0200 Subject: [PATCH 091/241] fix: read blocks from config --- .../tiptap/property-editor-ui-tiptap.element.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index b735aee02a..a4fac1bf92 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -3,7 +3,7 @@ import { customElement, html, property, state } from '@umbraco-cms/backoffice/ex import { UmbBlockRteEntriesContext, UmbBlockRteManagerContext } from '@umbraco-cms/backoffice/block-rte'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; -import type { UmbBlockRteLayoutModel } from '@umbraco-cms/backoffice/block-rte'; +import type { UmbBlockRteLayoutModel, UmbBlockRteTypeModel } from '@umbraco-cms/backoffice/block-rte'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; @@ -25,9 +25,15 @@ const elementName = 'umb-property-editor-ui-tiptap'; */ @customElement(elementName) export class UmbPropertyEditorUiTiptapElement extends UmbLitElement implements UmbPropertyEditorUiElement { - // public set config(config: UmbPropertyEditorConfigCollection | undefined) { + if (!config) return; + this._config = config; + + const blocks = config.getValueByAlias>('blocks') ?? []; + this.#managerContext.setBlockTypes(blocks); + + this.#managerContext.setEditorConfiguration(config); } @property({ From 58ca4b646cfead43cdfc659532dbead1581f419a Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:42:12 +0200 Subject: [PATCH 092/241] new group element --- ...ui-tiptap-toolbar-configuration.element.ts | 3 + ...ap-toolbar-groups-configuration.element.ts | 202 ++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 1014ceda82..54107b1c7b 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -8,6 +8,8 @@ import { type UmbPropertyEditorConfigCollection, } from '@umbraco-cms/backoffice/property-editor'; +import './tiptap-toolbar-groups-configuration.element.js'; + import { tinymce } from '@umbraco-cms/backoffice/external/tinymce'; const tinyIconSet = tinymce.IconManager.get('default'); @@ -230,6 +232,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement } override render() { + return html``; return html`
    ${repeat(this._selectedValuesNew, (row, index) => this.#renderRow(row, index))} diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts new file mode 100644 index 0000000000..5f15c61848 --- /dev/null +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts @@ -0,0 +1,202 @@ +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { customElement, css, html, property, repeat } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbId } from '@umbraco-cms/backoffice/id'; + +type ToolbarButton = { + alias: string; + label: string; + icon: string; +}; + +type ToolbarGroup = { + groupId: string; + buttons: ToolbarButton[]; +}; + +type ToolbarRow = { + rowId: string; + groups: ToolbarGroup[]; +}; + +type ToolbarConfig = ToolbarRow[]; + +const toolbarConfig: ToolbarConfig = [ + { + rowId: 'asdasgasgasd', + groups: [ + { + groupId: 'asdasldjh12h123', + buttons: [ + { alias: 'bold', label: 'Bold', icon: 'bold-icon' }, + { alias: 'italic', label: 'Italic', icon: 'italic-icon' }, + ], + }, + { + groupId: 'askdjljk123ljkh12kj3h', + buttons: [{ alias: 'underline', label: 'Underline', icon: 'underline-icon' }], + }, + ], + }, + { + rowId: '1l2j3ljk123l21j3', + groups: [ + { + groupId: 'asdashd9ashd0as87hdoasudh', + buttons: [{ alias: 'align-left', label: 'Align Left', icon: 'align-left-icon' }], + }, + ], + }, +]; + +@customElement('umb-tiptap-toolbar-groups-configuration') +export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { + @property({ attribute: false }) + set value(value: string) { + this.#value = value as string; + + this.requestUpdate('#selectedValuesNew'); + } + get value(): string { + return this.#value; + } + + #value = ''; + + #addGroup = (rowId) => { + toolbarConfig[toolbarConfig.length - 1].groups.push({ + groupId: UmbId.new(), + buttons: [], + }); + + this.requestUpdate(); + }; + + #removeGroup(groupId: string) { + const groupIndex = toolbarConfig.findIndex((row) => row.groups.some((group) => group.groupId === groupId)); + if (groupIndex === -1) return; + + toolbarConfig.splice(groupIndex, 1); + + this.requestUpdate(); + } + + #addRow = () => { + toolbarConfig.push({ + rowId: UmbId.new(), + groups: [ + { + groupId: UmbId.new(), + buttons: [], + }, + ], + }); + + this.requestUpdate(); + }; + + #removeRow(rowId: number) { + const rowIndex = toolbarConfig.findIndex((row) => row.rowId === rowId); + if (rowIndex === -1) return; + + toolbarConfig.splice(rowIndex, 1); + + this.requestUpdate(); + } + + #moveButton(alias: string, targetGroupId: string) { + const sourceGroup = toolbarConfig + .flatMap((row) => row.groups) + .find((group) => group.buttons.some((button) => button.alias === alias)); + + if (!sourceGroup) return; + + // remove button from source group + const buttonIndex = sourceGroup.buttons.findIndex((button) => button.alias === alias); + const button = sourceGroup.buttons.splice(buttonIndex, 1)[0]; + + // add button to target group + const targetGroup = toolbarConfig.flatMap((row) => row.groups).find((group) => group.groupId === targetGroupId); + if (!targetGroup) return; + + targetGroup.buttons.push(button); + + this.requestUpdate(); + } + + #onDragStart = (event: DragEvent, alias: string) => { + event.dataTransfer!.setData('text/plain', alias); + event.dataTransfer!.dropEffect = 'move'; + }; + + #onDragOver = (event: DragEvent) => { + event.preventDefault(); + }; + + #onDrop = (event: DragEvent, groupId: string) => { + event.preventDefault(); + + const alias = event.dataTransfer!.getData('text/plain'); + if (!alias) return; + + this.#moveButton(alias, groupId); + }; + + #renderButton = (button: ToolbarButton) => { + return html``; + }; + + #renderGroup = (group: ToolbarGroup) => { + return html`
    this.#onDrop(e, group.groupId)}> + ${repeat(group.buttons, (button) => button.alias, this.#renderButton)} +
    `; + }; + + #renderRow = (row: ToolbarRow) => { + return html`
    ${repeat(row.groups, (group) => group.groupId, this.#renderGroup)}
    `; + }; + + override render() { + return html` + + + ${repeat(toolbarConfig, (row) => row.rowId, this.#renderRow)}`; + } + + static override styles = [ + UmbTextStyles, + css` + :host { + display: flex; + flex-direction: column; + gap: 6px; + } + .row { + display: flex; + gap: 12px; + } + .group { + display: flex; + gap: 3px; + border: 1px solid #ccc; + padding: 6px; + min-height: 24px; + min-width: 24px; + } + `, + ]; +} + +export default UmbTiptapToolbarGroupsConfigurationElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-tiptap-toolbar-groups-configuration': UmbTiptapToolbarGroupsConfigurationElement; + } +} From 09cd18972123f8ad508497bf0c259952e08eff8b Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:54:49 +0200 Subject: [PATCH 093/241] remove buttons for rows and groups --- ...ap-toolbar-groups-configuration.element.ts | 53 ++++++++++++++----- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index 5f15c61848..6a9339d3dc 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts @@ -21,7 +21,7 @@ type ToolbarRow = { type ToolbarConfig = ToolbarRow[]; -const toolbarConfig: ToolbarConfig = [ +let toolbarConfig: ToolbarConfig = [ { rowId: 'asdasgasgasd', groups: [ @@ -63,8 +63,11 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { #value = ''; - #addGroup = (rowId) => { - toolbarConfig[toolbarConfig.length - 1].groups.push({ + #addGroup = (rowId: string) => { + const row = toolbarConfig.find((row) => row.rowId === rowId); + if (!row) return; + + row.groups.push({ groupId: UmbId.new(), buttons: [], }); @@ -73,10 +76,10 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { }; #removeGroup(groupId: string) { - const groupIndex = toolbarConfig.findIndex((row) => row.groups.some((group) => group.groupId === groupId)); - if (groupIndex === -1) return; + const row = toolbarConfig.find((row) => row.groups.some((group) => group.groupId === groupId)); + if (!row) return; - toolbarConfig.splice(groupIndex, 1); + row.groups = row.groups.filter((group) => group.groupId !== groupId); this.requestUpdate(); } @@ -95,11 +98,8 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { this.requestUpdate(); }; - #removeRow(rowId: number) { - const rowIndex = toolbarConfig.findIndex((row) => row.rowId === rowId); - if (rowIndex === -1) return; - - toolbarConfig.splice(rowIndex, 1); + #removeRow(rowId: string) { + toolbarConfig = toolbarConfig.filter((row) => row.rowId !== rowId); this.requestUpdate(); } @@ -155,11 +155,18 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { @dragover=${this.#onDragOver} @drop=${(e: DragEvent) => this.#onDrop(e, group.groupId)}> ${repeat(group.buttons, (button) => button.alias, this.#renderButton)} +
    `; }; #renderRow = (row: ToolbarRow) => { - return html`
    ${repeat(row.groups, (group) => group.groupId, this.#renderGroup)}
    `; + return html`
    + ${repeat(row.groups, (group) => group.groupId, this.#renderGroup)} + +
    `; }; override render() { @@ -178,10 +185,12 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { gap: 6px; } .row { + position: relative; display: flex; gap: 12px; } .group { + position: relative; display: flex; gap: 3px; border: 1px solid #ccc; @@ -189,6 +198,26 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { min-height: 24px; min-width: 24px; } + + .remove-group-button { + position: absolute; + top: -4px; + right: -4px; + display: none; + } + .group:hover .remove-group-button { + display: block; + } + + .remove-row-button { + position: absolute; + left: -25px; + top: 8px; + display: none; + } + .row:hover .remove-row-button { + display: block; + } `, ]; } From ea6e87eeee579abb7be698fde765cb9b76a843ff Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 24 Sep 2024 14:20:34 +0200 Subject: [PATCH 094/241] basic persistence --- ...ui-tiptap-toolbar-configuration.element.ts | 259 +++++------------- ...ap-toolbar-groups-configuration.element.ts | 79 +++--- 2 files changed, 110 insertions(+), 228 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 54107b1c7b..d59e2a6ed0 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -14,7 +14,22 @@ import { tinymce } from '@umbraco-cms/backoffice/external/tinymce'; const tinyIconSet = tinymce.IconManager.get('default'); -type ToolbarConfig = { +type EditorExtension = { + alias: string; + label: string; + icon?: string; + hideInToolbar: boolean; + row: number; + group: [number, number]; +}; + +type EditorExtensionValue = { + alias: string; + hideInToolbar: boolean; + position: [number, number]; +}; + +type ExtensionConfig = { alias: string; label: string; icon?: string; @@ -22,66 +37,54 @@ type ToolbarConfig = { category: string; }; -type ToolbarItems = Array<{ - name: string; - items: ToolbarConfig[]; +type ExtensionCategory = Array<{ + category: string; + extensions: ExtensionConfig[]; }>; -/** - * @element umb-property-editor-ui-tiptap-toolbar-configuration - */ @customElement('umb-property-editor-ui-tiptap-toolbar-configuration') export class UmbPropertyEditorUiTiptapToolbarConfigurationElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property({ attribute: false }) - set value(value: string | string[] | null) { - if (!value) { - this.#selectedValues = []; - } else { - if (typeof value === 'string') { - this.#selectedValues = value.split(',').filter((x) => x.length > 0); - } else if (Array.isArray(value)) { - this.#selectedValues = value; - } else { - this.#selectedValues = []; - } - } + set value(value: Array) { + if (!value) value = []; - this.requestUpdate('#selectedValuesNew'); + this.#value = value; + this.requestUpdate('#value'); } - get value(): string[] { - return this.#selectedValues; + get value(): Array { + return this.#value; } + #value: Array = []; + @property({ attribute: false }) config?: UmbPropertyEditorConfigCollection; @state() - private _toolbarItems: ToolbarItems = []; - - @state() - private _toolbarConfig: Array = []; + private _extensionCategories: ExtensionCategory = []; @state() - _selectedValuesNew: ToolbarConfig[][][] = [[[]]]; - - #selectedValues: string[] = []; - - #hoveredDropzone: HTMLElement | null = null; // Will be used to sort extensions in a group in the toolbar + private _extensionsConfig: Array = []; protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); - this.config?.getValueByAlias('toolbar')?.forEach((v) => { - this._toolbarConfig.push({ + const toolbarConfig = this.config?.getValueByAlias('toolbar'); + if (!toolbarConfig) return; + + const extensions: Array = []; + + toolbarConfig.forEach((v) => { + extensions.push({ ...v, - selected: this.value.includes(v.alias), + selected: this.value.some((x) => x.alias == v.alias), }); }); - const grouped = this._toolbarConfig.reduce((acc: any, item) => { + const grouped = extensions.reduce((acc: any, item) => { const group = item.category || 'miscellaneous'; // Assign to "miscellaneous" if no group if (!acc[group]) { @@ -91,164 +94,62 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement return acc; }, {}); - this._toolbarItems = Object.keys(grouped).map((group) => ({ - name: group.charAt(0).toUpperCase() + group.slice(1).replace(/-/g, ' '), - items: grouped[group], + this._extensionCategories = Object.keys(grouped).map((group) => ({ + category: group.charAt(0).toUpperCase() + group.slice(1).replace(/-/g, ' '), + extensions: grouped[group], })); this.requestUpdate('_toolbarConfig'); } - #onExtensionSelect(item: ToolbarConfig, row?: number, group?: number) { - // if no row is provided, add to the last row and last group - if (row === undefined) { - row = this._selectedValuesNew.length - 1; - } + #onExtensionSelect(item: ExtensionConfig) { + item.selected = !item.selected; - // if no group is provided, add to the last group in the row - if (group === undefined) { - group = this._selectedValuesNew[row].length - 1; + if (item.selected) { + this.value = [...this.value, { alias: item.alias, hideInToolbar: false, position: [0, 0] }]; + } else { + this.value = this.value.filter((x) => x.alias !== item.alias); } - // Add the item to the selectedValuesNew array - this._selectedValuesNew[row][group].push(item); - this.requestUpdate('_selectedValuesNew'); - } - - #addGroup(row: number) { - this._selectedValuesNew[row].push([]); - this.requestUpdate('_selectedValuesNew'); - } - - #addRow() { - this._selectedValuesNew.push([[]]); - this.requestUpdate('_selectedValuesNew'); - } - - #onChange = (item: ToolbarConfig) => { - const value = this._toolbarItems - .flatMap((group) => - group.items.map((i) => { - if (i.alias === item.alias) { - i.selected = !i.selected; - } - return i.selected ? i.alias : null; - }), - ) - .filter((v): v is string => v !== null); // Ensures we only keep non-null strings - - // If the value array is empty, set this.value to null, otherwise assign the array - this.value = value.length > 0 ? value : null; - this.dispatchEvent(new UmbPropertyValueChangeEvent()); - }; - - #onDragStart = (event: DragEvent, alias: string) => { - event.dataTransfer!.setData('text/plain', alias); - event.dataTransfer!.dropEffect = 'move'; - event.dataTransfer!.effectAllowed = 'move'; - }; - - #onDragOver = (event: DragEvent) => { - event.preventDefault(); - }; - - #onDragEnter = (event: DragEvent) => { - const dropzone = event - .composedPath() - .find((v) => v instanceof HTMLElement && v.classList.contains('toolbar-group') && v.hasAttribute('dropzone')); - - this.#hoveredDropzone = (dropzone as HTMLElement) || null; - console.log('hovered dropzone', this.#hoveredDropzone); - }; - - #onDrop = (event: DragEvent) => { - event.preventDefault(); - - const groupElement = event - .composedPath() - .find( - (v) => v instanceof HTMLElement && v.classList.contains('toolbar-group') && v.hasAttribute('dropzone'), - ) as HTMLElement; - - if (!groupElement) return; - - const alias = event.dataTransfer!.getData('text/plain'); - if (!alias) return; - - const item = this._toolbarConfig.find((v) => v.alias === alias); - if (!item) return; - - const rowAttribute = groupElement.getAttribute('umb-data-row'); - const rowIndex = rowAttribute ? Number.parseInt(rowAttribute) : null; - - const groupAttribute = groupElement.getAttribute('umb-data-group'); - const groupIndex = groupAttribute ? Number.parseInt(groupAttribute) : null; - - if (groupIndex === null || rowIndex === null) return; - - // remove alias from selectedValues - this._selectedValuesNew = this._selectedValuesNew.map((row) => - row.map((group) => group.filter((v) => v.alias !== alias)), - ); - - this.#onExtensionSelect(item, rowIndex, groupIndex); - }; - - #renderRow(row: ToolbarConfig[][], rowIndex: number) { - return html`
    - ${row.map((group, index) => { - return this.#renderGroup(group, index, rowIndex); - })} - this.#addGroup(rowIndex)}>Add group -
    `; } - #renderGroup(group: ToolbarConfig[], groupIndex: number, rowIndex: number) { - return html`
    - ${group.map((item) => { - return html` - this.#onDragStart(e, item.alias)} - compact - look="outline" - class=${item.selected ? 'selected' : ''} - label=${item.label} - .value=${item.alias} - @click=${() => this.#onChange(item)} - > - `; - })} -
    `; + #test() { + this.value = [ + { + alias: 'bold', + hideInToolbar: false, + position: [0, 0], + }, + { + alias: 'italic', + hideInToolbar: false, + position: [0, 0], + }, + { + alias: 'underline', + hideInToolbar: false, + position: [0, 0], + }, + ]; + this.dispatchEvent(new UmbPropertyValueChangeEvent()); } override render() { - return html``; return html` -
    - ${repeat(this._selectedValuesNew, (row, index) => this.#renderRow(row, index))} - this.#addRow()}>Add row -
    + +
    ${repeat( - this._toolbarItems, + this._extensionCategories, (category) => html`

    - ${category.name} + ${category.category} Hide in toolbar

    ${repeat( - category.items, + category.extensions, (item) => html`
    { - const row = toolbarConfig.find((row) => row.rowId === rowId); + const row = this.#value.find((row) => row.rowId === rowId); if (!row) return; row.groups.push({ @@ -76,7 +75,7 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { }; #removeGroup(groupId: string) { - const row = toolbarConfig.find((row) => row.groups.some((group) => group.groupId === groupId)); + const row = this.#value.find((row) => row.groups.some((group) => group.groupId === groupId)); if (!row) return; row.groups = row.groups.filter((group) => group.groupId !== groupId); @@ -85,7 +84,7 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { } #addRow = () => { - toolbarConfig.push({ + this.#value.push({ rowId: UmbId.new(), groups: [ { @@ -99,13 +98,13 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { }; #removeRow(rowId: string) { - toolbarConfig = toolbarConfig.filter((row) => row.rowId !== rowId); + this.#value = this.#value.filter((row) => row.rowId !== rowId); this.requestUpdate(); } #moveButton(alias: string, targetGroupId: string) { - const sourceGroup = toolbarConfig + const sourceGroup = this.#value .flatMap((row) => row.groups) .find((group) => group.buttons.some((button) => button.alias === alias)); @@ -116,7 +115,7 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { const button = sourceGroup.buttons.splice(buttonIndex, 1)[0]; // add button to target group - const targetGroup = toolbarConfig.flatMap((row) => row.groups).find((group) => group.groupId === targetGroupId); + const targetGroup = this.#value.flatMap((row) => row.groups).find((group) => group.groupId === targetGroupId); if (!targetGroup) return; targetGroup.buttons.push(button); @@ -173,7 +172,7 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { return html` - ${repeat(toolbarConfig, (row) => row.rowId, this.#renderRow)}`; + ${repeat(this.#value, (row) => row.rowId, this.#renderRow)}`; } static override styles = [ From 8a4518b40927ece21b27943c7707c1a2cd23edac Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 24 Sep 2024 16:59:10 +0200 Subject: [PATCH 095/241] new layout config format --- ...ui-tiptap-toolbar-configuration.element.ts | 26 ++- ...ap-toolbar-groups-configuration.element.ts | 211 +++++++----------- 2 files changed, 98 insertions(+), 139 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index d59e2a6ed0..58560697cb 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -14,19 +14,18 @@ import { tinymce } from '@umbraco-cms/backoffice/external/tinymce'; const tinyIconSet = tinymce.IconManager.get('default'); -type EditorExtension = { +type UmbEditorExtensionConfig = { alias: string; label: string; icon?: string; hideInToolbar: boolean; - row: number; - group: [number, number]; + position?: [number, number]; }; type EditorExtensionValue = { alias: string; hideInToolbar: boolean; - position: [number, number]; + position?: [number, number]; }; type ExtensionConfig = { @@ -67,7 +66,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement private _extensionCategories: ExtensionCategory = []; @state() - private _extensionsConfig: Array = []; + private _internalValue: Array = []; protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); @@ -106,10 +105,21 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement item.selected = !item.selected; if (item.selected) { - this.value = [...this.value, { alias: item.alias, hideInToolbar: false, position: [0, 0] }]; + this._internalValue = [ + ...this._internalValue, + { alias: item.alias, label: item.label, icon: item.icon, hideInToolbar: false }, + ]; } else { - this.value = this.value.filter((x) => x.alias !== item.alias); + this._internalValue = this._internalValue.filter((x) => x.alias !== item.alias); } + } + + #onChange() { + this.value = this._internalValue.map((x) => ({ + alias: x.alias, + hideInToolbar: x.hideInToolbar, + position: x.position, + })); this.dispatchEvent(new UmbPropertyValueChangeEvent()); } @@ -138,7 +148,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement override render() { return html` - +
    ${repeat( this._extensionCategories, diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index 88fd08c1f4..b057fc12a7 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts @@ -1,130 +1,71 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { customElement, css, html, property, repeat } from '@umbraco-cms/backoffice/external/lit'; +import { customElement, css, html, property, repeat, nothing } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UmbId } from '@umbraco-cms/backoffice/id'; -type ToolbarButton = { +type ToolbarLayout = Array>; + +type Extension = { alias: string; label: string; - icon: string; -}; - -type ToolbarGroup = { - groupId: string; - buttons: ToolbarButton[]; -}; - -type ToolbarRow = { - rowId: string; - groups: ToolbarGroup[]; + icon?: string; }; -type ToolbarConfig = ToolbarRow[]; - @customElement('umb-tiptap-toolbar-groups-configuration') export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { @property({ attribute: false }) - set value(value: ToolbarConfig) { + set value(value: Array) { // TODO: if value is null or does not have at least one row with one group, default to a single row with a single empty group - this.#value = value; - - this.requestUpdate('#value'); + // this.#value = value; + // this.requestUpdate('#value'); } - get value(): ToolbarConfig { + get value(): Array { return this.#value; } - #value: ToolbarConfig = [ + #value: Array = [ + [['bold', 'italic'], []], + [[], ['underline'], ['strikethrough']], + ]; + + @property({ attribute: false }) + extensions: Array = [ { - rowId: 'asdasgasgasd', - groups: [ - { - groupId: 'asdasldjh12h123', - buttons: [ - { alias: 'bold', label: 'Bold', icon: 'bold-icon' }, - { alias: 'italic', label: 'Italic', icon: 'italic-icon' }, - ], - }, - { - groupId: 'askdjljk123ljkh12kj3h', - buttons: [{ alias: 'underline', label: 'Underline', icon: 'underline-icon' }], - }, - ], + alias: 'bold', + label: 'Bold', }, { - rowId: '1l2j3ljk123l21j3', - groups: [ - { - groupId: 'asdashd9ashd0as87hdoasudh', - buttons: [{ alias: 'align-left', label: 'Align Left', icon: 'align-left-icon' }], - }, - ], + alias: 'italic', + label: 'Italic', + }, + { + alias: 'underline', + label: 'Underline', + }, + { + alias: 'strikethrough', + label: 'Strikethrough', }, ]; - #addGroup = (rowId: string) => { - const row = this.#value.find((row) => row.rowId === rowId); - if (!row) return; - - row.groups.push({ - groupId: UmbId.new(), - buttons: [], - }); - - this.requestUpdate(); - }; - - #removeGroup(groupId: string) { - const row = this.#value.find((row) => row.groups.some((group) => group.groupId === groupId)); - if (!row) return; - - row.groups = row.groups.filter((group) => group.groupId !== groupId); - - this.requestUpdate(); - } - - #addRow = () => { - this.#value.push({ - rowId: UmbId.new(), - groups: [ - { - groupId: UmbId.new(), - buttons: [], - }, - ], - }); - - this.requestUpdate(); - }; - - #removeRow(rowId: string) { - this.#value = this.#value.filter((row) => row.rowId !== rowId); - - this.requestUpdate(); - } - - #moveButton(alias: string, targetGroupId: string) { - const sourceGroup = this.#value - .flatMap((row) => row.groups) - .find((group) => group.buttons.some((button) => button.alias === alias)); - - if (!sourceGroup) return; + private moveItem(from: [number, number, number], to: [number, number, number]) { + const [fromRow, fromGroup, fromItem] = from; + const [toRow, toGroup, toItem] = to; - // remove button from source group - const buttonIndex = sourceGroup.buttons.findIndex((button) => button.alias === alias); - const button = sourceGroup.buttons.splice(buttonIndex, 1)[0]; + // Get the item to move from the 'from' position + const itemToMove = this.#value[fromRow][fromGroup][fromItem]; - // add button to target group - const targetGroup = this.#value.flatMap((row) => row.groups).find((group) => group.groupId === targetGroupId); - if (!targetGroup) return; + // Remove the item from the original position + this.#value[fromRow][fromGroup].splice(fromItem, 1); - targetGroup.buttons.push(button); + // Insert the item into the new position + this.#value[toRow][toGroup].splice(toItem, 0, itemToMove); - this.requestUpdate(); + // Trigger an update to re-render the UI + this.requestUpdate('value'); } - #onDragStart = (event: DragEvent, alias: string) => { - event.dataTransfer!.setData('text/plain', alias); + #onDragStart = (event: DragEvent, pos: [number, number, number]) => { + event.dataTransfer!.setData('application/json', JSON.stringify(pos)); event.dataTransfer!.dropEffect = 'move'; }; @@ -132,47 +73,49 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { event.preventDefault(); }; - #onDrop = (event: DragEvent, groupId: string) => { + #onDrop = (event: DragEvent, toPos: [number, number, number]) => { event.preventDefault(); - const alias = event.dataTransfer!.getData('text/plain'); - if (!alias) return; - - this.#moveButton(alias, groupId); + const fromPos: [number, number, number] = JSON.parse(event.dataTransfer!.getData('application/json') ?? '[0,0,0]'); + this.moveItem(fromPos, toPos); }; - #renderButton = (button: ToolbarButton) => { - return html``; - }; + private renderItem(alias: string, rowIndex: number, groupIndex: number, itemIndex: number) { + const extension = this.extensions.find((ext) => ext.alias === alias); + if (!extension) return nothing; - #renderGroup = (group: ToolbarGroup) => { return html`
    this.#onDrop(e, group.groupId)}> - ${repeat(group.buttons, (button) => button.alias, this.#renderButton)} - + class="item" + draggable="true" + @dragstart=${(e: DragEvent) => this.#onDragStart(e, [rowIndex, groupIndex, itemIndex])}> + ${extension.label}
    `; - }; + } - #renderRow = (row: ToolbarRow) => { - return html`
    - ${repeat(row.groups, (group) => group.groupId, this.#renderGroup)} - -
    `; - }; + private renderGroup(group: string[], rowIndex: number, groupIndex: number) { + return html` +
    this.#onDrop(e, [rowIndex, groupIndex, 0])}> + ${group.map((alias, itemIndex) => this.renderItem(alias, rowIndex, groupIndex, itemIndex))} + +
    + `; + } - override render() { - return html` - + private renderRow(row: string[][], rowIndex: number) { + return html` +
    + ${repeat(row, (group, groupIndex) => this.renderGroup(group, rowIndex, groupIndex))} + +
    + `; + } - ${repeat(this.#value, (row) => row.rowId, this.#renderRow)}`; + override render() { + return html`${repeat(this.#value, (row, rowIndex) => this.renderRow(row, rowIndex))}`; } static override styles = [ @@ -197,6 +140,12 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { min-height: 24px; min-width: 24px; } + .item { + padding: 3px; + border: 1px solid #ccc; + border-radius: 3px; + background-color: #f9f9f9; + } .remove-group-button { position: absolute; From 60d55f1ac6e38339b6f7b92750be32ffb5e7b5b0 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 24 Sep 2024 19:40:02 +0200 Subject: [PATCH 096/241] Feature: Tiptap blockpicker (#2335) * fix: editor is always available * fix: remove deprecated v14 stuff * fix: the block manager should not care about the editor * fix: the block manager should not care about the editor * feat: add new tiptap blockpicker extension * fix: save valid content * fix: disable white-space to conform blocks inside text * fix: set block types back to TinyMCE until migration has been completed * feat: define block content when inserting * feat: make `getLayouts` available on the base class * fix: remove unused parameter * feat: cleanup blocks on change * feat: adds inline blocks * feat: set docs for typings and update the interfaces to match and add setEditor to get the editor instance * feat: set docs for typings and update the interfaces to match and add setEditor to get the editor instance * feat: adds blocks in rte * chore: sonarcloud fix * feat: remove delete button as components can be stripped away directly from the DOM * feat: allow custom views for block-rte and filter the views based on conditions * feat: mark tiptap blocks with an outline when active * feat: export data content udi const * fix: add block-rte to vite's importmap so that tinymce works on the dev server * feat(tinymce): get the value from the event target * feat: allow tinymce to insert blocks by listening to the context * chore: mark styles as readonly * chore: cleanup code * fix: remove two fixed TODO comments * feat: used named capturing group * chore: import correct type in testing file * Removed extra `originData` from Block List manager context * Fixed issues in Tiptap toolbar button * Corrected base class for Tiptap Image extension * Fixed up the RTE package vite config to export the Tiptap classes (for CMS build) --------- Co-authored-by: leekelleher --- index.html | 7 + .../data/document-type/document-type.data.ts | 8 +- .../block-editor-custom-view.extension.ts | 2 +- .../context/block-grid-manager.context.ts | 2 +- .../context/block-list-manager.context.ts | 2 +- .../block-rte-entry.element.ts | 52 ++++-- .../ref-rte-block/ref-rte-block.element.ts | 11 +- .../context/block-rte-entries.context.ts | 25 +-- .../context/block-rte-manager.context.ts | 57 +----- src/packages/block/block-rte/manifests.ts | 7 +- .../tiny-mce-block-picker.plugin.ts | 50 ++++- .../block-picker.extension.ts | 176 ++++++++++++++++++ .../block-rte/tiptap-extension/manifests.ts | 16 ++ src/packages/block/block-rte/types.ts | 2 + .../block/context/block-manager.context.ts | 11 +- src/packages/block/custom-view/manifest.ts | 4 +- .../input-tiptap/input-tiptap.element.ts | 3 + .../toolbar/tiptap-toolbar-button.element.ts | 4 +- .../tiptap/extensions/core/image.extension.ts | 4 +- src/packages/rte/tiptap/extensions/types.ts | 51 ++++- .../property-editor-ui-tiptap.element.ts | 28 +-- src/packages/rte/vite.config.ts | 9 +- .../input-tiny-mce/input-tiny-mce.element.ts | 2 +- .../property-editor-ui-tiny-mce.element.ts | 8 +- 24 files changed, 397 insertions(+), 144 deletions(-) create mode 100644 src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts create mode 100644 src/packages/block/block-rte/tiptap-extension/manifests.ts diff --git a/index.html b/index.html index b92c54f0d6..5b7473a4d2 100644 --- a/index.html +++ b/index.html @@ -6,6 +6,13 @@ Umbraco + diff --git a/src/mocks/data/document-type/document-type.data.ts b/src/mocks/data/document-type/document-type.data.ts index 4fef647c2e..0145765978 100644 --- a/src/mocks/data/document-type/document-type.data.ts +++ b/src/mocks/data/document-type/document-type.data.ts @@ -1792,13 +1792,7 @@ This is to test the default configuration of the TinyMCE editor. Search for **dt-richTextEditorTinyMce** in the codebase to find the configuration and add configuration values. -**NB!** If this throws an error in console, go to \`input-tiny-mce.defaults.ts\` and comment out the script append on line 126: - -\`\`\`js -script.text = \`import "@umbraco-cms/backoffice/extension-registry";\`; -script.text = \`import "\${UMB_BLOCK_ENTRY_WEB_COMPONENTS_ABSOLUTE_PATH}";\`; -//editor.dom.doc.head.appendChild(script); -\`\`\``, +**NB!** If this throws an error in console, make sure that \`@umbraco-cms/backoffice/block-rte\` is available in the importmap.`, dataType: { id: 'dt-richTextEditorTinyMce' }, variesByCulture: false, variesBySegment: false, diff --git a/src/packages/block/block-custom-view/block-editor-custom-view.extension.ts b/src/packages/block/block-custom-view/block-editor-custom-view.extension.ts index d41818eae6..36f849fa13 100644 --- a/src/packages/block/block-custom-view/block-editor-custom-view.extension.ts +++ b/src/packages/block/block-custom-view/block-editor-custom-view.extension.ts @@ -14,7 +14,7 @@ export interface ManifestBlockEditorCustomView extends ManifestElement } forBlockEditor - Declare if this Custom View only must appear at specific Block Editors. * @description Optional condition if you like this custom view to only appear at a specific type of Block Editor. * @example 'block-list' - * @example ['block-list', 'block-grid'] + * @example ['block-list', 'block-grid', 'block-rte'] */ forBlockEditor?: string | Array; } diff --git a/src/packages/block/block-grid/context/block-grid-manager.context.ts b/src/packages/block/block-grid/context/block-grid-manager.context.ts index 416ce658ba..d85cf71cea 100644 --- a/src/packages/block/block-grid/context/block-grid-manager.context.ts +++ b/src/packages/block/block-grid/context/block-grid-manager.context.ts @@ -171,7 +171,7 @@ export class UmbBlockGridManagerContext< originData: UmbBlockGridWorkspaceOriginData, ) { this.setOneLayout(layoutEntry, originData); - this.insertBlockData(layoutEntry, content, settings, originData); + this.insertBlockData(layoutEntry, content, settings); return true; } diff --git a/src/packages/block/block-list/context/block-list-manager.context.ts b/src/packages/block/block-list/context/block-list-manager.context.ts index 1acd32840f..86d95a3cd5 100644 --- a/src/packages/block/block-list/context/block-list-manager.context.ts +++ b/src/packages/block/block-list/context/block-list-manager.context.ts @@ -39,7 +39,7 @@ export class UmbBlockListManagerContext< ) { this._layouts.appendOneAt(layoutEntry, originData.index ?? -1); - this.insertBlockData(layoutEntry, content, settings, originData); + this.insertBlockData(layoutEntry, content, settings); return true; } diff --git a/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts b/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts index 54a59940f8..1e6d55a034 100644 --- a/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts +++ b/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts @@ -1,10 +1,14 @@ -import type { UmbBlockRteLayoutModel } from '../../types.js'; +import { UMB_BLOCK_RTE, type UmbBlockRteLayoutModel } from '../../types.js'; import { UmbBlockRteEntryContext } from '../../context/block-rte-entry.context.js'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { html, css, property, state, customElement } from '@umbraco-cms/backoffice/external/lit'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UmbBlockEditorCustomViewProperties } from '@umbraco-cms/backoffice/block-custom-view'; +import type { + ManifestBlockEditorCustomView, + UmbBlockEditorCustomViewProperties, +} from '@umbraco-cms/backoffice/block-custom-view'; +import { stringOrStringArrayContains } from '@umbraco-cms/backoffice/utils'; import '../ref-rte-block/index.js'; @@ -13,22 +17,22 @@ import '../ref-rte-block/index.js'; */ @customElement('umb-rte-block') export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropertyEditorUiElement { - // @property({ type: String, attribute: 'data-content-udi', reflect: true }) public get contentUdi(): string | undefined { - return this._contentUdi; + return this.#contentUdi; } public set contentUdi(value: string | undefined) { if (!value) return; - this._contentUdi = value; + this.#contentUdi = value; this.#context.setContentUdi(value); } - private _contentUdi?: string | undefined; + #contentUdi?: string; #context = new UmbBlockRteEntryContext(this); @state() _showContentEdit = false; + @state() _hasSettings = false; @@ -44,6 +48,9 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert @state() _workspaceEditSettingsPath?: string; + @state() + _contentElementTypeAlias?: string; + @state() _blockViewProps: UmbBlockEditorCustomViewProperties = { contentUdi: undefined!, @@ -69,6 +76,9 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert this._hasSettings = !!key; this.#updateBlockViewProps({ config: { ...this._blockViewProps.config, showSettingsEdit: !!key } }); }); + this.observe(this.#context.contentElementTypeAlias, (alias) => { + this._contentElementTypeAlias = alias; + }); this.observe( this.#context.blockType, (blockType) => { @@ -142,16 +152,26 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert return html``; } + #filterBlockCustomViews = (manifest: ManifestBlockEditorCustomView) => { + const elementTypeAlias = this._contentElementTypeAlias ?? ''; + const isForBlockEditor = + !manifest.forBlockEditor || stringOrStringArrayContains(manifest.forBlockEditor, UMB_BLOCK_RTE); + const isForContentTypeAlias = + !manifest.forContentTypeAlias || stringOrStringArrayContains(manifest.forContentTypeAlias, elementTypeAlias); + return isForBlockEditor && isForContentTypeAlias; + }; + #renderBlock() { return html`
    ${this.#renderRefBlock()} + .filter=${this.#filterBlockCustomViews} + single> + ${this.#renderRefBlock()} + ${this._showContentEdit && this._workspaceEditContentPath ? html` @@ -163,9 +183,6 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert ` : ''} - this.#context.requestDelete()}> - -
    `; @@ -175,7 +192,7 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert return this.#renderBlock(); } - static override styles = [ + static override readonly styles = [ UmbTextStyles, css` :host { @@ -183,6 +200,13 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert display: block; user-select: none; user-drag: auto; + white-space: nowrap; + } + :host(.ProseMirror-selectednode) { + umb-ref-rte-block { + cursor: not-allowed; + outline: 3px solid #b4d7ff; + } } uui-action-bar { position: absolute; diff --git a/src/packages/block/block-rte/components/ref-rte-block/ref-rte-block.element.ts b/src/packages/block/block-rte/components/ref-rte-block/ref-rte-block.element.ts index 153538eaa9..c20abdfb1b 100644 --- a/src/packages/block/block-rte/components/ref-rte-block/ref-rte-block.element.ts +++ b/src/packages/block/block-rte/components/ref-rte-block/ref-rte-block.element.ts @@ -32,13 +32,16 @@ export class UmbRefRteBlockElement extends UmbLitElement { } override render() { - return html``; + return html` + + `; } - static override styles = [ + static override readonly styles = [ css` + :host { + display: block; + } uui-ref-node { min-height: var(--uui-size-16); } diff --git a/src/packages/block/block-rte/context/block-rte-entries.context.ts b/src/packages/block/block-rte/context/block-rte-entries.context.ts index ac2501c80a..7b356d3eea 100644 --- a/src/packages/block/block-rte/context/block-rte-entries.context.ts +++ b/src/packages/block/block-rte/context/block-rte-entries.context.ts @@ -50,15 +50,9 @@ export class UmbBlockRteEntriesContext extends UmbBlockEntriesContext< value.create.contentElementTypeKey, // We can parse an empty object, cause the rest will be filled in by others. {} as any, - data.originData as UmbBlockRteWorkspaceOriginData, ); if (created) { - this.insert( - created.layout, - created.content, - created.settings, - data.originData as UmbBlockRteWorkspaceOriginData, - ); + this.insert(created.layout, created.content, created.settings); } else { throw new Error('Failed to create block'); } @@ -131,25 +125,16 @@ export class UmbBlockRteEntriesContext extends UmbBlockEntriesContext< this._manager?.setLayouts(layouts); } - async create( - contentElementTypeKey: string, - partialLayoutEntry?: Omit, - originData?: UmbBlockRteWorkspaceOriginData, - ) { + async create(contentElementTypeKey: string, partialLayoutEntry?: Omit) { await this._retrieveManager; - return this._manager?.create(contentElementTypeKey, partialLayoutEntry, originData); + return this._manager?.create(contentElementTypeKey, partialLayoutEntry); } // insert Block? - async insert( - layoutEntry: UmbBlockRteLayoutModel, - content: UmbBlockDataType, - settings: UmbBlockDataType | undefined, - originData: UmbBlockRteWorkspaceOriginData, - ) { + async insert(layoutEntry: UmbBlockRteLayoutModel, content: UmbBlockDataType, settings: UmbBlockDataType | undefined) { await this._retrieveManager; - return this._manager?.insert(layoutEntry, content, settings, originData) ?? false; + return this._manager?.insert(layoutEntry, content, settings) ?? false; } // create Block? diff --git a/src/packages/block/block-rte/context/block-rte-manager.context.ts b/src/packages/block/block-rte/context/block-rte-manager.context.ts index 8f7e86d2a9..f568f2b474 100644 --- a/src/packages/block/block-rte/context/block-rte-manager.context.ts +++ b/src/packages/block/block-rte/context/block-rte-manager.context.ts @@ -1,7 +1,5 @@ import type { UmbBlockRteLayoutModel, UmbBlockRteTypeModel } from '../types.js'; -import type { UmbBlockRteWorkspaceOriginData } from '../index.js'; import type { UmbBlockDataType } from '../../block/types.js'; -import type { Editor } from '@umbraco-cms/backoffice/external/tinymce'; import { UmbBlockManagerContext } from '@umbraco-cms/backoffice/block'; import '../components/block-rte-entry/index.js'; @@ -12,32 +10,11 @@ import '../components/block-rte-entry/index.js'; export class UmbBlockRteManagerContext< BlockLayoutType extends UmbBlockRteLayoutModel = UmbBlockRteLayoutModel, > extends UmbBlockManagerContext { - // - #editor?: Editor; - - setTinyMceEditor(editor: Editor) { - this.#editor = editor; - } - - getTinyMceEditor() { - return this.#editor; - } - removeOneLayout(contentUdi: string) { this._layouts.removeOne(contentUdi); } - getLayouts(): Array { - return this._layouts.getValue(); - } - - create( - contentElementTypeKey: string, - partialLayoutEntry?: Omit, - // This property is used by some implementations, but not used in this. - // eslint-disable-next-line @typescript-eslint/no-unused-vars - originData?: UmbBlockRteWorkspaceOriginData, - ) { + create(contentElementTypeKey: string, partialLayoutEntry?: Omit) { const data = super.createBlockData(contentElementTypeKey, partialLayoutEntry); // Find block type. @@ -53,29 +30,10 @@ export class UmbBlockRteManagerContext< return data; } - insert( - layoutEntry: BlockLayoutType, - content: UmbBlockDataType, - settings: UmbBlockDataType | undefined, - originData: UmbBlockRteWorkspaceOriginData, - ) { - if (!this.#editor) return false; - + insert(layoutEntry: BlockLayoutType, content: UmbBlockDataType, settings: UmbBlockDataType | undefined) { this._layouts.appendOne(layoutEntry); - this.insertBlockData(layoutEntry, content, settings, originData); - - if (layoutEntry.displayInline) { - this.#editor.selection.setContent( - ``, - ); - } else { - this.#editor.selection.setContent( - ``, - ); - } - - this.#editor.fire('change'); + this.insertBlockData(layoutEntry, content, settings); return true; } @@ -85,13 +43,6 @@ export class UmbBlockRteManagerContext< * @internal */ public deleteLayoutElement(contentUdi: string) { - if (!this.#editor) return; - - const blockElementsOfThisUdi = this.#editor.dom.select( - `umb-rte-block[data-content-udi='${contentUdi}'], umb-rte-block-inline[data-content-udi='${contentUdi}']`, - ); - blockElementsOfThisUdi.forEach((blockElement) => { - this.#editor?.dom.remove(blockElement); - }); + this.removeBlockUdi(contentUdi); } } diff --git a/src/packages/block/block-rte/manifests.ts b/src/packages/block/block-rte/manifests.ts index 0f70e08783..7b3a871ae7 100644 --- a/src/packages/block/block-rte/manifests.ts +++ b/src/packages/block/block-rte/manifests.ts @@ -1,4 +1,9 @@ import { manifests as tinyMcePluginManifests } from './tiny-mce-plugin/manifests.js'; +import { manifests as tiptapExtensionManifests } from './tiptap-extension/manifests.js'; import { manifests as workspaceManifests } from './workspace/manifests.js'; -export const manifests: Array = [...tinyMcePluginManifests, ...workspaceManifests]; +export const manifests: Array = [ + ...tinyMcePluginManifests, + ...tiptapExtensionManifests, + ...workspaceManifests, +]; diff --git a/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts b/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts index c941292048..4abbf3c32a 100644 --- a/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts +++ b/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts @@ -1,18 +1,23 @@ +import type { UmbBlockDataType } from '../../block/types.js'; import { UMB_BLOCK_RTE_MANAGER_CONTEXT } from '../context/block-rte-manager.context-token.js'; import { UMB_BLOCK_RTE_ENTRIES_CONTEXT } from '../context/block-rte-entries.context-token.js'; +import { UMB_DATA_CONTENT_UDI, type UmbBlockRteLayoutModel } from '../types.js'; import { type TinyMcePluginArguments, UmbTinyMcePluginBase } from '@umbraco-cms/backoffice/tiny-mce'; import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type'; +import type { Editor } from '@umbraco-cms/backoffice/external/tinymce'; export default class UmbTinyMceMultiUrlPickerPlugin extends UmbTinyMcePluginBase { #localize = new UmbLocalizationController(this._host); - - private _blocks?: Array; + #editor: Editor; + #blocks?: Array; #entriesContext?: typeof UMB_BLOCK_RTE_ENTRIES_CONTEXT.TYPE; constructor(args: TinyMcePluginArguments) { super(args); + this.#editor = args.editor; + args.editor.ui.registry.addToggleButton('umbblockpicker', { icon: 'visualblocks', tooltip: this.#localize.term('blockEditor_insertBlock'), @@ -27,15 +32,21 @@ export default class UmbTinyMceMultiUrlPickerPlugin extends UmbTinyMcePluginBase }); this.consumeContext(UMB_BLOCK_RTE_MANAGER_CONTEXT, (context) => { - context.setTinyMceEditor(args.editor); - this.observe( context.blockTypes, (blockTypes) => { - this._blocks = blockTypes; + this.#blocks = blockTypes; }, 'blockType', ); + + this.observe( + context.contents, + (contents) => { + this.#updateBlocks(contents, context.getLayouts()); + }, + 'contents', + ); }); this.consumeContext(UMB_BLOCK_RTE_ENTRIES_CONTEXT, (context) => { this.#entriesContext = context; @@ -64,11 +75,10 @@ export default class UmbTinyMceMultiUrlPickerPlugin extends UmbTinyMcePluginBase return; } - // TODO: Missing solution to skip catalogue if only one type available. [NL] let createPath: string | undefined = undefined; - if (this._blocks?.length === 1) { - const elementKey = this._blocks[0].contentElementTypeKey; + if (this.#blocks?.length === 1) { + const elementKey = this.#blocks[0].contentElementTypeKey; createPath = this.#entriesContext.getPathForCreateBlock() + 'modal/umb-modal-workspace/create/' + elementKey; } else { createPath = this.#entriesContext.getPathForCreateBlock(); @@ -78,4 +88,28 @@ export default class UmbTinyMceMultiUrlPickerPlugin extends UmbTinyMcePluginBase window.history.pushState({}, '', createPath); } } + + #updateBlocks(blocks: UmbBlockDataType[], layouts: Array) { + const editor = this.#editor; + if (!editor?.dom) return; + + const existingBlocks = editor.dom + .select('umb-rte-block, umb-rte-block-inline') + .map((x) => x.getAttribute(UMB_DATA_CONTENT_UDI)); + const newBlocks = blocks.filter((x) => !existingBlocks.find((contentUdi) => contentUdi === x.udi)); + + newBlocks.forEach((block) => { + // Find layout for block + const layout = layouts.find((x) => x.contentUdi === block.udi); + const inline = layout?.displayInline ?? false; + + let blockTag = 'umb-rte-block'; + + if (inline) { + blockTag = 'umb-rte-block-inline'; + } + + editor.insertContent(`<${blockTag} data-content-udi="${block.udi}">`); + }); + } } diff --git a/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts b/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts new file mode 100644 index 0000000000..d70c9e9adc --- /dev/null +++ b/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts @@ -0,0 +1,176 @@ +import { UMB_BLOCK_RTE_MANAGER_CONTEXT } from '../context/block-rte-manager.context-token.js'; +import { UMB_BLOCK_RTE_ENTRIES_CONTEXT } from '../context/block-rte-entries.context-token.js'; +import type { UmbBlockDataType } from '../../block/types.js'; +import { UMB_DATA_CONTENT_UDI, type UmbBlockRteLayoutModel } from '../types.js'; +import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type'; +import { UmbTiptapToolbarElementApiBase } from '@umbraco-cms/backoffice/tiptap'; +import { Node, type Editor } from '@umbraco-cms/backoffice/external/tiptap'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { distinctUntilChanged } from '@umbraco-cms/backoffice/external/rxjs'; + +declare module '@tiptap/core' { + interface Commands { + umbRteBlock: { + setBlock: (options: { contentUdi: string }) => ReturnType; + }; + umbRteBlockInline: { + setBlockInline: (options: { contentUdi: string }) => ReturnType; + }; + } +} + +const umbRteBlock = Node.create({ + name: 'umbRteBlock', + group: 'block', + content: undefined, // The block does not have any content, it is just a wrapper. + atom: true, // The block is an atom, meaning it is a single unit that cannot be split. + marks: '', // We do not allow marks on the block + draggable: true, + selectable: true, + + addAttributes() { + return { + [UMB_DATA_CONTENT_UDI]: { + isRequired: true, + }, + }; + }, + + parseHTML() { + return [{ tag: 'umb-rte-block' }]; + }, + + renderHTML({ HTMLAttributes }) { + return ['umb-rte-block', HTMLAttributes]; + }, + + addCommands() { + return { + setBlock: + (options) => + ({ commands }) => { + const attrs = { [UMB_DATA_CONTENT_UDI]: options.contentUdi }; + return commands.insertContent({ + type: this.name, + attrs, + }); + }, + }; + }, +}); + +const umbRteBlockInline = umbRteBlock.extend({ + name: 'umbRteBlockInline', + group: 'inline', + inline: true, + + parseHTML() { + return [{ tag: 'umb-rte-block-inline' }]; + }, + + renderHTML({ HTMLAttributes }) { + return ['umb-rte-block-inline', HTMLAttributes]; + }, + + addCommands() { + return { + setBlockInline: + (options) => + ({ commands }) => { + const attrs = { [UMB_DATA_CONTENT_UDI]: options.contentUdi }; + return commands.insertContent({ + type: this.name, + attrs, + }); + }, + }; + }, +}); + +export default class UmbTiptapBlockPickerExtension extends UmbTiptapToolbarElementApiBase { + #blocks?: Array; + #entriesContext?: typeof UMB_BLOCK_RTE_ENTRIES_CONTEXT.TYPE; + + constructor(host: UmbControllerHost) { + super(host); + + this.consumeContext(UMB_BLOCK_RTE_MANAGER_CONTEXT, (context) => { + this.observe( + context.blockTypes, + (blockTypes) => { + this.#blocks = blockTypes; + }, + 'blockType', + ); + this.observe( + context.contents.pipe( + distinctUntilChanged((prev, curr) => prev.map((y) => y.udi).join() === curr.map((y) => y.udi).join()), + ), + (contents) => { + this.#updateBlocks(contents, context.getLayouts()); + }, + 'contents', + ); + }); + this.consumeContext(UMB_BLOCK_RTE_ENTRIES_CONTEXT, (context) => { + this.#entriesContext = context; + }); + } + + getTiptapExtensions() { + return [umbRteBlock, umbRteBlockInline]; + } + + override isActive(editor: Editor) { + return ( + editor.isActive(`umb-rte-block[${UMB_DATA_CONTENT_UDI}]`) || + editor.isActive(`umb-rte-block-inline[${UMB_DATA_CONTENT_UDI}]`) + ); + } + + override async execute() { + return this.#createBlock(); + } + + #createBlock() { + if (!this.#entriesContext) { + console.error('[Block Picker] No entries context available.'); + return; + } + + let createPath: string | undefined = undefined; + + if (this.#blocks?.length === 1) { + const elementKey = this.#blocks[0].contentElementTypeKey; + createPath = this.#entriesContext.getPathForCreateBlock() + 'modal/umb-modal-workspace/create/' + elementKey; + } else { + createPath = this.#entriesContext.getPathForCreateBlock(); + } + + if (createPath) { + window.history.pushState({}, '', createPath); + } + } + + #updateBlocks(blocks: UmbBlockDataType[], layouts: Array) { + const editor = this._editor; + if (!editor) return; + + const existingBlocks = Array.from(editor.view.dom.querySelectorAll('umb-rte-block, umb-rte-block-inline')).map( + (x) => x.getAttribute(UMB_DATA_CONTENT_UDI), + ); + const newBlocks = blocks.filter((x) => !existingBlocks.find((contentUdi) => contentUdi === x.udi)); + + newBlocks.forEach((block) => { + // Find layout for block + const layout = layouts.find((x) => x.contentUdi === block.udi); + const inline = layout?.displayInline ?? false; + + if (inline) { + editor.commands.setBlockInline({ contentUdi: block.udi }); + } else { + editor.commands.setBlock({ contentUdi: block.udi }); + } + }); + } +} diff --git a/src/packages/block/block-rte/tiptap-extension/manifests.ts b/src/packages/block/block-rte/tiptap-extension/manifests.ts new file mode 100644 index 0000000000..2181248d85 --- /dev/null +++ b/src/packages/block/block-rte/tiptap-extension/manifests.ts @@ -0,0 +1,16 @@ +import type { ManifestTiptapExtensionButtonKind } from '@umbraco-cms/backoffice/tiptap'; + +export const manifests: ManifestTiptapExtensionButtonKind[] = [ + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.TiptapExtension.BlockPicker', + name: 'Block Picker Tiptap Extension Button', + api: () => import('./block-picker.extension.js'), + meta: { + alias: 'umbblockpicker', + icon: 'icon-plugin', + label: '#blockEditor_insertBlock', + }, + }, +]; diff --git a/src/packages/block/block-rte/types.ts b/src/packages/block/block-rte/types.ts index c97075ec68..dca59515c5 100644 --- a/src/packages/block/block-rte/types.ts +++ b/src/packages/block/block-rte/types.ts @@ -2,6 +2,8 @@ import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type'; import type { UmbBlockLayoutBaseModel, UmbBlockValueType } from '@umbraco-cms/backoffice/block'; export const UMB_BLOCK_RTE_TYPE = 'block-rte-type'; +export const UMB_BLOCK_RTE = 'block-rte'; +export const UMB_DATA_CONTENT_UDI = 'data-content-udi'; export interface UmbBlockRteTypeModel extends UmbBlockTypeBaseModel { displayInline: boolean; diff --git a/src/packages/block/block/context/block-manager.context.ts b/src/packages/block/block/context/block-manager.context.ts index b56ef6ac6f..ce98e0bee7 100644 --- a/src/packages/block/block/context/block-manager.context.ts +++ b/src/packages/block/block/context/block-manager.context.ts @@ -86,6 +86,9 @@ export abstract class UmbBlockManagerContext< setLayouts(layouts: Array) { this._layouts.setValue(layouts); } + getLayouts() { + return this._layouts.getValue(); + } setContents(contents: Array) { this.#contents.setValue(contents); } @@ -276,16 +279,12 @@ export abstract class UmbBlockManagerContext< layoutEntry: BlockLayoutType, content: UmbBlockDataType, settings: UmbBlockDataType | undefined, - // TODO: [v15]: ignoring unused var here here to prevent a breaking change - // eslint-disable-next-line @typescript-eslint/no-unused-vars - originData: BlockOriginDataType, ) { // Create content entry: if (layoutEntry.contentUdi) { this.#contents.appendOne(content); } else { throw new Error('Cannot create block, missing contentUdi'); - return false; } //Create settings entry: @@ -293,4 +292,8 @@ export abstract class UmbBlockManagerContext< this.#settings.appendOne(settings); } } + + protected removeBlockUdi(contentUdi: string) { + this.#contents.removeOne(contentUdi); + } } diff --git a/src/packages/block/custom-view/manifest.ts b/src/packages/block/custom-view/manifest.ts index 46e74f3b3a..317189167a 100644 --- a/src/packages/block/custom-view/manifest.ts +++ b/src/packages/block/custom-view/manifest.ts @@ -1,4 +1,6 @@ -export const manifest: UmbExtensionManifest = { +import type { ManifestBlockEditorCustomView } from '../block-custom-view/block-editor-custom-view.extension.js'; + +export const manifest: ManifestBlockEditorCustomView = { type: 'blockEditorCustomView', alias: 'Umb.blockEditorCustomView.TestView', name: 'Block Editor Custom View Test', diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 0aa053cde2..b37d8d3f0c 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -95,6 +95,9 @@ export class UmbInputTiptapElement extends UmbFormControlMixin { + this._extensions.forEach((ext) => ext.setEditor(editor)); + }, onUpdate: ({ editor }) => { this.#markup = editor.getHTML(); this.dispatchEvent(new UmbChangeEvent()); diff --git a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts index fc3c7219bd..d412ec4682 100644 --- a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts +++ b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts @@ -45,8 +45,8 @@ export class UmbTiptapToolbarButtonElement extends UmbLitElement { compact look=${this._isActive ? 'outline' : 'default'} label=${ifDefined(this.manifest?.meta.label)} - title=${this.manifest?.meta.label ? this.localize.term(this.manifest.meta.label) : ''} - @click=${() => this.api?.execute(this.editor)}> + title=${this.manifest?.meta.label ? this.localize.string(this.manifest.meta.label) : ''} + @click=${() => (this.api && this.editor ? this.api.execute(this.editor) : null)}> ${when( this.manifest?.meta.icon, () => html``, diff --git a/src/packages/rte/tiptap/extensions/core/image.extension.ts b/src/packages/rte/tiptap/extensions/core/image.extension.ts index bf8b9956e9..7254da3b61 100644 --- a/src/packages/rte/tiptap/extensions/core/image.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/image.extension.ts @@ -1,7 +1,7 @@ -import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import { UmbTiptapExtensionApiBase } from '../types.js'; import { UmbImage } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapImageExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapImageExtensionApi extends UmbTiptapExtensionApiBase { getTiptapExtensions() { return [UmbImage.configure({ inline: true })]; } diff --git a/src/packages/rte/tiptap/extensions/types.ts b/src/packages/rte/tiptap/extensions/types.ts index b10b98f2a3..2ab9a0179e 100644 --- a/src/packages/rte/tiptap/extensions/types.ts +++ b/src/packages/rte/tiptap/extensions/types.ts @@ -5,12 +5,38 @@ import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; export interface UmbTiptapExtensionApi extends UmbApi { + /** + * Sets the editor instance to the extension. + */ + setEditor(editor: Editor): void; + + /** + * Gets the Tiptap extensions for the editor. + */ getTiptapExtensions(args?: UmbTiptapExtensionArgs): Array; } export abstract class UmbTiptapExtensionApiBase extends UmbControllerBase implements UmbTiptapExtensionApi { - public manifest?: ManifestTiptapExtension; + /** + * The manifest for the extension. + */ + protected _manifest?: ManifestTiptapExtension; + /** + * The editor instance. + */ + protected _editor?: Editor; + + /** + * @inheritdoc + */ + setEditor(editor: Editor): void { + this._editor = editor; + } + + /** + * @inheritdoc + */ abstract getTiptapExtensions(args?: UmbTiptapExtensionArgs): Array; } @@ -24,18 +50,31 @@ export interface UmbTiptapExtensionArgs { } export interface UmbTiptapToolbarElementApi extends UmbTiptapExtensionApi { - execute(editor?: Editor): void; - isActive(editor?: Editor): boolean; + /** + * Executes the toolbar element action. + */ + execute(editor: Editor): void; + + /** + * Checks if the toolbar element is active. + */ + isActive(editor: Editor): boolean; } export abstract class UmbTiptapToolbarElementApiBase extends UmbTiptapExtensionApiBase implements UmbTiptapToolbarElementApi { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public execute(editor?: Editor) {} + /** + * A method to execute the toolbar element action. + */ + public abstract execute(editor: Editor): void; + /** + * Informs the toolbar element if it is active or not. It uses the manifest meta alias to check if the toolbar element is active. + * @see {ManifestTiptapExtension} + */ public isActive(editor?: Editor) { - return editor && this.manifest?.meta.alias ? editor?.isActive(this.manifest.meta.alias) : false; + return editor && this._manifest?.meta.alias ? editor?.isActive(this._manifest.meta.alias) : false; } } diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index a4fac1bf92..fcd9ce238a 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -10,13 +10,12 @@ import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extensi import '../../components/input-tiptap/input-tiptap.element.js'; import type { UmbBlockValueType } from '@umbraco-cms/backoffice/block'; -// Look at Tiny for correct types export interface UmbRichTextEditorValueType { markup: string; blocks: UmbBlockValueType; } -const UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS = 'Umbraco.RichText'; +const UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS = 'Umbraco.TinyMCE'; const elementName = 'umb-property-editor-ui-tiptap'; @@ -128,21 +127,24 @@ export class UmbPropertyEditorUiTiptapElement extends UmbLitElement implements U markup: this._latestMarkup, }; - // TODO: Validate blocks - // Loop through used, to remove the classes on these. - /*const blockEls = div.querySelectorAll(`umb-rte-block, umb-rte-block-inline`); - blockEls.forEach((blockEl) => { - blockEl.removeAttribute('contenteditable'); - blockEl.removeAttribute('class'); - }); - // Remove unused Blocks of Blocks Layout. Leaving only the Blocks that are present in Markup. - //const blockElements = editor.dom.select(`umb-rte-block, umb-rte-block-inline`); - const usedContentUdis = Array.from(blockEls).map((blockElement) => blockElement.getAttribute('data-content-udi')); + const usedContentUdis: string[] = []; + + // Regex matching all block elements in the markup, and extracting the content UDI. It's the same as the one used on the backend. + const regex = new RegExp( + /(?:)?<\/umb-rte-block(?:-inline)?>/gi, + ); + let blockElement: RegExpExecArray | null; + while ((blockElement = regex.exec(this._latestMarkup)) !== null) { + if (blockElement.groups?.udi) { + usedContentUdis.push(blockElement.groups.udi); + } + } + const unusedBlocks = this.#managerContext.getLayouts().filter((x) => usedContentUdis.indexOf(x.contentUdi) === -1); unusedBlocks.forEach((blockLayout) => { this.#managerContext.removeOneLayout(blockLayout.contentUdi); - });*/ + }); this.#fireChangeEvent(); } diff --git a/src/packages/rte/vite.config.ts b/src/packages/rte/vite.config.ts index 1385afcf23..231d180c29 100644 --- a/src/packages/rte/vite.config.ts +++ b/src/packages/rte/vite.config.ts @@ -8,5 +8,12 @@ const dist = '../../../dist-cms/packages/rte'; rmSync(dist, { recursive: true, force: true }); export default defineConfig({ - ...getDefaultConfig({ dist }), + ...getDefaultConfig({ + dist, + entry: { + 'tiptap/index': 'tiptap/index.ts', + manifests: 'manifests.ts', + 'umbraco-package': 'umbraco-package.ts', + }, + }), }); diff --git a/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts b/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts index 8e8f81cafa..e2d3c3b877 100644 --- a/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts +++ b/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts @@ -380,7 +380,7 @@ export class UmbInputTinyMceElement extends UUIFormControlMixin(UmbLitElement, ' return html`
    `; } - static override styles = [ + static override readonly styles = [ css` .tox-tinymce { position: relative; diff --git a/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts index 8cb80938c6..7f95d97cdf 100644 --- a/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts +++ b/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts @@ -1,3 +1,4 @@ +import type { UmbInputTinyMceElement } from '../../components/input-tiny-mce/input-tiny-mce.element.js'; import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; @@ -115,13 +116,12 @@ export class UmbPropertyEditorUITinyMceElement extends UmbLitElement implements this.dispatchEvent(new UmbPropertyValueChangeEvent()); } - #onChange() { - const editor = this.#managerContext.getTinyMceEditor(); - if (!editor) return; + #onChange(event: CustomEvent & { target: UmbInputTinyMceElement }) { + const value = event.target.value; // Clone the DOM, to remove the classes and attributes on the original: const div = document.createElement('div'); - div.innerHTML = editor.getContent(); + div.innerHTML = value.toString(); // Loop through used, to remove the classes on these. const blockEls = div.querySelectorAll(`umb-rte-block, umb-rte-block-inline`); From d3042316b2483ff5209b8459401bd6e1f32f6865 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Wed, 25 Sep 2024 09:56:09 +0200 Subject: [PATCH 097/241] fixed immutable bug --- ...ui-tiptap-toolbar-configuration.element.ts | 92 ++++++++----------- ...ap-toolbar-groups-configuration.element.ts | 71 ++++++++------ 2 files changed, 78 insertions(+), 85 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 58560697cb..eab44def55 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -11,21 +11,16 @@ import { import './tiptap-toolbar-groups-configuration.element.js'; import { tinymce } from '@umbraco-cms/backoffice/external/tinymce'; +import type UmbTiptapToolbarGroupsConfigurationElement from './tiptap-toolbar-groups-configuration.element.js'; const tinyIconSet = tinymce.IconManager.get('default'); -type UmbEditorExtensionConfig = { - alias: string; - label: string; - icon?: string; - hideInToolbar: boolean; - position?: [number, number]; -}; +type ToolbarLayout = string[][][]; -type EditorExtensionValue = { - alias: string; - hideInToolbar: boolean; - position?: [number, number]; +// If an extension exists in the extensions array but not in the toolbarLayout, it means that the extension is hidden in the toolbar +type NewestValue = { + extensions: Array; + toolbarLayout: ToolbarLayout; }; type ExtensionConfig = { @@ -36,10 +31,10 @@ type ExtensionConfig = { category: string; }; -type ExtensionCategory = Array<{ +type ExtensionCategory = { category: string; extensions: ExtensionConfig[]; -}>; +}; @customElement('umb-property-editor-ui-tiptap-toolbar-configuration') export class UmbPropertyEditorUiTiptapToolbarConfigurationElement @@ -47,39 +42,43 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement implements UmbPropertyEditorUiElement { @property({ attribute: false }) - set value(value: Array) { - if (!value) value = []; + set value(value: NewestValue) { + if (!value) value = { extensions: [], toolbarLayout: [] }; this.#value = value; this.requestUpdate('#value'); } - get value(): Array { + get value(): NewestValue { return this.#value; } - #value: Array = []; + #value: NewestValue = { extensions: [], toolbarLayout: [] }; @property({ attribute: false }) config?: UmbPropertyEditorConfigCollection; @state() - private _extensionCategories: ExtensionCategory = []; + private _toolbarLayout: ToolbarLayout = [[[]]]; @state() - private _internalValue: Array = []; + private _extensionCategories: ExtensionCategory[] = []; + + @state() + private _availableExtensions: ExtensionConfig[] = []; protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); const toolbarConfig = this.config?.getValueByAlias('toolbar'); if (!toolbarConfig) return; + this._availableExtensions = toolbarConfig; const extensions: Array = []; - toolbarConfig.forEach((v) => { + this._availableExtensions.forEach((v) => { extensions.push({ ...v, - selected: this.value.some((x) => x.alias == v.alias), + selected: this.value.extensions.includes(v.alias), }); }); @@ -99,56 +98,37 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement })); this.requestUpdate('_toolbarConfig'); + this.requestUpdate('_availableExtensions'); } #onExtensionSelect(item: ExtensionConfig) { item.selected = !item.selected; if (item.selected) { - this._internalValue = [ - ...this._internalValue, - { alias: item.alias, label: item.label, icon: item.icon, hideInToolbar: false }, - ]; + const lastRow = this._toolbarLayout[this._toolbarLayout.length - 1]; // Last row + const lastGroup = lastRow[lastRow.length - 1]; // Last group in the last row + lastGroup.push(item.alias); } else { - this._internalValue = this._internalValue.filter((x) => x.alias !== item.alias); + this._toolbarLayout = this._toolbarLayout.map((row) => + row.map((group) => group.filter((alias) => alias !== item.alias)), + ); } - } - - #onChange() { - this.value = this._internalValue.map((x) => ({ - alias: x.alias, - hideInToolbar: x.hideInToolbar, - position: x.position, - })); - this.dispatchEvent(new UmbPropertyValueChangeEvent()); + this.requestUpdate(); } - #test() { - this.value = [ - { - alias: 'bold', - hideInToolbar: false, - position: [0, 0], - }, - { - alias: 'italic', - hideInToolbar: false, - position: [0, 0], - }, - { - alias: 'underline', - hideInToolbar: false, - position: [0, 0], - }, - ]; + #onChange = (event: CustomEvent) => { + const value = (event.target as UmbTiptapToolbarGroupsConfigurationElement).value; + this.value = { + ...this.value, + toolbarLayout: value, + }; this.dispatchEvent(new UmbPropertyValueChangeEvent()); - } + }; override render() { return html` - - +
    ${repeat( this._extensionCategories, diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index b057fc12a7..8530910fee 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts @@ -1,6 +1,15 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { customElement, css, html, property, repeat, nothing } from '@umbraco-cms/backoffice/external/lit'; +import { + customElement, + css, + html, + property, + repeat, + nothing, + type PropertyValues, +} from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; type ToolbarLayout = Array>; @@ -14,9 +23,12 @@ type Extension = { export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { @property({ attribute: false }) set value(value: Array) { - // TODO: if value is null or does not have at least one row with one group, default to a single row with a single empty group - // this.#value = value; - // this.requestUpdate('#value'); + //If value is null or does not have at least one row with one group, default to a single row with a single empty group + if (!value || value.length === 0 || value[0].length === 0) { + value = [[[]]]; + } + this.#value = value; + this.requestUpdate(); } get value(): Array { return this.#value; @@ -28,41 +40,42 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { ]; @property({ attribute: false }) - extensions: Array = [ - { - alias: 'bold', - label: 'Bold', - }, - { - alias: 'italic', - label: 'Italic', - }, - { - alias: 'underline', - label: 'Underline', - }, - { - alias: 'strikethrough', - label: 'Strikethrough', - }, - ]; + extensions: Array = []; - private moveItem(from: [number, number, number], to: [number, number, number]) { + private moveItem = (from: [number, number, number], to: [number, number, number]) => { const [fromRow, fromGroup, fromItem] = from; const [toRow, toGroup, toItem] = to; - // Get the item to move from the 'from' position - const itemToMove = this.#value[fromRow][fromGroup][fromItem]; + // Deep clone the entire value array (shallow copy of nested arrays) + const clonedValue = this.#value.map((row) => row.map((group) => [...group])); + + const fromGroupArray = clonedValue[fromRow]?.[fromGroup]; + const toGroupArray = clonedValue[toRow]?.[toGroup]; + + if (!fromGroupArray || !toGroupArray) { + console.error('Invalid group or row indexes.'); + return; + } + + const itemToMove = fromGroupArray[fromItem]; + if (typeof itemToMove === 'undefined') { + console.error('Invalid item index:', fromItem); + return; + } // Remove the item from the original position - this.#value[fromRow][fromGroup].splice(fromItem, 1); + fromGroupArray.splice(fromItem, 1); // Insert the item into the new position - this.#value[toRow][toGroup].splice(toItem, 0, itemToMove); + toGroupArray.splice(toItem, 0, itemToMove); - // Trigger an update to re-render the UI + // Replace the whole value with the modified cloned structure + this.#value = clonedValue; + + // Trigger a re-render this.requestUpdate('value'); - } + this.dispatchEvent(new UmbChangeEvent()); + }; #onDragStart = (event: DragEvent, pos: [number, number, number]) => { event.dataTransfer!.setData('application/json', JSON.stringify(pos)); From bfe7692511da886054eae9d6bdfb16fdb2ead372 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:42:27 +0200 Subject: [PATCH 098/241] init dropdown --- .../input-tiptap/tiptap-fixed-menu.element.ts | 3 + .../tiptap-toolbar-dropdown.element.ts | 145 ++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 5b879b5c4a..f276ecc7df 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -3,6 +3,8 @@ import { css, customElement, html, property } from '@umbraco-cms/backoffice/exte import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; +import '../toolbar/tiptap-toolbar-dropdown.element.js'; + const elementName = 'umb-tiptap-fixed-menu'; @customElement(elementName) @@ -30,6 +32,7 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { .filter=${(ext: ManifestTiptapExtension) => !!ext.kind || !!ext.element} .elementProps=${{ editor: this.editor }}> + `; } diff --git a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts new file mode 100644 index 0000000000..205af34d1e --- /dev/null +++ b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts @@ -0,0 +1,145 @@ +import type { ManifestTiptapExtensionButtonKind } from '../../extensions/tiptap-extension.js'; +import type { UmbTiptapToolbarElementApi } from '../../extensions/types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; +import { + css, + customElement, + html, + ifDefined, + nothing, + repeat, + state, + when, + type TemplateResult, +} from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import type { UUIPopoverContainerElement } from '@umbraco-ui/uui'; + +const elementName = 'umb-tiptap-toolbar-dropdown'; + +type DropdownItem = { + label: string; + nested?: DropdownItem[]; +}; + +@customElement(elementName) +export class UmbTiptapToolbarDropdownElement extends UmbLitElement { + #testDropdownItems: Array = [ + { + label: 'Headings', + nested: [ + { + label: 'Page header', + }, + { + label: 'Section header', + }, + { + label: 'Paragraph header', + nested: [ + { + label: 'Paragraph header 1', + }, + { + label: 'Paragraph header 2', + }, + ], + }, + ], + }, + { + label: 'Blocks', + nested: [ + { + label: 'Paragraph', + }, + ], + }, + { + label: 'Containers', + nested: [ + { + label: 'Quote', + }, + { + label: 'Code', + }, + ], + }, + ]; + + #onMouseEnter = (popoverId: string) => { + const popover = this.shadowRoot?.querySelector(`#${this.#makeAlias(popoverId)}`) as UUIPopoverContainerElement; + if (!popover) return; + popover.showPopover(); + }; + + #onMouseLeave = (popoverId: string) => { + popoverId = popoverId.replace(/\s/g, '-').toLowerCase(); + const popover = this.shadowRoot?.querySelector(`#${this.#makeAlias(popoverId)}`) as UUIPopoverContainerElement; + if (!popover) return; + popover.hidePopover(); + }; + + #makeAlias(label: string) { + return label.replace(/\s/g, '-').toLowerCase(); + } + + #renderItem(item: DropdownItem): TemplateResult { + return html` + + `; + } + + #renderItems(items: Array) { + return html` ${repeat( + items, + (item) => item.label, + (item) => html`${this.#renderItem(item)}`, + )}`; + } + + override render() { + return html` + Text styles + + `; + } + + static override readonly styles = css` + :host { + position: absolute; + top: -67px; + --uui-button-content-align: left; + } + + uui-popover-container { + box-shadow: var(--uui-shadow-depth-3); + } + + .dropdown-item { + background-color: var(--uui-color-surface); + display: flex; + flex-direction: column; + text-wrap: nowrap; + } + `; +} + +export { UmbTiptapToolbarDropdownElement as element }; + +declare global { + interface HTMLElementTagNameMap { + [elementName]: UmbTiptapToolbarDropdownElement; + } +} From 4d57ff5465bc5ef0f5ae7d6acb743b62f5d991de Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 24 Sep 2024 09:59:03 +0200 Subject: [PATCH 099/241] styling --- .../input-tiptap/tiptap-fixed-menu.element.ts | 7 +- .../tiptap-toolbar-dropdown.element.ts | 81 ++++++++++--------- 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index f276ecc7df..9ae665be09 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -27,12 +27,12 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { override render() { return html` + !!ext.kind || !!ext.element} .elementProps=${{ editor: this.editor }}> - `; } @@ -46,12 +46,13 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { color: var(--color-text); display: grid; grid-template-columns: repeat(auto-fill, minmax(24px, 1fr)); - gap: 4px; + gap: var(--uui-size-space-1); position: sticky; top: -25px; left: 0px; right: 0px; - padding: 4px; + padding: var(--uui-size-space-3); + align-items: center; } :host([readonly]) { diff --git a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts index 205af34d1e..863bd5d2c9 100644 --- a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts +++ b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts @@ -1,19 +1,6 @@ -import type { ManifestTiptapExtensionButtonKind } from '../../extensions/tiptap-extension.js'; -import type { UmbTiptapToolbarElementApi } from '../../extensions/types.js'; -import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -import { - css, - customElement, - html, - ifDefined, - nothing, - repeat, - state, - when, - type TemplateResult, -} from '@umbraco-cms/backoffice/external/lit'; +import { css, customElement, html, nothing, repeat, type TemplateResult } from '@umbraco-cms/backoffice/external/lit'; +import type { UUIPopoverContainerElement } from '@umbraco-cms/backoffice/external/uui'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UUIPopoverContainerElement } from '@umbraco-ui/uui'; const elementName = 'umb-tiptap-toolbar-dropdown'; @@ -91,47 +78,61 @@ export class UmbTiptapToolbarDropdownElement extends UmbLitElement { class="dropdown-item" @mouseenter=${() => this.#onMouseEnter(item.label)} @mouseleave=${() => this.#onMouseLeave(item.label)}> - - - ${item.nested ? this.#renderItems(item.nested) : nothing} - + + ${item.nested ? this.#renderItems(item.label, item.nested) : nothing}
    `; } - #renderItems(items: Array) { - return html` ${repeat( - items, - (item) => item.label, - (item) => html`${this.#renderItem(item)}`, - )}`; + #renderItems(label: string, items: Array) { + return html` +
    + ${repeat( + items, + (item) => item.label, + (item) => html`${this.#renderItem(item)}`, + )} +
    +
    `; } override render() { return html` - Text styles - + + ${this.#renderItems('Text styles', this.#testDropdownItems)} `; } static override readonly styles = css` - :host { - position: absolute; - top: -67px; - --uui-button-content-align: left; + button { + border: unset; + background-color: unset; + font: unset; + text-align: unset; } - uui-popover-container { - box-shadow: var(--uui-shadow-depth-3); + .label { + border-radius: var(--uui-border-radius); + width: 100%; + box-sizing: border-box; + align-content: center; + padding: var(--uui-size-space-1) var(--uui-size-space-3); + align-items: center; + cursor: pointer; + color: var(--uui-color-text); } - .dropdown-item { - background-color: var(--uui-color-surface); - display: flex; - flex-direction: column; - text-wrap: nowrap; + .selected-value { + background: var(--uui-color-surface-alt); + } + + .popover-content { + background: var(--uui-color-surface); + border-radius: var(--uui-border-radius); + box-shadow: var(--uui-shadow-depth-3); + padding: var(--uui-size-space-1); } `; } From c5daee1006e0de418afdf5bce85254a2331af439 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:20:04 +0200 Subject: [PATCH 100/241] styling --- .../toolbar/tiptap-toolbar-dropdown.element.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts index 863bd5d2c9..bb8fff58c0 100644 --- a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts +++ b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts @@ -1,5 +1,5 @@ import { css, customElement, html, nothing, repeat, type TemplateResult } from '@umbraco-cms/backoffice/external/lit'; -import type { UUIPopoverContainerElement } from '@umbraco-cms/backoffice/external/uui'; +import type { PopoverContainerPlacement, UUIPopoverContainerElement } from '@umbraco-cms/backoffice/external/uui'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; const elementName = 'umb-tiptap-toolbar-dropdown'; @@ -86,8 +86,12 @@ export class UmbTiptapToolbarDropdownElement extends UmbLitElement { `; } - #renderItems(label: string, items: Array) { - return html` + #renderItems( + label: string, + items: Array, + placement: PopoverContainerPlacement = 'right-start', + ): TemplateResult { + return html`
    ${repeat( items, @@ -101,7 +105,7 @@ export class UmbTiptapToolbarDropdownElement extends UmbLitElement { override render() { return html` - ${this.#renderItems('Text styles', this.#testDropdownItems)} + ${this.#renderItems('Text styles', this.#testDropdownItems, 'bottom-start')} `; } @@ -124,6 +128,11 @@ export class UmbTiptapToolbarDropdownElement extends UmbLitElement { color: var(--uui-color-text); } + .label:hover { + background: var(--uui-color-surface-alt); + color: var(--uui-color-interactive-emphasis); + } + .selected-value { background: var(--uui-color-surface-alt); } From 65bcb36de88e39e41a5e7df17b8b1cf46fe7f4dd Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:58:02 +0200 Subject: [PATCH 101/241] styling --- .../toolbar/tiptap-toolbar-dropdown.element.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts index bb8fff58c0..f9c2f1cc1e 100644 --- a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts +++ b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts @@ -79,7 +79,7 @@ export class UmbTiptapToolbarDropdownElement extends UmbLitElement { @mouseenter=${() => this.#onMouseEnter(item.label)} @mouseleave=${() => this.#onMouseLeave(item.label)}> ${item.nested ? this.#renderItems(item.label, item.nested) : nothing}
    @@ -104,7 +104,9 @@ export class UmbTiptapToolbarDropdownElement extends UmbLitElement { override render() { return html` - + ${this.#renderItems('Text styles', this.#testDropdownItems, 'bottom-start')} `; } @@ -117,15 +119,23 @@ export class UmbTiptapToolbarDropdownElement extends UmbLitElement { text-align: unset; } + uui-symbol-expand { + position: absolute; + right: 5px; + top: 5px; + } + .label { border-radius: var(--uui-border-radius); width: 100%; box-sizing: border-box; align-content: center; padding: var(--uui-size-space-1) var(--uui-size-space-3); + padding-right: 21px; align-items: center; cursor: pointer; color: var(--uui-color-text); + position: relative; } .label:hover { From 869256a385cb0c9da7ed1adc2128b98d9bb742f7 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 25 Sep 2024 11:56:54 +0200 Subject: [PATCH 102/241] feat: convert to a base element --- src/packages/rte/tiptap/components/index.ts | 1 + .../input-tiptap/tiptap-fixed-menu.element.ts | 3 +- .../tiptap-toolbar-dropdown-base.element.ts | 120 +++++++++++++ .../tiptap-toolbar-dropdown.element.ts | 165 ------------------ .../extensions/umb/style-select.extension.ts | 24 +++ 5 files changed, 146 insertions(+), 167 deletions(-) create mode 100644 src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown-base.element.ts delete mode 100644 src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts create mode 100644 src/packages/rte/tiptap/extensions/umb/style-select.extension.ts diff --git a/src/packages/rte/tiptap/components/index.ts b/src/packages/rte/tiptap/components/index.ts index d7e1544b7d..f03903fb07 100644 --- a/src/packages/rte/tiptap/components/index.ts +++ b/src/packages/rte/tiptap/components/index.ts @@ -1 +1,2 @@ export * from './input-tiptap/index.js'; +export * from './toolbar/tiptap-toolbar-dropdown-base.element.js'; diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 9ae665be09..0dadca09f2 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -3,7 +3,7 @@ import { css, customElement, html, property } from '@umbraco-cms/backoffice/exte import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -import '../toolbar/tiptap-toolbar-dropdown.element.js'; +import '../toolbar/tiptap-toolbar-dropdown-base.element.js'; const elementName = 'umb-tiptap-fixed-menu'; @@ -27,7 +27,6 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { override render() { return html` - !!ext.kind || !!ext.element} diff --git a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown-base.element.ts b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown-base.element.ts new file mode 100644 index 0000000000..d9750baa8b --- /dev/null +++ b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown-base.element.ts @@ -0,0 +1,120 @@ +import { css, html, nothing, repeat, type TemplateResult } from '@umbraco-cms/backoffice/external/lit'; +import type { PopoverContainerPlacement, UUIPopoverContainerElement } from '@umbraco-cms/backoffice/external/uui'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; + +export type TiptapDropdownItem = { + alias: string; + label: string; + nested?: TiptapDropdownItem[]; + execute?: () => void; + isActive?: () => boolean; +}; + +export abstract class UmbTiptapToolbarDropdownBaseElement extends UmbLitElement { + protected abstract get items(): TiptapDropdownItem[]; + protected abstract get label(): string; + + #onMouseEnter = (popoverId: string) => { + const popover = this.shadowRoot?.querySelector(`#${this.makeAlias(popoverId)}`) as UUIPopoverContainerElement; + if (!popover) return; + popover.showPopover(); + }; + + #onMouseLeave = (popoverId: string) => { + popoverId = popoverId.replace(/\s/g, '-').toLowerCase(); + const popover = this.shadowRoot?.querySelector(`#${this.makeAlias(popoverId)}`) as UUIPopoverContainerElement; + if (!popover) return; + popover.hidePopover(); + }; + + protected makeAlias(label: string) { + return label.replace(/\s/g, '-').toLowerCase(); + } + + protected renderItem(item: TiptapDropdownItem): TemplateResult { + return html` + + `; + } + + protected renderItems( + label: string, + items: Array, + placement: PopoverContainerPlacement = 'right-start', + ): TemplateResult { + return html` +
    + ${repeat( + items, + (item) => item.alias, + (item) => html`${this.renderItem(item)}`, + )} +
    +
    `; + } + protected override render() { + return html` + + ${this.renderItems(this.label, this.items, 'bottom-start')} + `; + } + + static override readonly styles = [ + UmbTextStyles, + css` + button { + border: unset; + background-color: unset; + font: unset; + text-align: unset; + } + + uui-symbol-expand { + position: absolute; + right: 5px; + top: 5px; + } + + .label { + border-radius: var(--uui-border-radius); + width: 100%; + box-sizing: border-box; + align-content: center; + padding: var(--uui-size-space-1) var(--uui-size-space-3); + padding-right: 21px; + align-items: center; + cursor: pointer; + color: var(--uui-color-text); + position: relative; + } + + .label:hover { + background: var(--uui-color-surface-alt); + color: var(--uui-color-interactive-emphasis); + } + + .selected-value { + background: var(--uui-color-surface-alt); + } + + .popover-content { + background: var(--uui-color-surface); + border-radius: var(--uui-border-radius); + box-shadow: var(--uui-shadow-depth-3); + padding: var(--uui-size-space-1); + } + `, + ]; +} diff --git a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts deleted file mode 100644 index f9c2f1cc1e..0000000000 --- a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { css, customElement, html, nothing, repeat, type TemplateResult } from '@umbraco-cms/backoffice/external/lit'; -import type { PopoverContainerPlacement, UUIPopoverContainerElement } from '@umbraco-cms/backoffice/external/uui'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; - -const elementName = 'umb-tiptap-toolbar-dropdown'; - -type DropdownItem = { - label: string; - nested?: DropdownItem[]; -}; - -@customElement(elementName) -export class UmbTiptapToolbarDropdownElement extends UmbLitElement { - #testDropdownItems: Array = [ - { - label: 'Headings', - nested: [ - { - label: 'Page header', - }, - { - label: 'Section header', - }, - { - label: 'Paragraph header', - nested: [ - { - label: 'Paragraph header 1', - }, - { - label: 'Paragraph header 2', - }, - ], - }, - ], - }, - { - label: 'Blocks', - nested: [ - { - label: 'Paragraph', - }, - ], - }, - { - label: 'Containers', - nested: [ - { - label: 'Quote', - }, - { - label: 'Code', - }, - ], - }, - ]; - - #onMouseEnter = (popoverId: string) => { - const popover = this.shadowRoot?.querySelector(`#${this.#makeAlias(popoverId)}`) as UUIPopoverContainerElement; - if (!popover) return; - popover.showPopover(); - }; - - #onMouseLeave = (popoverId: string) => { - popoverId = popoverId.replace(/\s/g, '-').toLowerCase(); - const popover = this.shadowRoot?.querySelector(`#${this.#makeAlias(popoverId)}`) as UUIPopoverContainerElement; - if (!popover) return; - popover.hidePopover(); - }; - - #makeAlias(label: string) { - return label.replace(/\s/g, '-').toLowerCase(); - } - - #renderItem(item: DropdownItem): TemplateResult { - return html` - - `; - } - - #renderItems( - label: string, - items: Array, - placement: PopoverContainerPlacement = 'right-start', - ): TemplateResult { - return html` -
    - ${repeat( - items, - (item) => item.label, - (item) => html`${this.#renderItem(item)}`, - )} -
    -
    `; - } - - override render() { - return html` - - ${this.#renderItems('Text styles', this.#testDropdownItems, 'bottom-start')} - `; - } - - static override readonly styles = css` - button { - border: unset; - background-color: unset; - font: unset; - text-align: unset; - } - - uui-symbol-expand { - position: absolute; - right: 5px; - top: 5px; - } - - .label { - border-radius: var(--uui-border-radius); - width: 100%; - box-sizing: border-box; - align-content: center; - padding: var(--uui-size-space-1) var(--uui-size-space-3); - padding-right: 21px; - align-items: center; - cursor: pointer; - color: var(--uui-color-text); - position: relative; - } - - .label:hover { - background: var(--uui-color-surface-alt); - color: var(--uui-color-interactive-emphasis); - } - - .selected-value { - background: var(--uui-color-surface-alt); - } - - .popover-content { - background: var(--uui-color-surface); - border-radius: var(--uui-border-radius); - box-shadow: var(--uui-shadow-depth-3); - padding: var(--uui-size-space-1); - } - `; -} - -export { UmbTiptapToolbarDropdownElement as element }; - -declare global { - interface HTMLElementTagNameMap { - [elementName]: UmbTiptapToolbarDropdownElement; - } -} diff --git a/src/packages/rte/tiptap/extensions/umb/style-select.extension.ts b/src/packages/rte/tiptap/extensions/umb/style-select.extension.ts new file mode 100644 index 0000000000..6429d7021d --- /dev/null +++ b/src/packages/rte/tiptap/extensions/umb/style-select.extension.ts @@ -0,0 +1,24 @@ +import { UmbTiptapToolbarDropdownBaseElement, type TiptapDropdownItem } from '../../components/index.js'; +import { customElement, state } from '@umbraco-cms/backoffice/external/lit'; + +const elementName = 'umb-tiptap-style-select-toolbar-element'; + +@customElement(elementName) +export class UmbTiptapStyleSelectToolbarElement extends UmbTiptapToolbarDropdownBaseElement { + protected override label = 'Style select'; + + @state() + protected override get items(): TiptapDropdownItem[] { + throw new Error('Method not implemented.'); + } + + static override readonly styles = UmbTiptapToolbarDropdownBaseElement.styles; +} + +export { UmbTiptapStyleSelectToolbarElement as element }; + +declare global { + interface HTMLElementTagNameMap { + [elementName]: UmbTiptapStyleSelectToolbarElement; + } +} From 4a016e0bdcd5a44a2f62d8e037ed269018217ff1 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 25 Sep 2024 11:58:35 +0200 Subject: [PATCH 103/241] feat: disable the style select extension for now --- src/packages/rte/tiptap/property-editors/tiptap/manifests.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 10051e7b93..fd5d2ce6de 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -70,7 +70,6 @@ export const manifests: Array = [ { alias: 'superscript', label: 'Superscript', icon: 'superscript', category: 'text-transformation' }, // Styling and Font Group - { alias: 'styles', label: 'Style select', icon: 'permanent-pen', category: 'styling' }, { alias: 'fontname', label: 'Font select', icon: 'text-color', category: 'styling' }, { alias: 'fontsize', label: 'Font size', icon: 'text-color', category: 'styling' }, From 198dea03a6e11df522bc2937331c6ef3c2af0489 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:27:15 +0200 Subject: [PATCH 104/241] frozen arrays <3 --- ...ui-tiptap-toolbar-configuration.element.ts | 8 +- ...ap-toolbar-groups-configuration.element.ts | 74 +++++++++---------- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index eab44def55..df5b2896dd 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -45,11 +45,15 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement set value(value: NewestValue) { if (!value) value = { extensions: [], toolbarLayout: [] }; - this.#value = value; + this.#value = this.#deepClone(value) as NewestValue; this.requestUpdate('#value'); } get value(): NewestValue { - return this.#value; + return this.#deepClone(this.#value) as NewestValue; + } + + #deepClone(value: unknown): unknown { + return JSON.parse(JSON.stringify(value)); } #value: NewestValue = { extensions: [], toolbarLayout: [] }; diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index 8530910fee..b74105b57d 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts @@ -1,13 +1,5 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { - customElement, - css, - html, - property, - repeat, - nothing, - type PropertyValues, -} from '@umbraco-cms/backoffice/external/lit'; +import { customElement, css, html, property, repeat, nothing } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; @@ -27,17 +19,14 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { if (!value || value.length === 0 || value[0].length === 0) { value = [[[]]]; } - this.#value = value; + this._value = value; this.requestUpdate(); } get value(): Array { - return this.#value; + return this._value; } - #value: Array = [ - [['bold', 'italic'], []], - [[], ['underline'], ['strikethrough']], - ]; + _value: Array = [[[]]]; @property({ attribute: false }) extensions: Array = []; @@ -46,33 +35,40 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { const [fromRow, fromGroup, fromItem] = from; const [toRow, toGroup, toItem] = to; - // Deep clone the entire value array (shallow copy of nested arrays) - const clonedValue = this.#value.map((row) => row.map((group) => [...group])); + // Get the item to move from the 'from' position + const itemToMove = this.value[fromRow][fromGroup][fromItem]; - const fromGroupArray = clonedValue[fromRow]?.[fromGroup]; - const toGroupArray = clonedValue[toRow]?.[toGroup]; + // Remove the item from the original position + this.value[fromRow][fromGroup].splice(fromItem, 1); - if (!fromGroupArray || !toGroupArray) { - console.error('Invalid group or row indexes.'); - return; - } + // Insert the item into the new position + this.value[toRow][toGroup].splice(toItem, 0, itemToMove); - const itemToMove = fromGroupArray[fromItem]; - if (typeof itemToMove === 'undefined') { - console.error('Invalid item index:', fromItem); - return; - } + // Trigger a re-render + this.requestUpdate('value'); + this.dispatchEvent(new UmbChangeEvent()); + }; - // Remove the item from the original position - fromGroupArray.splice(fromItem, 1); + #addGroup = (rowIndex: number, groupIndex: number) => { + this.value[rowIndex].splice(groupIndex, 0, []); + this.requestUpdate('value'); + this.dispatchEvent(new UmbChangeEvent()); + }; - // Insert the item into the new position - toGroupArray.splice(toItem, 0, itemToMove); + #removeGroup = (rowIndex: number, groupIndex: number) => { + this.value[rowIndex].splice(groupIndex, 1); + this.requestUpdate('value'); + this.dispatchEvent(new UmbChangeEvent()); + }; - // Replace the whole value with the modified cloned structure - this.#value = clonedValue; + #addRow = (rowIndex: number) => { + this.value.splice(rowIndex, 0, [[]]); + this.requestUpdate('value'); + this.dispatchEvent(new UmbChangeEvent()); + }; - // Trigger a re-render + #removeRow = (rowIndex: number) => { + this.value.splice(rowIndex, 1); this.requestUpdate('value'); this.dispatchEvent(new UmbChangeEvent()); }; @@ -113,7 +109,7 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { @dragover=${this.#onDragOver} @drop=${(e: DragEvent) => this.#onDrop(e, [rowIndex, groupIndex, 0])}> ${group.map((alias, itemIndex) => this.renderItem(alias, rowIndex, groupIndex, itemIndex))} - +
    `; } @@ -122,13 +118,15 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { return html`
    ${repeat(row, (group, groupIndex) => this.renderGroup(group, rowIndex, groupIndex))} - + +
    `; } override render() { - return html`${repeat(this.#value, (row, rowIndex) => this.renderRow(row, rowIndex))}`; + return html`${repeat(this._value, (row, rowIndex) => this.renderRow(row, rowIndex))} + `; } static override styles = [ From 60771343346018fb99a2f3a6475fb6828db71763 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:40:19 +0200 Subject: [PATCH 105/241] feat: add extra properties to image --- src/external/tiptap/extensions/tiptap-umb-image.extension.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/external/tiptap/extensions/tiptap-umb-image.extension.ts b/src/external/tiptap/extensions/tiptap-umb-image.extension.ts index 5b3737576e..fb3d645b4e 100644 --- a/src/external/tiptap/extensions/tiptap-umb-image.extension.ts +++ b/src/external/tiptap/extensions/tiptap-umb-image.extension.ts @@ -22,6 +22,7 @@ export const UmbImage = Image.extend({ }, 'data-tmpimg': { default: null }, 'data-udi': { default: null }, + 'data-caption': { default: null }, }; }, }); @@ -47,6 +48,8 @@ declare module '@tiptap/core' { srcset?: string; sizes?: string; 'data-tmpimg'?: string; + 'data-udi'?: string; + 'data-caption'?: string; }) => ReturnType; }; } From 7430aa0cfaed901735939f6872213a3094725e35 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:40:58 +0200 Subject: [PATCH 106/241] feat: add maxWidth to imageSize function --- .../core/utils/media/image-size.function.ts | 43 +++++++++++++------ .../extensions/umb/media-upload.extension.ts | 8 +--- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/packages/core/utils/media/image-size.function.ts b/src/packages/core/utils/media/image-size.function.ts index cbd93b58fe..8958da1c0b 100644 --- a/src/packages/core/utils/media/image-size.function.ts +++ b/src/packages/core/utils/media/image-size.function.ts @@ -1,25 +1,40 @@ /** * Get the dimensions of an image from a URL. * @param {string} url The URL of the image. It can be a local file (blob url) or a remote file. - * @returns {Promise<{width: number, height: number}>} The width and height of the image as downloaded from the URL. + * @param {{maxWidth?: number}} opts Options for the image size. + * @param {number} opts.maxWidth The maximum width of the image. If the image is wider than this, it will be scaled down to this width while keeping the aspect ratio. + * @returns {Promise<{width: number, height: number, naturalWidth: number, naturalHeight: number}>} The width and height of the image as downloaded from the URL. The width and height can differ from the natural numbers if maxImageWidth is given. */ -export function imageSize(url: string): Promise<{ width: number; height: number }> { +export function imageSize( + url: string, + opts?: { maxWidth?: number }, +): Promise<{ width: number; height: number; naturalWidth: number; naturalHeight: number }> { const img = new Image(); - const promise = new Promise<{ width: number; height: number }>((resolve, reject) => { - img.onload = () => { - // Natural size is the actual image size regardless of rendering. - // The 'normal' `width`/`height` are for the **rendered** size. - const width = img.naturalWidth; - const height = img.naturalHeight; + const promise = new Promise<{ width: number; height: number; naturalWidth: number; naturalHeight: number }>( + (resolve, reject) => { + img.onload = () => { + // Natural size is the actual image size regardless of rendering. + // The 'normal' `width`/`height` are for the **rendered** size. + const naturalWidth = img.naturalWidth; + const naturalHeight = img.naturalHeight; + let width = naturalWidth; + let height = naturalHeight; - // Resolve promise with the width and height - resolve({ width, height }); - }; + if (opts?.maxWidth && opts.maxWidth > 0 && width > opts?.maxWidth) { + const ratio = opts.maxWidth / naturalWidth; + width = opts.maxWidth; + height = Math.round(naturalHeight * ratio); + } - // Reject promise on error - img.onerror = reject; - }); + // Resolve promise with the width and height + resolve({ width, height, naturalWidth, naturalHeight }); + }; + + // Reject promise on error + img.onerror = reject; + }, + ); // Setting the source makes it start downloading and eventually call `onload` img.src = url; diff --git a/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts b/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts index ec9e7cfd5d..2cfd79dca1 100644 --- a/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts @@ -99,13 +99,7 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi return; } - let { width, height } = await imageSize(URL.createObjectURL(upload.file)); - - if (maxImageSize > 0 && width > maxImageSize) { - const ratio = maxImageSize / width; - width = maxImageSize; - height = Math.round(height * ratio); - } + const { width, height } = await imageSize(URL.createObjectURL(upload.file), { maxWidth: maxImageSize }); editor .chain() From 8d2b74a73c62a736bc9494793bae01c11e2698db Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:41:37 +0200 Subject: [PATCH 107/241] feat: move the media with caption modal from the tinymce to the media module --- src/packages/media/media/index.ts | 5 +++++ src/packages/media/media/modals/index.ts | 1 + src/packages/media/media/modals/manifests.ts | 3 ++- .../media}/modals/media-caption-alt-text/index.ts | 0 .../media/modals/media-caption-alt-text}/manifests.ts | 2 +- .../media-caption-alt-text-modal.element.ts | 0 .../media-caption-alt-text-modal.token.ts | 0 src/packages/tiny-mce/manifests.ts | 3 +-- src/packages/tiny-mce/modals/index.ts | 1 - .../tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts | 8 ++++++-- 10 files changed, 16 insertions(+), 7 deletions(-) rename src/packages/{tiny-mce => media/media}/modals/media-caption-alt-text/index.ts (100%) rename src/packages/{tiny-mce/modals => media/media/modals/media-caption-alt-text}/manifests.ts (63%) rename src/packages/{tiny-mce => media/media}/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts (100%) rename src/packages/{tiny-mce => media/media}/modals/media-caption-alt-text/media-caption-alt-text-modal.token.ts (100%) delete mode 100644 src/packages/tiny-mce/modals/index.ts diff --git a/src/packages/media/media/index.ts b/src/packages/media/media/index.ts index 8ee054676a..ca9211b392 100644 --- a/src/packages/media/media/index.ts +++ b/src/packages/media/media/index.ts @@ -11,6 +11,11 @@ export { UMB_MEDIA_TREE_ALIAS, UMB_MEDIA_TREE_PICKER_MODAL } from './tree/index. export { UMB_MEDIA_COLLECTION_ALIAS } from './collection/index.js'; export { UMB_MEDIA_MENU_ALIAS } from './menu/index.js'; export { UMB_MEDIA_PICKER_MODAL } from './modals/media-picker/index.js'; +export { + UMB_MEDIA_CAPTION_ALT_TEXT_MODAL, + type UmbMediaCaptionAltTextModalData, + type UmbMediaCaptionAltTextModalValue, +} from './modals/media-caption-alt-text/index.js'; export type { UmbMediaTreeItemModel } from './tree/index.js'; export { UmbMediaAuditLogRepository } from './audit-log/index.js'; diff --git a/src/packages/media/media/modals/index.ts b/src/packages/media/media/modals/index.ts index 380266b300..4dc380687c 100644 --- a/src/packages/media/media/modals/index.ts +++ b/src/packages/media/media/modals/index.ts @@ -1,2 +1,3 @@ export * from './image-cropper-editor/index.js'; +export * from './media-caption-alt-text/index.js'; export * from './media-picker/index.js'; diff --git a/src/packages/media/media/modals/manifests.ts b/src/packages/media/media/modals/manifests.ts index 260e1366e4..fb40e44c2f 100644 --- a/src/packages/media/media/modals/manifests.ts +++ b/src/packages/media/media/modals/manifests.ts @@ -1,4 +1,5 @@ import { manifests as imageCropperEditorManifests } from './image-cropper-editor/manifests.js'; +import { manifests as mediaCaptionAltTextManifests } from './media-caption-alt-text/manifests.js'; import { manifests as mediaPickerManifests } from './media-picker/manifests.js'; -export const manifests = [...imageCropperEditorManifests, ...mediaPickerManifests]; +export const manifests = [...imageCropperEditorManifests, ...mediaCaptionAltTextManifests, ...mediaPickerManifests]; diff --git a/src/packages/tiny-mce/modals/media-caption-alt-text/index.ts b/src/packages/media/media/modals/media-caption-alt-text/index.ts similarity index 100% rename from src/packages/tiny-mce/modals/media-caption-alt-text/index.ts rename to src/packages/media/media/modals/media-caption-alt-text/index.ts diff --git a/src/packages/tiny-mce/modals/manifests.ts b/src/packages/media/media/modals/media-caption-alt-text/manifests.ts similarity index 63% rename from src/packages/tiny-mce/modals/manifests.ts rename to src/packages/media/media/modals/media-caption-alt-text/manifests.ts index cd73c2fa3f..61cb7b6d72 100644 --- a/src/packages/tiny-mce/modals/manifests.ts +++ b/src/packages/media/media/modals/media-caption-alt-text/manifests.ts @@ -3,6 +3,6 @@ export const manifests: Array = [ type: 'modal', alias: 'Umb.Modal.MediaCaptionAltText', name: 'Media Caption Alt Text', - element: () => import('./media-caption-alt-text/media-caption-alt-text-modal.element.js'), + element: () => import('./media-caption-alt-text-modal.element.js'), }, ]; diff --git a/src/packages/tiny-mce/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts b/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts similarity index 100% rename from src/packages/tiny-mce/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts rename to src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts diff --git a/src/packages/tiny-mce/modals/media-caption-alt-text/media-caption-alt-text-modal.token.ts b/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.token.ts similarity index 100% rename from src/packages/tiny-mce/modals/media-caption-alt-text/media-caption-alt-text-modal.token.ts rename to src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.token.ts diff --git a/src/packages/tiny-mce/manifests.ts b/src/packages/tiny-mce/manifests.ts index 7c24fabe3e..0c978efd63 100644 --- a/src/packages/tiny-mce/manifests.ts +++ b/src/packages/tiny-mce/manifests.ts @@ -1,5 +1,4 @@ import { manifests as propertyEditors } from './property-editors/manifests.js'; import { manifests as plugins } from './plugins/manifests.js'; -import { manifests as modalManifests } from './modals/manifests.js'; -export const manifests: Array = [...propertyEditors, ...plugins, ...modalManifests]; +export const manifests: Array = [...propertyEditors, ...plugins]; diff --git a/src/packages/tiny-mce/modals/index.ts b/src/packages/tiny-mce/modals/index.ts deleted file mode 100644 index 011799ec00..0000000000 --- a/src/packages/tiny-mce/modals/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './media-caption-alt-text/index.js'; diff --git a/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index 91b79639a7..4f8194adcb 100644 --- a/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts @@ -1,11 +1,15 @@ import { getGuid } from '../utils.js'; -import { UMB_MEDIA_CAPTION_ALT_TEXT_MODAL } from '../modals/media-caption-alt-text/media-caption-alt-text-modal.token.js'; import { type TinyMcePluginArguments, UmbTinyMcePluginBase } from '../components/input-tiny-mce/tiny-mce-plugin.js'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { RawEditorOptions } from '@umbraco-cms/backoffice/external/tinymce'; import { UmbTemporaryFileRepository } from '@umbraco-cms/backoffice/temporary-file'; import { UmbId } from '@umbraco-cms/backoffice/id'; -import { sizeImageInEditor, uploadBlobImages, UMB_MEDIA_PICKER_MODAL } from '@umbraco-cms/backoffice/media'; +import { + sizeImageInEditor, + uploadBlobImages, + UMB_MEDIA_PICKER_MODAL, + UMB_MEDIA_CAPTION_ALT_TEXT_MODAL, +} from '@umbraco-cms/backoffice/media'; import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; interface MediaPickerTargetData { From 1e5125a730e61964f4e8dc8d556ce46548cac197 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:47:17 +0200 Subject: [PATCH 108/241] feat: move getGuid to global util --- .../{tiny-mce/utils.ts => core/utils/get-guid.function.ts} | 4 ---- src/packages/core/utils/index.ts | 1 + src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) rename src/packages/{tiny-mce/utils.ts => core/utils/get-guid.function.ts} (94%) diff --git a/src/packages/tiny-mce/utils.ts b/src/packages/core/utils/get-guid.function.ts similarity index 94% rename from src/packages/tiny-mce/utils.ts rename to src/packages/core/utils/get-guid.function.ts index d1236320b4..3078f5cf6a 100644 --- a/src/packages/tiny-mce/utils.ts +++ b/src/packages/core/utils/get-guid.function.ts @@ -1,7 +1,3 @@ -/** - * - * @param udi - */ export function getGuid(udi: string) { if (!udi.startsWith('umb://')) throw new Error('udi does not start with umb://'); diff --git a/src/packages/core/utils/index.ts b/src/packages/core/utils/index.ts index abbc560792..5babb4a5fe 100644 --- a/src/packages/core/utils/index.ts +++ b/src/packages/core/utils/index.ts @@ -1,6 +1,7 @@ export * from './debounce/debounce.function.js'; export * from './direction/index.js'; export * from './download/blob-download.function.js'; +export * from './get-guid.function.js'; export * from './get-processed-image-url.function.js'; export * from './math/math.js'; export * from './media/image-size.function.js'; diff --git a/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index 4f8194adcb..7e3658e8d1 100644 --- a/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts @@ -1,5 +1,5 @@ -import { getGuid } from '../utils.js'; import { type TinyMcePluginArguments, UmbTinyMcePluginBase } from '../components/input-tiny-mce/tiny-mce-plugin.js'; +import { getGuid } from '@umbraco-cms/backoffice/utils'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { RawEditorOptions } from '@umbraco-cms/backoffice/external/tinymce'; import { UmbTemporaryFileRepository } from '@umbraco-cms/backoffice/temporary-file'; From 3d970e5323c4bdd5d468338274c4ff2577cb037e Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 25 Sep 2024 15:04:55 +0200 Subject: [PATCH 109/241] Revert "feat: move getGuid to global util" This reverts commit 1e5125a730e61964f4e8dc8d556ce46548cac197. --- src/packages/core/utils/index.ts | 1 - src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts | 2 +- .../{core/utils/get-guid.function.ts => tiny-mce/utils.ts} | 4 ++++ 3 files changed, 5 insertions(+), 2 deletions(-) rename src/packages/{core/utils/get-guid.function.ts => tiny-mce/utils.ts} (94%) diff --git a/src/packages/core/utils/index.ts b/src/packages/core/utils/index.ts index 5babb4a5fe..abbc560792 100644 --- a/src/packages/core/utils/index.ts +++ b/src/packages/core/utils/index.ts @@ -1,7 +1,6 @@ export * from './debounce/debounce.function.js'; export * from './direction/index.js'; export * from './download/blob-download.function.js'; -export * from './get-guid.function.js'; export * from './get-processed-image-url.function.js'; export * from './math/math.js'; export * from './media/image-size.function.js'; diff --git a/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index 7e3658e8d1..4f8194adcb 100644 --- a/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts @@ -1,5 +1,5 @@ +import { getGuid } from '../utils.js'; import { type TinyMcePluginArguments, UmbTinyMcePluginBase } from '../components/input-tiny-mce/tiny-mce-plugin.js'; -import { getGuid } from '@umbraco-cms/backoffice/utils'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { RawEditorOptions } from '@umbraco-cms/backoffice/external/tinymce'; import { UmbTemporaryFileRepository } from '@umbraco-cms/backoffice/temporary-file'; diff --git a/src/packages/core/utils/get-guid.function.ts b/src/packages/tiny-mce/utils.ts similarity index 94% rename from src/packages/core/utils/get-guid.function.ts rename to src/packages/tiny-mce/utils.ts index 3078f5cf6a..d1236320b4 100644 --- a/src/packages/core/utils/get-guid.function.ts +++ b/src/packages/tiny-mce/utils.ts @@ -1,3 +1,7 @@ +/** + * + * @param udi + */ export function getGuid(udi: string) { if (!udi.startsWith('umb://')) throw new Error('udi does not start with umb://'); From 67d09c3ad776999924272d5abce0346842f8f48a Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:02:02 +0200 Subject: [PATCH 110/241] works --- ...ui-tiptap-toolbar-configuration.element.ts | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index df5b2896dd..7daabb016f 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -46,7 +46,6 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement if (!value) value = { extensions: [], toolbarLayout: [] }; this.#value = this.#deepClone(value) as NewestValue; - this.requestUpdate('#value'); } get value(): NewestValue { return this.#deepClone(this.#value) as NewestValue; @@ -62,7 +61,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement config?: UmbPropertyEditorConfigCollection; @state() - private _toolbarLayout: ToolbarLayout = [[[]]]; + private _selectedExtensions: string[] = []; @state() private _extensionCategories: ExtensionCategory[] = []; @@ -77,16 +76,14 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement if (!toolbarConfig) return; this._availableExtensions = toolbarConfig; - const extensions: Array = []; - - this._availableExtensions.forEach((v) => { - extensions.push({ + this._availableExtensions = this._availableExtensions.map((v) => { + return { ...v, - selected: this.value.extensions.includes(v.alias), - }); + selected: this.#value.extensions.includes(v.alias), + }; }); - const grouped = extensions.reduce((acc: any, item) => { + const grouped = this._availableExtensions.reduce((acc: any, item) => { const group = item.category || 'miscellaneous'; // Assign to "miscellaneous" if no group if (!acc[group]) { @@ -109,30 +106,48 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement item.selected = !item.selected; if (item.selected) { - const lastRow = this._toolbarLayout[this._toolbarLayout.length - 1]; // Last row + const lastRow = this.#value.toolbarLayout[this.#value.toolbarLayout.length - 1]; // Last row const lastGroup = lastRow[lastRow.length - 1]; // Last group in the last row lastGroup.push(item.alias); } else { - this._toolbarLayout = this._toolbarLayout.map((row) => + this.#value.toolbarLayout = this.#value.toolbarLayout.map((row) => row.map((group) => group.filter((alias) => alias !== item.alias)), ); } - this.requestUpdate(); + this.#value = { + ...this.#value, + extensions: item.selected + ? [...this.#value.extensions, item.alias] + : this.#value.extensions.filter((alias) => alias !== item.alias), + }; + + this.dispatchEvent(new UmbPropertyValueChangeEvent()); } #onChange = (event: CustomEvent) => { const value = (event.target as UmbTiptapToolbarGroupsConfigurationElement).value; - this.value = { - ...this.value, + + // If any groups or rows have been removed from the toolbar layout, remove the corresponding extensions + const extensions = value.flat(2); + // only keep the extensions that are in the new toolbar layout + const test = this.#value.extensions.filter((alias) => extensions.includes(alias)); + + this.#value = { toolbarLayout: value, + extensions: test, }; + + console.log('value', this.#value); + + // Remove the removed extensions from the available extensions + this.dispatchEvent(new UmbPropertyValueChangeEvent()); }; override render() { return html` - +
    ${repeat( this._extensionCategories, From fc21630bef54435eecaebf63bf08d8aeeeb13b5a Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Wed, 25 Sep 2024 17:42:18 +0200 Subject: [PATCH 111/241] using context --- ...ui-tiptap-toolbar-configuration.element.ts | 42 +++++------ ...ap-toolbar-groups-configuration.element.ts | 73 +++++++++---------- 2 files changed, 53 insertions(+), 62 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 7daabb016f..df518a84bf 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -12,6 +12,7 @@ import './tiptap-toolbar-groups-configuration.element.js'; import { tinymce } from '@umbraco-cms/backoffice/external/tinymce'; import type UmbTiptapToolbarGroupsConfigurationElement from './tiptap-toolbar-groups-configuration.element.js'; +import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; const tinyIconSet = tinymce.IconManager.get('default'); @@ -60,18 +61,31 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement @property({ attribute: false }) config?: UmbPropertyEditorConfigCollection; - @state() - private _selectedExtensions: string[] = []; - @state() private _extensionCategories: ExtensionCategory[] = []; @state() private _availableExtensions: ExtensionConfig[] = []; + #toolbarLayout = new UmbArrayState([[[]]], (x) => x); + toolbarLayout = this.#toolbarLayout.asObservable(); + + constructor() { + super(); + this.provideContext('umb-tiptap-toolbar-context', { state: this.#toolbarLayout, observable: this.toolbarLayout }); + + this.toolbarLayout.subscribe((value) => { + this.#value.toolbarLayout = value; + + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + }); + } + protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); + this.#toolbarLayout.setValue(this.#value.toolbarLayout); + const toolbarConfig = this.config?.getValueByAlias('toolbar'); if (!toolbarConfig) return; this._availableExtensions = toolbarConfig; @@ -125,29 +139,9 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement this.dispatchEvent(new UmbPropertyValueChangeEvent()); } - #onChange = (event: CustomEvent) => { - const value = (event.target as UmbTiptapToolbarGroupsConfigurationElement).value; - - // If any groups or rows have been removed from the toolbar layout, remove the corresponding extensions - const extensions = value.flat(2); - // only keep the extensions that are in the new toolbar layout - const test = this.#value.extensions.filter((alias) => extensions.includes(alias)); - - this.#value = { - toolbarLayout: value, - extensions: test, - }; - - console.log('value', this.#value); - - // Remove the removed extensions from the available extensions - - this.dispatchEvent(new UmbPropertyValueChangeEvent()); - }; - override render() { return html` - +
    ${repeat( this._extensionCategories, diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index b74105b57d..3b8e86bd83 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts @@ -1,9 +1,7 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { customElement, css, html, property, repeat, nothing } from '@umbraco-cms/backoffice/external/lit'; +import { customElement, css, html, property, repeat, nothing, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; - -type ToolbarLayout = Array>; +import type { Observable, UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; type Extension = { alias: string; @@ -14,63 +12,62 @@ type Extension = { @customElement('umb-tiptap-toolbar-groups-configuration') export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { @property({ attribute: false }) - set value(value: Array) { - //If value is null or does not have at least one row with one group, default to a single row with a single empty group - if (!value || value.length === 0 || value[0].length === 0) { - value = [[[]]]; - } - this._value = value; - this.requestUpdate(); - } - get value(): Array { - return this._value; - } + extensions: Array = []; - _value: Array = [[[]]]; + @state() + private _toolbar: string[][][] = [[[]]]; - @property({ attribute: false }) - extensions: Array = []; + #toolbarLayout: UmbArrayState | undefined; + + constructor() { + super(); + + this.consumeContext( + 'umb-tiptap-toolbar-context', + (instance: { state: UmbArrayState; observable: Observable }) => { + this.#toolbarLayout = instance.state; + + this.observe(instance.observable, (value) => { + this._toolbar = value.map((rows) => rows.map((groups) => [...groups])); + }); + }, + ); + } private moveItem = (from: [number, number, number], to: [number, number, number]) => { const [fromRow, fromGroup, fromItem] = from; const [toRow, toGroup, toItem] = to; // Get the item to move from the 'from' position - const itemToMove = this.value[fromRow][fromGroup][fromItem]; + const itemToMove = this._toolbar[fromRow][fromGroup][fromItem]; // Remove the item from the original position - this.value[fromRow][fromGroup].splice(fromItem, 1); + this._toolbar[fromRow][fromGroup].splice(fromItem, 1); // Insert the item into the new position - this.value[toRow][toGroup].splice(toItem, 0, itemToMove); + this._toolbar[toRow][toGroup].splice(toItem, 0, itemToMove); - // Trigger a re-render - this.requestUpdate('value'); - this.dispatchEvent(new UmbChangeEvent()); + this.#toolbarLayout?.setValue(this._toolbar); }; #addGroup = (rowIndex: number, groupIndex: number) => { - this.value[rowIndex].splice(groupIndex, 0, []); - this.requestUpdate('value'); - this.dispatchEvent(new UmbChangeEvent()); + this._toolbar[rowIndex].splice(groupIndex, 0, []); + this.#toolbarLayout?.setValue(this._toolbar); }; #removeGroup = (rowIndex: number, groupIndex: number) => { - this.value[rowIndex].splice(groupIndex, 1); - this.requestUpdate('value'); - this.dispatchEvent(new UmbChangeEvent()); + this._toolbar[rowIndex].splice(groupIndex, 1); + this.#toolbarLayout?.setValue(this._toolbar); }; #addRow = (rowIndex: number) => { - this.value.splice(rowIndex, 0, [[]]); - this.requestUpdate('value'); - this.dispatchEvent(new UmbChangeEvent()); + this._toolbar.splice(rowIndex, 0, [[]]); + this.#toolbarLayout?.setValue(this._toolbar); }; #removeRow = (rowIndex: number) => { - this.value.splice(rowIndex, 1); - this.requestUpdate('value'); - this.dispatchEvent(new UmbChangeEvent()); + this._toolbar.splice(rowIndex, 1); + this.#toolbarLayout?.setValue(this._toolbar); }; #onDragStart = (event: DragEvent, pos: [number, number, number]) => { @@ -125,8 +122,8 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { } override render() { - return html`${repeat(this._value, (row, rowIndex) => this.renderRow(row, rowIndex))} - `; + return html`${repeat(this._toolbar, (row, rowIndex) => this.renderRow(row, rowIndex))} + `; } static override styles = [ From 4c99df1cccf3e3e9a934aad767babfd2de4cf288 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:08:41 +0200 Subject: [PATCH 112/241] now works --- ...ui-tiptap-toolbar-configuration.element.ts | 66 ++++++++++--------- ...ap-toolbar-groups-configuration.element.ts | 4 +- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index df518a84bf..0daa31014a 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -11,17 +11,14 @@ import { import './tiptap-toolbar-groups-configuration.element.js'; import { tinymce } from '@umbraco-cms/backoffice/external/tinymce'; -import type UmbTiptapToolbarGroupsConfigurationElement from './tiptap-toolbar-groups-configuration.element.js'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; const tinyIconSet = tinymce.IconManager.get('default'); -type ToolbarLayout = string[][][]; - // If an extension exists in the extensions array but not in the toolbarLayout, it means that the extension is hidden in the toolbar -type NewestValue = { +type ServerValue = { extensions: Array; - toolbarLayout: ToolbarLayout; + toolbarLayout: string[][][]; }; type ExtensionConfig = { @@ -43,20 +40,16 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement implements UmbPropertyEditorUiElement { @property({ attribute: false }) - set value(value: NewestValue) { + set value(value: ServerValue) { if (!value) value = { extensions: [], toolbarLayout: [] }; - this.#value = this.#deepClone(value) as NewestValue; - } - get value(): NewestValue { - return this.#deepClone(this.#value) as NewestValue; + this.#value = value; } - - #deepClone(value: unknown): unknown { - return JSON.parse(JSON.stringify(value)); + get value(): ServerValue { + return this.#value; } - #value: NewestValue = { extensions: [], toolbarLayout: [] }; + #value: ServerValue = { extensions: [], toolbarLayout: [] }; @property({ attribute: false }) config?: UmbPropertyEditorConfigCollection; @@ -72,10 +65,13 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement constructor() { super(); - this.provideContext('umb-tiptap-toolbar-context', { state: this.#toolbarLayout, observable: this.toolbarLayout }); + this.provideContext('umb-tiptap-toolbar-context', { + state: this.#toolbarLayout, + observable: this.toolbarLayout, + }); this.toolbarLayout.subscribe((value) => { - this.#value.toolbarLayout = value; + this.#value = { ...this.#value, toolbarLayout: value }; this.dispatchEvent(new UmbPropertyValueChangeEvent()); }); @@ -86,11 +82,10 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement this.#toolbarLayout.setValue(this.#value.toolbarLayout); - const toolbarConfig = this.config?.getValueByAlias('toolbar'); - if (!toolbarConfig) return; - this._availableExtensions = toolbarConfig; + const toolbarConfigValue = this.config?.getValueByAlias('toolbar'); + if (!toolbarConfigValue) return; - this._availableExtensions = this._availableExtensions.map((v) => { + this._availableExtensions = toolbarConfigValue.map((v) => { return { ...v, selected: this.#value.extensions.includes(v.alias), @@ -119,29 +114,38 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement #onExtensionSelect(item: ExtensionConfig) { item.selected = !item.selected; + // Clone the toolbarLayout for immutability before making changes + const updatedLayout = this.#value.toolbarLayout.map((row) => row.map((group) => [...group])); + + // Add or remove the alias from toolbarLayout if (item.selected) { - const lastRow = this.#value.toolbarLayout[this.#value.toolbarLayout.length - 1]; // Last row - const lastGroup = lastRow[lastRow.length - 1]; // Last group in the last row - lastGroup.push(item.alias); + const lastRow = updatedLayout.at(-1); + const lastGroup = lastRow?.at(-1); + lastGroup?.push(item.alias); } else { - this.#value.toolbarLayout = this.#value.toolbarLayout.map((row) => - row.map((group) => group.filter((alias) => alias !== item.alias)), + updatedLayout.forEach((row) => + row.forEach((group, groupIndex) => { + row[groupIndex] = group.filter((alias) => alias !== item.alias); + }), ); } + // Update extensions based on the selection state + const updatedExtensions = item.selected + ? [...this.#value.extensions, item.alias] + : this.#value.extensions.filter((alias) => alias !== item.alias); + this.#value = { ...this.#value, - extensions: item.selected - ? [...this.#value.extensions, item.alias] - : this.#value.extensions.filter((alias) => alias !== item.alias), + extensions: updatedExtensions, }; - - this.dispatchEvent(new UmbPropertyValueChangeEvent()); + this.#toolbarLayout.setValue(updatedLayout); + this.requestUpdate('_extensionCategories'); } override render() { return html` - +
    ${repeat( this._extensionCategories, diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index 3b8e86bd83..411be39421 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts @@ -12,7 +12,7 @@ type Extension = { @customElement('umb-tiptap-toolbar-groups-configuration') export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { @property({ attribute: false }) - extensions: Array = []; + availableExtensions: Array = []; @state() private _toolbar: string[][][] = [[[]]]; @@ -87,7 +87,7 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { }; private renderItem(alias: string, rowIndex: number, groupIndex: number, itemIndex: number) { - const extension = this.extensions.find((ext) => ext.alias === alias); + const extension = this.availableExtensions.find((ext) => ext.alias === alias); if (!extension) return nothing; return html`
    Date: Thu, 26 Sep 2024 00:24:40 +0200 Subject: [PATCH 113/241] work --- ...ui-tiptap-toolbar-configuration.element.ts | 8 + ...ap-toolbar-groups-configuration.element.ts | 59 +++++++ ...p-toolbar-groups-configuration2.element.ts | 153 ++++++++++++++++++ 3 files changed, 220 insertions(+) create mode 100644 src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 0daa31014a..d288504bd5 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -15,6 +15,13 @@ import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; const tinyIconSet = tinymce.IconManager.get('default'); +type TestServerValue = [ + { + alias: string; + position: [number, number, number]; + }, +]; + // If an extension exists in the extensions array but not in the toolbarLayout, it means that the extension is hidden in the toolbar type ServerValue = { extensions: Array; @@ -146,6 +153,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement override render() { return html` +
    ${repeat( this._extensionCategories, diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index 411be39421..8c9051ef06 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts @@ -19,6 +19,60 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { #toolbarLayout: UmbArrayState | undefined; + #testData: TestServerValue = [ + { + alias: 'bold', + position: [0, 0, 0], + }, + { + alias: 'italic', + position: [0, 0, 1], + }, + { + alias: 'undo', + position: [0, 1, 0], + }, + { + alias: 'redo', + position: [0, 1, 1], + }, + { + alias: 'copy', + position: [1, 0, 0], + }, + { + alias: 'paste', + position: [1, 2, 0], + }, + ]; + + toStructuredData = (data: any): string[][][] => { + const structuredData: string[][][] = []; + + data.forEach(({ alias, position }) => { + const [rowIndex, groupIndex, aliasIndex] = position; + + // Ensure the row exists up to rowIndex + while (structuredData.length <= rowIndex) { + structuredData.push([]); + } + + const currentRow = structuredData[rowIndex]; + + // Ensure the group exists up to groupIndex within the row + while (currentRow.length <= groupIndex) { + currentRow.push([]); + } + + const currentGroup = currentRow[groupIndex]; + + // Ensure the alias is placed at the correct position in the group + currentGroup[aliasIndex] = alias; + }); + + return structuredData; + }; + constructor() { super(); @@ -32,6 +86,10 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { }); }, ); + + setTimeout(() => { + this._toolbar = this.toStructuredData(this.#testData); + }, 2000); } private moveItem = (from: [number, number, number], to: [number, number, number]) => { @@ -99,6 +157,7 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { } private renderGroup(group: string[], rowIndex: number, groupIndex: number) { + console.log('group', group); return html`
    { + const structuredData: string[][][] = []; + + data.forEach(({ alias, position }) => { + const [rowIndex, groupIndex, aliasIndex] = position; + + while (structuredData.length <= rowIndex) { + structuredData.push([]); + } + + const currentRow = structuredData[rowIndex]; + + while (currentRow.length <= groupIndex) { + currentRow.push([]); + } + + const currentGroup = currentRow[groupIndex]; + + currentGroup[aliasIndex] = alias; + }); + + return structuredData; + }; + + toOriginalFormat = (structuredData: string[][][]) => { + const originalData: any = []; + + structuredData.forEach((row, rowIndex) => { + row.forEach((group, groupIndex) => { + group.forEach((alias, aliasIndex) => { + if (alias) { + originalData.push({ + alias, + position: [rowIndex, groupIndex, aliasIndex], + }); + } + }); + }); + }); + + return originalData; + }; + + override render() { + return html``; + } + + static override styles = [ + UmbTextStyles, + css` + :host { + display: flex; + flex-direction: column; + gap: 6px; + } + .row { + position: relative; + display: flex; + gap: 12px; + } + .group { + position: relative; + display: flex; + gap: 3px; + border: 1px solid #ccc; + padding: 6px; + min-height: 24px; + min-width: 24px; + } + .item { + padding: 3px; + border: 1px solid #ccc; + border-radius: 3px; + background-color: #f9f9f9; + } + + .remove-group-button { + position: absolute; + top: -4px; + right: -4px; + display: none; + } + .group:hover .remove-group-button { + display: block; + } + + .remove-row-button { + position: absolute; + left: -25px; + top: 8px; + display: none; + } + .row:hover .remove-row-button { + display: block; + } + `, + ]; +} + +export default UmbTiptapToolbarGroupsConfiguration2Element; + +declare global { + interface HTMLElementTagNameMap { + 'umb-tiptap-toolbar-groups-configuration2': UmbTiptapToolbarGroupsConfiguration2Element; + } +} From 85e0cc21396543df2d34703975eecf2286742919 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 01:17:04 +0200 Subject: [PATCH 114/241] work work --- ...ui-tiptap-toolbar-configuration.element.ts | 102 ++++++------------ ...p-toolbar-groups-configuration2.element.ts | 65 ++++++++--- 2 files changed, 79 insertions(+), 88 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index d288504bd5..1ccb15acee 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -9,18 +9,17 @@ import { } from '@umbraco-cms/backoffice/property-editor'; import './tiptap-toolbar-groups-configuration.element.js'; +import './tiptap-toolbar-groups-configuration2.element.js'; import { tinymce } from '@umbraco-cms/backoffice/external/tinymce'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; const tinyIconSet = tinymce.IconManager.get('default'); -type TestServerValue = [ - { - alias: string; - position: [number, number, number]; - }, -]; +type TestServerValue = Array<{ + alias: string; + position: [number, number, number]; //TODO: This should be optional to allow for hiding extensions +}>; // If an extension exists in the extensions array but not in the toolbarLayout, it means that the extension is hidden in the toolbar type ServerValue = { @@ -32,13 +31,19 @@ type ExtensionConfig = { alias: string; label: string; icon?: string; - selected: boolean; category: string; }; +type Extension = { + alias: string; + label: string; + icon?: string; + selected: boolean; +}; + type ExtensionCategory = { category: string; - extensions: ExtensionConfig[]; + extensions: Extension[]; }; @customElement('umb-property-editor-ui-tiptap-toolbar-configuration') @@ -47,16 +52,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement implements UmbPropertyEditorUiElement { @property({ attribute: false }) - set value(value: ServerValue) { - if (!value) value = { extensions: [], toolbarLayout: [] }; - - this.#value = value; - } - get value(): ServerValue { - return this.#value; - } - - #value: ServerValue = { extensions: [], toolbarLayout: [] }; + value: TestServerValue = []; @property({ attribute: false }) config?: UmbPropertyEditorConfigCollection; @@ -65,95 +61,59 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement private _extensionCategories: ExtensionCategory[] = []; @state() - private _availableExtensions: ExtensionConfig[] = []; - - #toolbarLayout = new UmbArrayState([[[]]], (x) => x); - toolbarLayout = this.#toolbarLayout.asObservable(); - - constructor() { - super(); - this.provideContext('umb-tiptap-toolbar-context', { - state: this.#toolbarLayout, - observable: this.toolbarLayout, - }); - - this.toolbarLayout.subscribe((value) => { - this.#value = { ...this.#value, toolbarLayout: value }; - - this.dispatchEvent(new UmbPropertyValueChangeEvent()); - }); - } + private _extensionConfigs: ExtensionConfig[] = []; protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); - this.#toolbarLayout.setValue(this.#value.toolbarLayout); + this.#setupExtensionCategories(); + } + #setupExtensionCategories() { const toolbarConfigValue = this.config?.getValueByAlias('toolbar'); if (!toolbarConfigValue) return; - - this._availableExtensions = toolbarConfigValue.map((v) => { + const withSelected = toolbarConfigValue.map((v) => { return { ...v, - selected: this.#value.extensions.includes(v.alias), + selected: this.value.some((item) => item.alias === v.alias), }; }); - const grouped = this._availableExtensions.reduce((acc: any, item) => { + const grouped = withSelected.reduce((acc: any, item) => { const group = item.category || 'miscellaneous'; // Assign to "miscellaneous" if no group - if (!acc[group]) { acc[group] = []; } acc[group].push(item); return acc; }, {}); - this._extensionCategories = Object.keys(grouped).map((group) => ({ category: group.charAt(0).toUpperCase() + group.slice(1).replace(/-/g, ' '), extensions: grouped[group], })); - - this.requestUpdate('_toolbarConfig'); - this.requestUpdate('_availableExtensions'); } - #onExtensionSelect(item: ExtensionConfig) { + #onExtensionSelect(item: Extension) { item.selected = !item.selected; - // Clone the toolbarLayout for immutability before making changes - const updatedLayout = this.#value.toolbarLayout.map((row) => row.map((group) => [...group])); - - // Add or remove the alias from toolbarLayout if (item.selected) { - const lastRow = updatedLayout.at(-1); - const lastGroup = lastRow?.at(-1); - lastGroup?.push(item.alias); + this.value = [ + ...this.value, + { + alias: item.alias, + position: [0, 5, 0], //TODO remove when hiding extensions is implemented + }, + ]; } else { - updatedLayout.forEach((row) => - row.forEach((group, groupIndex) => { - row[groupIndex] = group.filter((alias) => alias !== item.alias); - }), - ); + this.value = this.value.filter((v) => v.alias !== item.alias); } - // Update extensions based on the selection state - const updatedExtensions = item.selected - ? [...this.#value.extensions, item.alias] - : this.#value.extensions.filter((alias) => alias !== item.alias); - - this.#value = { - ...this.#value, - extensions: updatedExtensions, - }; - this.#toolbarLayout.setValue(updatedLayout); this.requestUpdate('_extensionCategories'); } override render() { return html` - - +
    ${repeat( this._extensionCategories, diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index 0ecdff6ad6..d0b0018a5e 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts @@ -2,6 +2,7 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { customElement, css, html, property, repeat, nothing, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { Observable, UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; type Extension = { alias: string; @@ -9,45 +10,63 @@ type Extension = { icon?: string; }; -type TestServerValue = [ - { - alias: string; - position: [number, number, number]; - }, -]; +type TestServerValue = Array<{ + alias: string; + position: [number, number, number]; +}>; @customElement('umb-tiptap-toolbar-groups-configuration2') export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { #testData: TestServerValue = [ { - alias: 'bold', + alias: 'asdasd', position: [0, 0, 0], }, { - alias: 'italic', + alias: 'itafsdgsdglic', position: [0, 0, 1], }, { - alias: 'undo', + alias: 'sdgsdg', position: [0, 1, 0], }, { - alias: 'redo', + alias: 'asdasd', position: [0, 1, 1], }, { - alias: 'copy', + alias: 'afasf', position: [1, 0, 0], }, { - alias: 'paste', + alias: 'sdgsdg', position: [1, 2, 0], }, ]; - toStructuredData = (data: any): string[][][] => { + @property({ attribute: false }) + set value(value: TestServerValue) { + if (this.#originalFormat === value) return; + this.#originalFormat = value; + this._structuredData = this.toStructuredData(value); + } + + get value(): TestServerValue { + console.log('get value groups'); + return this.#originalFormat; + } + + @state() + _structuredData: string[][][] = [[[]]]; + + #originalFormat: TestServerValue = []; + + toStructuredData = (data: TestServerValue) => { + console.log('toStructuredData'); const structuredData: string[][][] = []; + if (!data.length) return [[[]]]; + data.forEach(({ alias, position }) => { const [rowIndex, groupIndex, aliasIndex] = position; @@ -70,6 +89,7 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { }; toOriginalFormat = (structuredData: string[][][]) => { + console.log('toOriginalFormat'); const originalData: any = []; structuredData.forEach((row, rowIndex) => { @@ -87,9 +107,20 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { return originalData; }; + private renderItem(alias: string) { + return html`
    ${alias}
    `; + } + + private renderGroup(group: string[]) { + return html`
    ${group.map((alias) => this.renderItem(alias))}
    `; + } + + private renderRow(row: string[][]) { + return html`
    ${repeat(row, (group) => this.renderGroup(group))}
    `; + } override render() { - return html``; + return html`${repeat(this._structuredData, (row) => this.renderRow(row))}`; } static override styles = [ @@ -109,16 +140,16 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { position: relative; display: flex; gap: 3px; - border: 1px solid #ccc; + border: 1px solid var(--uui-color-border); padding: 6px; min-height: 24px; min-width: 24px; } .item { padding: 3px; - border: 1px solid #ccc; + border: 1px solid var(--uui-color-border); border-radius: 3px; - background-color: #f9f9f9; + background-color: var(--uui-color-surface); } .remove-group-button { From e80274c7a7f54aec67def505803028c18a784d65 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 01:31:37 +0200 Subject: [PATCH 115/241] drag and drop --- ...ui-tiptap-toolbar-configuration.element.ts | 7 +- ...p-toolbar-groups-configuration2.element.ts | 107 +++++++++++------- 2 files changed, 66 insertions(+), 48 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 1ccb15acee..6af3211475 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -16,17 +16,12 @@ import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; const tinyIconSet = tinymce.IconManager.get('default'); +// If an extension does not have a position, it is considered hidden in the toolbar type TestServerValue = Array<{ alias: string; position: [number, number, number]; //TODO: This should be optional to allow for hiding extensions }>; -// If an extension exists in the extensions array but not in the toolbarLayout, it means that the extension is hidden in the toolbar -type ServerValue = { - extensions: Array; - toolbarLayout: string[][][]; -}; - type ExtensionConfig = { alias: string; label: string; diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index d0b0018a5e..5d48132cea 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts @@ -17,33 +17,6 @@ type TestServerValue = Array<{ @customElement('umb-tiptap-toolbar-groups-configuration2') export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { - #testData: TestServerValue = [ - { - alias: 'asdasd', - position: [0, 0, 0], - }, - { - alias: 'itafsdgsdglic', - position: [0, 0, 1], - }, - { - alias: 'sdgsdg', - position: [0, 1, 0], - }, - { - alias: 'asdasd', - position: [0, 1, 1], - }, - { - alias: 'afasf', - position: [1, 0, 0], - }, - { - alias: 'sdgsdg', - position: [1, 2, 0], - }, - ]; - @property({ attribute: false }) set value(value: TestServerValue) { if (this.#originalFormat === value) return; @@ -61,6 +34,71 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { #originalFormat: TestServerValue = []; + #currentDragAlias?: string; + + #onDragStart = (event: DragEvent, alias: string) => { + this.#currentDragAlias = alias; + event.dataTransfer!.dropEffect = 'move'; + }; + + #onDragOver = (event: DragEvent) => { + event.preventDefault(); + }; + + #onDrop = (event: DragEvent, toPos: [number, number, number]) => { + event.preventDefault(); + const fromPos = this.#originalFormat.find((item) => item.alias === this.#currentDragAlias)?.position; + if (!fromPos) return; + + this.moveItem(fromPos, toPos); + }; + + private moveItem = (from: [number, number, number], to: [number, number, number]) => { + const [fromRow, fromGroup, fromItem] = from; + const [toRow, toGroup, toItem] = to; + + // Get the item to move from the 'from' position + const itemToMove = this._structuredData[fromRow][fromGroup][fromItem]; + + // Remove the item from the original position + this._structuredData[fromRow][fromGroup].splice(fromItem, 1); + + // Insert the item into the new position + this._structuredData[toRow][toGroup].splice(toItem, 0, itemToMove); + + this.requestUpdate('_structuredData'); + + this.dispatchEvent(new UmbChangeEvent()); + }; + + private renderItem(alias: string) { + return html`
    this.#onDragStart(e, alias)}> + ${alias} +
    `; + } + + private renderGroup(group: string[], rowIndex: number, groupIndex: number) { + return html` +
    this.#onDrop(e, [rowIndex, groupIndex, group.length])}> + ${group.map((alias) => this.renderItem(alias))} +
    + `; + } + + private renderRow(row: string[][], rowIndex: number) { + return html` +
    ${repeat(row, (group, groupIndex) => this.renderGroup(group, rowIndex, groupIndex))}
    + `; + } + + override render() { + return html`${repeat(this._structuredData, (row, rowIndex) => this.renderRow(row, rowIndex))}`; + } + toStructuredData = (data: TestServerValue) => { console.log('toStructuredData'); const structuredData: string[][][] = []; @@ -107,21 +145,6 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { return originalData; }; - private renderItem(alias: string) { - return html`
    ${alias}
    `; - } - - private renderGroup(group: string[]) { - return html`
    ${group.map((alias) => this.renderItem(alias))}
    `; - } - - private renderRow(row: string[][]) { - return html`
    ${repeat(row, (group) => this.renderGroup(group))}
    `; - } - - override render() { - return html`${repeat(this._structuredData, (row) => this.renderRow(row))}`; - } static override styles = [ UmbTextStyles, From b2a55526dfec75b4d84adc8953bb68fae0b5e625 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 02:00:06 +0200 Subject: [PATCH 116/241] optional position and hidden extensions --- ...ui-tiptap-toolbar-configuration.element.ts | 3 +- ...p-toolbar-groups-configuration2.element.ts | 50 +++++++++++++------ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 6af3211475..6b309a4a61 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -19,7 +19,7 @@ const tinyIconSet = tinymce.IconManager.get('default'); // If an extension does not have a position, it is considered hidden in the toolbar type TestServerValue = Array<{ alias: string; - position: [number, number, number]; //TODO: This should be optional to allow for hiding extensions + position?: [number, number, number]; }>; type ExtensionConfig = { @@ -96,7 +96,6 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement ...this.value, { alias: item.alias, - position: [0, 5, 0], //TODO remove when hiding extensions is implemented }, ]; } else { diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index 5d48132cea..f7f79c73ed 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts @@ -12,20 +12,20 @@ type Extension = { type TestServerValue = Array<{ alias: string; - position: [number, number, number]; + position?: [number, number, number]; }>; @customElement('umb-tiptap-toolbar-groups-configuration2') export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { @property({ attribute: false }) set value(value: TestServerValue) { - if (this.#originalFormat === value) return; + // if (this.#originalFormat === value) return; + // TODO: also check if the added values have positions, if not, there's no need to update the structured data. this.#originalFormat = value; this._structuredData = this.toStructuredData(value); } get value(): TestServerValue { - console.log('get value groups'); return this.#originalFormat; } @@ -48,26 +48,33 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { #onDrop = (event: DragEvent, toPos: [number, number, number]) => { event.preventDefault(); const fromPos = this.#originalFormat.find((item) => item.alias === this.#currentDragAlias)?.position; - if (!fromPos) return; - this.moveItem(fromPos, toPos); + if (fromPos) { + this.moveItem(fromPos, toPos); + } else if (this.#currentDragAlias) { + this.insertItem(this.#currentDragAlias, toPos); + } }; private moveItem = (from: [number, number, number], to: [number, number, number]) => { - const [fromRow, fromGroup, fromItem] = from; - const [toRow, toGroup, toItem] = to; + const [rowIndex, groupIndex, itemIndex] = from; // Get the item to move from the 'from' position - const itemToMove = this._structuredData[fromRow][fromGroup][fromItem]; + const itemToMove = this._structuredData[rowIndex][groupIndex][itemIndex]; // Remove the item from the original position - this._structuredData[fromRow][fromGroup].splice(fromItem, 1); + this._structuredData[rowIndex][groupIndex].splice(itemIndex, 1); + this.insertItem(itemToMove, to); + }; + + private insertItem = (alias: string, toPos: [number, number, number]) => { + const [rowIndex, groupIndex, itemIndex] = toPos; // Insert the item into the new position - this._structuredData[toRow][toGroup].splice(toItem, 0, itemToMove); + this._structuredData[rowIndex][groupIndex].splice(itemIndex, 0, alias); + this.#originalFormat = this.toOriginalFormat(this._structuredData); this.requestUpdate('_structuredData'); - this.dispatchEvent(new UmbChangeEvent()); }; @@ -96,16 +103,22 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { } override render() { - return html`${repeat(this._structuredData, (row, rowIndex) => this.renderRow(row, rowIndex))}`; + return html`${repeat(this._structuredData, (row, rowIndex) => this.renderRow(row, rowIndex))} + +
    +

    Extensions hidden from the toolbar

    + ${this.#originalFormat.filter((item) => !item.position).map((item) => this.renderItem(item.alias))} +
    `; } toStructuredData = (data: TestServerValue) => { - console.log('toStructuredData'); const structuredData: string[][][] = []; if (!data.length) return [[[]]]; data.forEach(({ alias, position }) => { + if (!position) return; + const [rowIndex, groupIndex, aliasIndex] = position; while (structuredData.length <= rowIndex) { @@ -127,8 +140,7 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { }; toOriginalFormat = (structuredData: string[][][]) => { - console.log('toOriginalFormat'); - const originalData: any = []; + const originalData: TestServerValue = []; structuredData.forEach((row, rowIndex) => { row.forEach((group, groupIndex) => { @@ -143,6 +155,14 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { }); }); + // Add the items that are not in any group and does not already exist in the original data + const itemsNotInGroups = this.#originalFormat.filter((item) => !item.position); + itemsNotInGroups.forEach((item) => { + if (!originalData.some((i) => i.alias === item.alias)) { + originalData.push(item); + } + }); + return originalData; }; From 3015046f157f51238eb10d5b3d25aa242c725409 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 02:17:32 +0200 Subject: [PATCH 117/241] fix no items bug --- .../tiptap-toolbar-groups-configuration2.element.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index f7f79c73ed..9f52d8b66b 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts @@ -112,10 +112,9 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { } toStructuredData = (data: TestServerValue) => { - const structuredData: string[][][] = []; - if (!data.length) return [[[]]]; + const structuredData: string[][][] = [[[]]]; data.forEach(({ alias, position }) => { if (!position) return; From 1ecb2895fbdadd5670e1f2ba906ca65a3505fd49 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 02:17:43 +0200 Subject: [PATCH 118/241] add on change --- ...ui-tiptap-toolbar-configuration.element.ts | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 6b309a4a61..92d16dea1b 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -103,20 +103,23 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement } this.requestUpdate('_extensionCategories'); + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + } + + #onChange(event: CustomEvent) { + this.value = event.target.value; + this.dispatchEvent(new UmbPropertyValueChangeEvent()); } override render() { return html` - +
    ${repeat( this._extensionCategories, (category) => html`
    -

    - ${category.category} - Hide in toolbar -

    +

    ${category.category}

    ${repeat( category.extensions, (item) => @@ -131,7 +134,6 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement > ${item.label} -
    `, )}
    @@ -156,19 +158,20 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement --uui-button-border-width: 2px; } .extensions { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + display: flex; + flex-wrap: wrap; gap: 16px; margin-top: 16px; } .extension-item { display: grid; - grid-template-columns: 36px 1fr auto; + grid-template-columns: 36px 1fr; grid-template-rows: 1fr; align-items: center; gap: 9px; } .category { + flex: 1; background-color: var(--uui-color-surface-alt); padding: 12px; border-radius: 6px; From 82da2523628aa071493881cc93ea84c13f475a54 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 02:43:23 +0200 Subject: [PATCH 119/241] work --- ...ui-tiptap-toolbar-configuration.element.ts | 9 +++ ...p-toolbar-groups-configuration2.element.ts | 70 ++++++++++++++++--- 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 92d16dea1b..91fddc754b 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -108,6 +108,15 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement #onChange(event: CustomEvent) { this.value = event.target.value; + + // update the selected state of the extensions + // TODO this should be done in a more efficient way + this._extensionCategories.forEach((category) => { + category.extensions.forEach((item) => { + item.selected = this.value.some((v) => v.alias === item.alias); + }); + }); + this.dispatchEvent(new UmbPropertyValueChangeEvent()); } diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index 9f52d8b66b..090cbfe4a3 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts @@ -29,6 +29,8 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { return this.#originalFormat; } + //TODO: Use the context again so that we can remove items from the extensions list from here. + @state() _structuredData: string[][][] = [[[]]]; @@ -72,12 +74,49 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { const [rowIndex, groupIndex, itemIndex] = toPos; // Insert the item into the new position this._structuredData[rowIndex][groupIndex].splice(itemIndex, 0, alias); - this.#originalFormat = this.toOriginalFormat(this._structuredData); + this.#updateOriginalFormat(); this.requestUpdate('_structuredData'); this.dispatchEvent(new UmbChangeEvent()); }; + #addGroup = (rowIndex: number, groupIndex: number) => { + this._structuredData[rowIndex].splice(groupIndex, 0, []); + this.requestUpdate('_structuredData'); + }; + + #removeGroup = (rowIndex: number, groupIndex: number) => { + if (rowIndex === 0 && groupIndex === 0) { + // Prevent removing the last group + this._structuredData[rowIndex][groupIndex] = []; + } else { + this._structuredData[rowIndex].splice(groupIndex, 1); + } + this.requestUpdate('_structuredData'); + this.#updateOriginalFormat(); + }; + + #addRow = (rowIndex: number) => { + this._structuredData.splice(rowIndex, 0, [[]]); + this.requestUpdate('_structuredData'); + }; + + #removeRow = (rowIndex: number) => { + if (rowIndex === 0) { + // Prevent removing the last row + this._structuredData[rowIndex] = [[]]; + } else { + this._structuredData.splice(rowIndex, 1); + } + this.requestUpdate('_structuredData'); + this.#updateOriginalFormat(); + }; + + #updateOriginalFormat() { + this.#originalFormat = this.toOriginalFormat(this._structuredData); + this.dispatchEvent(new UmbChangeEvent()); + } + private renderItem(alias: string) { return html`
    this.#onDragStart(e, alias)}> ${alias} @@ -92,23 +131,31 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { @dragover=${this.#onDragOver} @drop=${(e: DragEvent) => this.#onDrop(e, [rowIndex, groupIndex, group.length])}> ${group.map((alias) => this.renderItem(alias))} +
    `; } private renderRow(row: string[][], rowIndex: number) { return html` -
    ${repeat(row, (group, groupIndex) => this.renderGroup(group, rowIndex, groupIndex))}
    +
    + ${repeat(row, (group, groupIndex) => this.renderGroup(group, rowIndex, groupIndex))} + + +
    `; } override render() { - return html`${repeat(this._structuredData, (row, rowIndex) => this.renderRow(row, rowIndex))} + return html` + ${repeat(this._structuredData, (row, rowIndex) => this.renderRow(row, rowIndex))} +

    Extensions hidden from the toolbar

    ${this.#originalFormat.filter((item) => !item.position).map((item) => this.renderItem(item.alias))} -
    `; +
    + `; } toStructuredData = (data: TestServerValue) => { @@ -154,11 +201,16 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { }); }); - // Add the items that are not in any group and does not already exist in the original data - const itemsNotInGroups = this.#originalFormat.filter((item) => !item.position); - itemsNotInGroups.forEach((item) => { - if (!originalData.some((i) => i.alias === item.alias)) { - originalData.push(item); + //TODO Should the extensions removed from the toolbar be removed from the original data as well? + // or just put into the hidden extensions list? (just remove the position property) + + // add items from this.#originalFormat only if they are not already in the structured data and they have no position + this.#originalFormat.forEach((item) => { + if (!item.position) { + const exists = originalData.find((i) => i.alias === item.alias); + if (!exists) { + originalData.push(item); + } } }); From f4df47276565c31964949a5ae34406da6a40e172 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 02:46:34 +0200 Subject: [PATCH 120/241] dont remove items completely --- ...p-toolbar-groups-configuration2.element.ts | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index 090cbfe4a3..d26adcab39 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts @@ -201,19 +201,25 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { }); }); - //TODO Should the extensions removed from the toolbar be removed from the original data as well? - // or just put into the hidden extensions list? (just remove the position property) - - // add items from this.#originalFormat only if they are not already in the structured data and they have no position + // add items from this.#originalFormat only if they are not already in the structured data. and if they have a position property set, unset it. this.#originalFormat.forEach((item) => { - if (!item.position) { - const exists = originalData.find((i) => i.alias === item.alias); - if (!exists) { - originalData.push(item); - } + if (!originalData.some((i) => i.alias === item.alias)) { + originalData.push({ + alias: item.alias, + }); } }); + // TODO: this code removes the items completely, while the one above just puts them back into the hidden extensions list. Which one do we prefer? + // this.#originalFormat.forEach((item) => { + // if (!item.position) { + // const exists = originalData.find((i) => i.alias === item.alias); + // if (!exists) { + // originalData.push(item); + // } + // } + // }); + return originalData; }; From bff3a190823fd41b2984d35ce756d072f97f755a Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 02:49:46 +0200 Subject: [PATCH 121/241] styling --- .../tiptap-toolbar-groups-configuration2.element.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index d26adcab39..8fca14cd4f 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts @@ -151,8 +151,8 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { ${repeat(this._structuredData, (row, rowIndex) => this.renderRow(row, rowIndex))} -
    -

    Extensions hidden from the toolbar

    +

    Extensions hidden from the toolbar

    +
    ${this.#originalFormat.filter((item) => !item.position).map((item) => this.renderItem(item.alias))}
    `; @@ -231,6 +231,13 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { flex-direction: column; gap: 6px; } + .hidden-extensions { + display: flex; + gap: 6px; + } + .hidden-extensions-header { + margin-bottom: 3px; + } .row { position: relative; display: flex; @@ -251,7 +258,6 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { border-radius: 3px; background-color: var(--uui-color-surface); } - .remove-group-button { position: absolute; top: -4px; From e34b9d1f9e930f7490e90918f3fd634540960939 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 09:13:09 +0200 Subject: [PATCH 122/241] fix: correct import after merge --- .../rte/tiptap/extensions/umb/code-editor.extension.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts b/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts index be55c65dbe..32722af243 100644 --- a/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts @@ -1,6 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import { UMB_CODE_EDITOR_MODAL } from '@umbraco-cms/backoffice/code-editor'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -import { UMB_CODE_EDITOR_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; export default class UmbTiptapCodeEditorExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => []; From 3a342832ae461e713e74c23a4e295d277eb584e8 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 09:14:34 +0200 Subject: [PATCH 123/241] fix: correct types after merge --- src/mocks/data/document/document.data.ts | 2 ++ src/packages/tiny-mce/index.ts | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mocks/data/document/document.data.ts b/src/mocks/data/document/document.data.ts index 6b512da1de..f220a31719 100644 --- a/src/mocks/data/document/document.data.ts +++ b/src/mocks/data/document/document.data.ts @@ -892,6 +892,7 @@ export const data: Array = [ values: [ { alias: 'tiptap', + editorAlias: 'Umb.PropertyEditorUi.Tiptap', culture: null, segment: null, value: { @@ -909,6 +910,7 @@ export const data: Array = [ }, { alias: 'tinymce', + editorAlias: 'Umb.PropertyEditorUi.TinyMCE', culture: null, segment: null, value: { diff --git a/src/packages/tiny-mce/index.ts b/src/packages/tiny-mce/index.ts index 97c9d86639..8a8c2711ca 100644 --- a/src/packages/tiny-mce/index.ts +++ b/src/packages/tiny-mce/index.ts @@ -1,2 +1 @@ export * from './components/index.js'; -export * from './modals/index.js'; From 0129e0d0f92cd136040c11ef75259615dbf7e953 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:47:17 +0200 Subject: [PATCH 124/241] feat: move getGuid to global util --- .../{tiny-mce/utils.ts => core/utils/get-guid.function.ts} | 4 ---- src/packages/core/utils/index.ts | 1 + src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) rename src/packages/{tiny-mce/utils.ts => core/utils/get-guid.function.ts} (94%) diff --git a/src/packages/tiny-mce/utils.ts b/src/packages/core/utils/get-guid.function.ts similarity index 94% rename from src/packages/tiny-mce/utils.ts rename to src/packages/core/utils/get-guid.function.ts index d1236320b4..3078f5cf6a 100644 --- a/src/packages/tiny-mce/utils.ts +++ b/src/packages/core/utils/get-guid.function.ts @@ -1,7 +1,3 @@ -/** - * - * @param udi - */ export function getGuid(udi: string) { if (!udi.startsWith('umb://')) throw new Error('udi does not start with umb://'); diff --git a/src/packages/core/utils/index.ts b/src/packages/core/utils/index.ts index abbc560792..5babb4a5fe 100644 --- a/src/packages/core/utils/index.ts +++ b/src/packages/core/utils/index.ts @@ -1,6 +1,7 @@ export * from './debounce/debounce.function.js'; export * from './direction/index.js'; export * from './download/blob-download.function.js'; +export * from './get-guid.function.js'; export * from './get-processed-image-url.function.js'; export * from './math/math.js'; export * from './media/image-size.function.js'; diff --git a/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index 4f8194adcb..7e3658e8d1 100644 --- a/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts @@ -1,5 +1,5 @@ -import { getGuid } from '../utils.js'; import { type TinyMcePluginArguments, UmbTinyMcePluginBase } from '../components/input-tiny-mce/tiny-mce-plugin.js'; +import { getGuid } from '@umbraco-cms/backoffice/utils'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { RawEditorOptions } from '@umbraco-cms/backoffice/external/tinymce'; import { UmbTemporaryFileRepository } from '@umbraco-cms/backoffice/temporary-file'; From d86185b7efe90dad43672464986e92fa15345066 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 09:29:14 +0200 Subject: [PATCH 125/241] feat: rename getGuid to getGuidFromUdi --- ...get-guid.function.ts => get-guid-from-udi.function.ts} | 8 +++++++- src/packages/core/utils/index.ts | 2 +- .../tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) rename src/packages/core/utils/{get-guid.function.ts => get-guid-from-udi.function.ts} (58%) diff --git a/src/packages/core/utils/get-guid.function.ts b/src/packages/core/utils/get-guid-from-udi.function.ts similarity index 58% rename from src/packages/core/utils/get-guid.function.ts rename to src/packages/core/utils/get-guid-from-udi.function.ts index 3078f5cf6a..92655aae55 100644 --- a/src/packages/core/utils/get-guid.function.ts +++ b/src/packages/core/utils/get-guid-from-udi.function.ts @@ -1,4 +1,10 @@ -export function getGuid(udi: string) { +/** + * Get the guid from a UDI. + * @example getGuidFromUdi('umb://document/4f058f8b1f7e4f3e8b4b6b4b4b6b4b6b') // '4f058f8b-1f7e-4f3e-8b4b-6b4b4b6b4b6b' + * @param {string} udi The UDI to get the guid from. + * @returns {string} The guid from the UDI. + */ +export function getGuidFromUdi(udi: string) { if (!udi.startsWith('umb://')) throw new Error('udi does not start with umb://'); const withoutScheme = udi.replace('umb://', ''); diff --git a/src/packages/core/utils/index.ts b/src/packages/core/utils/index.ts index 5babb4a5fe..7bbf20844e 100644 --- a/src/packages/core/utils/index.ts +++ b/src/packages/core/utils/index.ts @@ -1,7 +1,7 @@ export * from './debounce/debounce.function.js'; export * from './direction/index.js'; export * from './download/blob-download.function.js'; -export * from './get-guid.function.js'; +export * from './get-guid-from-udi.function.js'; export * from './get-processed-image-url.function.js'; export * from './math/math.js'; export * from './media/image-size.function.js'; diff --git a/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index 7e3658e8d1..b8ab127669 100644 --- a/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts @@ -1,5 +1,5 @@ import { type TinyMcePluginArguments, UmbTinyMcePluginBase } from '../components/input-tiny-mce/tiny-mce-plugin.js'; -import { getGuid } from '@umbraco-cms/backoffice/utils'; +import { getGuidFromUdi } from '@umbraco-cms/backoffice/utils'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { RawEditorOptions } from '@umbraco-cms/backoffice/external/tinymce'; import { UmbTemporaryFileRepository } from '@umbraco-cms/backoffice/temporary-file'; @@ -147,7 +147,7 @@ export default class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase { //startNodeIsVirtual, }, value: { - selection: currentTarget.udi ? [getGuid(currentTarget.udi)] : [], + selection: currentTarget.udi ? [getGuidFromUdi(currentTarget.udi)] : [], }, }); From 6d3eb881c712dc65698e27391dda20defbbe87c7 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 09:32:36 +0200 Subject: [PATCH 126/241] feat: set styling of paragraphs to emulate tiny --- .../tiptap/components/input-tiptap/input-tiptap.element.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index b37d8d3f0c..85e18d8a67 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -83,8 +83,8 @@ export class UmbInputTiptapElement extends UmbFormControlMixin('maxWidth'); const maxHeight = this.configuration?.getValueByAlias('maxHeight'); - this.setAttribute('style', `max-width: ${maxWidth}px;`); - element.setAttribute('style', `max-height: ${maxHeight}px;`); + if (maxWidth) this.setAttribute('style', `max-width: ${maxWidth}px;`); + if (maxHeight) element.setAttribute('style', `max-height: ${maxHeight}px;`); const extensions = this._extensions .map((ext) => ext.getTiptapExtensions({ configuration: this.configuration })) @@ -176,8 +176,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin Date: Thu, 26 Sep 2024 09:35:09 +0200 Subject: [PATCH 127/241] feat: add highlight of blockpicker plugin --- .../block-rte/tiptap-extension/block-picker.extension.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts b/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts index d70c9e9adc..42e48b8a7d 100644 --- a/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts +++ b/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts @@ -122,10 +122,7 @@ export default class UmbTiptapBlockPickerExtension extends UmbTiptapToolbarEleme } override isActive(editor: Editor) { - return ( - editor.isActive(`umb-rte-block[${UMB_DATA_CONTENT_UDI}]`) || - editor.isActive(`umb-rte-block-inline[${UMB_DATA_CONTENT_UDI}]`) - ); + return editor.isActive('umbRteBlock') || editor.isActive('umbRteBlockInline'); } override async execute() { From 3ac39a6724afc992db4dbfb0b1e26c521425cef5 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Thu, 19 Sep 2024 17:04:23 +0100 Subject: [PATCH 128/241] Tiptap extension: Embedded Media --- .../tiptap-umb-embedded-media.extension.ts | 54 +++++++++++++++++++ src/external/tiptap/index.ts | 1 + .../modal/embedded-media-modal.element.ts | 6 ++- .../input-tiptap/input-tiptap.element.ts | 26 +++++++++ .../rte/tiptap/extensions/manifests.ts | 7 +-- .../tiptap/extensions/umb/embed.extension.ts | 11 ---- .../umb/embedded-media.extension.ts | 38 +++++++++++++ 7 files changed, 127 insertions(+), 16 deletions(-) create mode 100644 src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts delete mode 100644 src/packages/rte/tiptap/extensions/umb/embed.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts diff --git a/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts b/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts new file mode 100644 index 0000000000..bcef5ee6eb --- /dev/null +++ b/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts @@ -0,0 +1,54 @@ +import { mergeAttributes, Node } from '@tiptap/core'; + +export const umbEmbeddedMedia = Node.create({ + name: 'umbEmbeddedMedia', + group() { + return this.options.inline ? 'inline' : 'block'; + }, + inline() { + return this.options.inline; + }, + atom: true, + marks: '', + draggable: true, + selectable: true, + + addAttributes() { + return { + 'data-embed-constrain': { default: false }, + 'data-embed-height': { default: 240 }, + 'data-embed-url': { default: null }, + 'data-embed-width': { default: 360 }, + markup: { default: null }, + }; + }, + + parseHTML() { + return [{ tag: 'div', class: 'umb-embed-holder', getAttrs: (node) => ({ markup: node.innerHTML }) }]; + }, + + renderHTML({ HTMLAttributes }) { + const { markup, ...attrs } = HTMLAttributes; + const embed = document.createRange().createContextualFragment(markup); + return ['div', mergeAttributes({ class: 'umb-embed-holder' }, attrs), embed]; + }, + + addCommands() { + return { + setEmbeddedMedia: + (options) => + ({ commands }) => { + const attrs = { markup: options.markup, 'data-embed-url': options.url }; + return commands.insertContent({ type: this.name, attrs }); + }, + }; + }, +}); + +declare module '@tiptap/core' { + interface Commands { + umbEmbeddedMedia: { + setEmbeddedMedia: (options: { markup: string; url: string }) => ReturnType; + }; + } +} diff --git a/src/external/tiptap/index.ts b/src/external/tiptap/index.ts index 5c6fabf333..6f9245ee78 100644 --- a/src/external/tiptap/index.ts +++ b/src/external/tiptap/index.ts @@ -24,4 +24,5 @@ export { TextAlign } from '@tiptap/extension-text-align'; export { Underline } from '@tiptap/extension-underline'; export { Image } from '@tiptap/extension-image'; // CUSTOM EXTENSIONS +export * from './extensions/tiptap-umb-embedded-media.extension.js'; export * from './extensions/tiptap-umb-image.extension.js'; diff --git a/src/packages/embedded-media/modal/embedded-media-modal.element.ts b/src/packages/embedded-media/modal/embedded-media-modal.element.ts index fcf5ff01a6..62f8468c17 100644 --- a/src/packages/embedded-media/modal/embedded-media-modal.element.ts +++ b/src/packages/embedded-media/modal/embedded-media-modal.element.ts @@ -1,8 +1,9 @@ import { UmbOEmbedRepository } from '../repository/oembed.repository.js'; import type { UmbEmbeddedMediaModalData, UmbEmbeddedMediaModalValue } from './embedded-media-modal.token.js'; import { css, html, unsafeHTML, when, customElement, state } from '@umbraco-cms/backoffice/external/lit'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { umbFocus } from '@umbraco-cms/backoffice/lit-element'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UUIButtonState, UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; @customElement('umb-embedded-media-modal') @@ -27,6 +28,7 @@ export class UmbEmbeddedMediaModalElement extends UmbModalBaseElement< override connectedCallback() { super.connectedCallback(); + if (this.data?.width) this._width = this.data.width; if (this.data?.height) this._height = this.data.height; if (this.data?.constrain) this.value = { ...this.value, constrain: this.data.constrain }; @@ -81,7 +83,7 @@ export class UmbEmbeddedMediaModalElement extends UmbModalBaseElement<
    - + * { + user-select: none; + pointer-events: none; + } + + .umb-embed-holder.ProseMirror-selectednode { + outline: 2px solid var(--uui-palette-spanish-pink-light); + } + + .umb-embed-holder::before { + z-index: 1000; + width: 100%; + height: 100%; + position: absolute; + content: ' '; + } + + .umb-embed-holder.ProseMirror-selectednode::before { + background: rgba(0, 0, 0, 0.025); + } } `, ]; diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index cb135c4773..827e80d2fa 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -14,6 +14,7 @@ const kinds: Array = [ }, ]; +// TODO: [LK] Move each of these to their corresponding packages, e.g. "code-editor", "embedded-media", "media", "multi-url-picker" const umbExtensions: Array = [ { type: 'tiptapExtension', @@ -33,11 +34,11 @@ const umbExtensions: Array import('./umb/embed.extension.js'), + api: () => import('./umb/embedded-media.extension.js'), meta: { - alias: 'umb-embed', + alias: 'umb-embedded-media', icon: 'icon-embed', - label: 'Embed', + label: '#general_embed', }, }, { diff --git a/src/packages/rte/tiptap/extensions/umb/embed.extension.ts b/src/packages/rte/tiptap/extensions/umb/embed.extension.ts deleted file mode 100644 index f09ecfd084..0000000000 --- a/src/packages/rte/tiptap/extensions/umb/embed.extension.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; - -export default class UmbTiptapEmbedExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions = () => []; - - override async execute(editor?: Editor) { - console.log('umb-embed.execute', editor); - // Research: https://github.com/ueberdosis/tiptap/tree/main/packages/extension-youtube - } -} diff --git a/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts b/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts new file mode 100644 index 0000000000..ccd8ee60e0 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts @@ -0,0 +1,38 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import { umbEmbeddedMedia } from '@umbraco-cms/backoffice/external/tiptap'; +import { UMB_EMBEDDED_MEDIA_MODAL } from '@umbraco-cms/backoffice/embedded-media'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapEmbedExtensionApi extends UmbTiptapToolbarElementApiBase { + getTiptapExtensions = () => [umbEmbeddedMedia.configure({ inline: true })]; + + override isActive = (editor: Editor) => editor.isActive(umbEmbeddedMedia.name) === true; + + override async execute(editor?: Editor) { + const data = { + constrain: false, + height: 240, + width: 360, + url: '', + }; + + const attrs = editor?.getAttributes(umbEmbeddedMedia.name); + if (attrs) { + data.constrain = attrs['data-embed-constrain']; + data.height = attrs['data-embed-height']; + data.width = attrs['data-embed-width']; + data.url = attrs['data-embed-url']; + } + + const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const modalHandler = modalManager.open(this, UMB_EMBEDDED_MEDIA_MODAL, { data }); + + if (!modalHandler) return; + + const result = await modalHandler.onSubmit().catch(() => undefined); + if (!result) return; + + editor?.commands.setEmbeddedMedia(result); + } +} From d276929a7aad64e1a717e39b0f8eb14ece45f325 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 10:18:35 +0200 Subject: [PATCH 129/241] init input read config --- .../input-tiptap/input-tiptap.element.ts | 12 +- .../input-tiptap/tiptap-fixed-menu.element.ts | 10 +- ...ui-tiptap-toolbar-configuration.element.ts | 39 +++++-- ...p-toolbar-groups-configuration2.element.ts | 4 +- .../property-editors/tiptap/manifests.ts | 104 +++++++++--------- 5 files changed, 105 insertions(+), 64 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 0aa053cde2..8370dc7cdb 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -56,6 +56,12 @@ export class UmbInputTiptapElement extends UmbFormControlMixin = []; + protected override async firstUpdated() { await Promise.all([await this.#loadExtensions(), await this.#loadEditor()]); } @@ -83,6 +89,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin('maxWidth'); const maxHeight = this.configuration?.getValueByAlias('maxHeight'); + this._toolbarConfig = this.configuration?.getValueByAlias('toolbar') as any; this.setAttribute('style', `max-width: ${maxWidth}px;`); element.setAttribute('style', `max-height: ${maxHeight}px;`); @@ -108,7 +115,10 @@ export class UmbInputTiptapElement extends UmbFormControlMixin html``, () => html` - + `, )}
    diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 5b879b5c4a..ad36b8bd8c 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -10,6 +10,12 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { @property({ type: Boolean, reflect: true }) readonly = false; + @property({ attribute: false }) + toolbarConfig: Array<{ + alias: string; + position?: [number, number, number]; + }> = []; + @property({ attribute: false }) set editor(value) { const oldValue = this.#editor; @@ -27,7 +33,9 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { return html` !!ext.kind || !!ext.element} + .filter=${(ext: ManifestTiptapExtension) => { + return !!this.toolbarConfig.find((x) => x.alias === ext.alias) && (!!ext.kind || !!ext.element); + }} .elementProps=${{ editor: this.editor }}> `; diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 91fddc754b..9138d3847f 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -2,7 +2,7 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import { customElement, css, html, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; +import { umbExtensionsRegistry, type UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { UmbPropertyValueChangeEvent, type UmbPropertyEditorConfigCollection, @@ -47,7 +47,15 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement implements UmbPropertyEditorUiElement { @property({ attribute: false }) - value: TestServerValue = []; + set value(value: TestServerValue) { + if (!value) value = []; + this.#value = value; + } + get value(): TestServerValue { + return this.#value; + } + + #value: TestServerValue = []; @property({ attribute: false }) config?: UmbPropertyEditorConfigCollection; @@ -58,19 +66,34 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement @state() private _extensionConfigs: ExtensionConfig[] = []; + @state() + private _extensions: ExtensionConfig[] = []; + protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); - this.#setupExtensionCategories(); + this.observe(umbExtensionsRegistry.byType('tiptapExtension'), (extensions) => { + console.log('extensions', extensions); + this._extensions = extensions.map((ext) => { + return { + alias: ext.alias, + label: ext.meta.label, + icon: ext.meta.icon, + category: '', + }; + }); + this.#setupExtensionCategories(); + }); } #setupExtensionCategories() { - const toolbarConfigValue = this.config?.getValueByAlias('toolbar'); - if (!toolbarConfigValue) return; - const withSelected = toolbarConfigValue.map((v) => { + // console.log('exensions', this._extensions); + // const toolbarConfigValue = this.config?.getValueByAlias('toolbar'); + // if (!toolbarConfigValue) return; + const withSelected = this._extensions.map((v) => { return { ...v, - selected: this.value.some((item) => item.alias === v.alias), + selected: this.value?.some((item) => item.alias === v.alias), }; }); @@ -140,7 +163,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement label=${item.label} .value=${item.alias} @click=${() => this.#onExtensionSelect(item)} - >
    ${item.label}
    `, diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index 8fca14cd4f..6d94938f87 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts @@ -153,13 +153,13 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement {

    Extensions hidden from the toolbar

    - ${this.#originalFormat.filter((item) => !item.position).map((item) => this.renderItem(item.alias))} + ${this.#originalFormat?.filter((item) => !item.position).map((item) => this.renderItem(item.alias))}
    `; } toStructuredData = (data: TestServerValue) => { - if (!data.length) return [[[]]]; + if (!data?.length) return [[[]]]; const structuredData: string[][][] = [[[]]]; data.forEach(({ alias, position }) => { diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 10051e7b93..57baf235eb 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -1,4 +1,4 @@ -import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; +import { umbExtensionsRegistry, type ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; export const manifests: Array = [ { @@ -19,67 +19,67 @@ export const manifests: Array = [ description: 'Pick the toolbar options that should be available when editing', propertyEditorUiAlias: 'Umb.PropertyEditorUi.Tiptap.ToolbarConfiguration', weight: 10, - config: [ - { - alias: 'toolbar', - value: [ - // Clipboard Group - { alias: 'undo', label: 'Undo', icon: 'undo', category: 'clipboard' }, - { alias: 'redo', label: 'Redo', icon: 'redo', category: 'clipboard' }, - { alias: 'cut', label: 'Cut', icon: 'cut', category: 'clipboard' }, - { alias: 'copy', label: 'Copy', icon: 'copy', category: 'clipboard' }, - { alias: 'paste', label: 'Paste', icon: 'paste', category: 'clipboard' }, + // config: [ + // { + // alias: 'toolbar', + // value: [ + // // Clipboard Group + // { alias: 'undo', label: 'Undo', icon: 'undo', category: 'clipboard' }, + // { alias: 'redo', label: 'Redo', icon: 'redo', category: 'clipboard' }, + // { alias: 'cut', label: 'Cut', icon: 'cut', category: 'clipboard' }, + // { alias: 'copy', label: 'Copy', icon: 'copy', category: 'clipboard' }, + // { alias: 'paste', label: 'Paste', icon: 'paste', category: 'clipboard' }, - // Formatting Group - { alias: 'bold', label: 'Bold', icon: 'bold', category: 'formatting' }, - { alias: 'italic', label: 'Italic', icon: 'italic', category: 'formatting' }, - { alias: 'underline', label: 'Underline', icon: 'underline', category: 'formatting' }, - { alias: 'strikethrough', label: 'Strikethrough', icon: 'strike-through', category: 'formatting' }, - { alias: 'removeformat', label: 'Remove format', icon: 'remove-formatting', category: 'formatting' }, + // // Formatting Group + // { alias: 'bold', label: 'Bold', icon: 'bold', category: 'formatting' }, + // { alias: 'italic', label: 'Italic', icon: 'italic', category: 'formatting' }, + // { alias: 'underline', label: 'Underline', icon: 'underline', category: 'formatting' }, + // { alias: 'strikethrough', label: 'Strikethrough', icon: 'strike-through', category: 'formatting' }, + // { alias: 'removeformat', label: 'Remove format', icon: 'remove-formatting', category: 'formatting' }, - // Color Group - { alias: 'forecolor', label: 'Text color', icon: 'text-color', category: 'color' }, - { alias: 'backcolor', label: 'Background color', icon: 'highlight-bg-color', category: 'color' }, + // // Color Group + // { alias: 'forecolor', label: 'Text color', icon: 'text-color', category: 'color' }, + // { alias: 'backcolor', label: 'Background color', icon: 'highlight-bg-color', category: 'color' }, - // Alignment Group - { alias: 'alignleft', label: 'Align left', icon: 'align-left', category: 'alignment' }, - { alias: 'aligncenter', label: 'Align center', icon: 'align-center', category: 'alignment' }, - { alias: 'alignright', label: 'Align right', icon: 'align-right', category: 'alignment' }, - { alias: 'alignjustify', label: 'Justify justify', icon: 'align-justify', category: 'alignment' }, + // // Alignment Group + // { alias: 'alignleft', label: 'Align left', icon: 'align-left', category: 'alignment' }, + // { alias: 'aligncenter', label: 'Align center', icon: 'align-center', category: 'alignment' }, + // { alias: 'alignright', label: 'Align right', icon: 'align-right', category: 'alignment' }, + // { alias: 'alignjustify', label: 'Justify justify', icon: 'align-justify', category: 'alignment' }, - // List Group - { alias: 'bullist', label: 'Bullet list', icon: 'unordered-list', category: 'list' }, - { alias: 'numlist', label: 'Numbered list', icon: 'ordered-list', category: 'list' }, + // // List Group + // { alias: 'bullist', label: 'Bullet list', icon: 'unordered-list', category: 'list' }, + // { alias: 'numlist', label: 'Numbered list', icon: 'ordered-list', category: 'list' }, - // Indentation Group - { alias: 'outdent', label: 'Outdent', icon: 'outdent', category: 'indentation' }, - { alias: 'indent', label: 'Indent', icon: 'indent', category: 'indentation' }, + // // Indentation Group + // { alias: 'outdent', label: 'Outdent', icon: 'outdent', category: 'indentation' }, + // { alias: 'indent', label: 'Indent', icon: 'indent', category: 'indentation' }, - // Insert Elements Group - { alias: 'anchor', label: 'Anchor', icon: 'bookmark', category: 'insert' }, - { alias: 'table', label: 'Table', icon: 'table', category: 'insert' }, - { alias: 'hr', label: 'Horizontal rule', icon: 'horizontal-rule', category: 'insert' }, - { alias: 'charmap', label: 'Character map', icon: 'insert-character', category: 'insert' }, + // // Insert Elements Group + // { alias: 'anchor', label: 'Anchor', icon: 'bookmark', category: 'insert' }, + // { alias: 'table', label: 'Table', icon: 'table', category: 'insert' }, + // { alias: 'hr', label: 'Horizontal rule', icon: 'horizontal-rule', category: 'insert' }, + // { alias: 'charmap', label: 'Character map', icon: 'insert-character', category: 'insert' }, - // Direction Group - { alias: 'rtl', label: 'Right to left', icon: 'rtl', category: 'direction' }, - { alias: 'ltr', label: 'Left to right', icon: 'ltr', category: 'direction' }, + // // Direction Group + // { alias: 'rtl', label: 'Right to left', icon: 'rtl', category: 'direction' }, + // { alias: 'ltr', label: 'Left to right', icon: 'ltr', category: 'direction' }, - // Text Transformation Group - { alias: 'subscript', label: 'Subscript', icon: 'subscript', category: 'text-transformation' }, - { alias: 'superscript', label: 'Superscript', icon: 'superscript', category: 'text-transformation' }, + // // Text Transformation Group + // { alias: 'subscript', label: 'Subscript', icon: 'subscript', category: 'text-transformation' }, + // { alias: 'superscript', label: 'Superscript', icon: 'superscript', category: 'text-transformation' }, - // Styling and Font Group - { alias: 'styles', label: 'Style select', icon: 'permanent-pen', category: 'styling' }, - { alias: 'fontname', label: 'Font select', icon: 'text-color', category: 'styling' }, - { alias: 'fontsize', label: 'Font size', icon: 'text-color', category: 'styling' }, + // // Styling and Font Group + // { alias: 'styles', label: 'Style select', icon: 'permanent-pen', category: 'styling' }, + // { alias: 'fontname', label: 'Font select', icon: 'text-color', category: 'styling' }, + // { alias: 'fontsize', label: 'Font size', icon: 'text-color', category: 'styling' }, - // Block Element Group - { alias: 'blockquote', label: 'Blockquote', icon: 'quote', category: 'block-elements' }, - { alias: 'formatblock', label: 'Format block', icon: 'format', category: 'block-elements' }, - ], - }, - ], + // // Block Element Group + // { alias: 'blockquote', label: 'Blockquote', icon: 'quote', category: 'block-elements' }, + // { alias: 'formatblock', label: 'Format block', icon: 'format', category: 'block-elements' }, + // ], + // }, + // ], }, { alias: 'maxWidth', From 225f3c880a8bd6b0b76b923648f78045402de532 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 13:58:47 +0200 Subject: [PATCH 130/241] feat: add support for providing preconfigured altText and caption to media-caption modal --- .../media-caption-alt-text-modal.element.ts | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts b/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts index adb87d2e7d..40581dd0a5 100644 --- a/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts +++ b/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts @@ -27,7 +27,7 @@ export class UmbMediaCaptionAltTextModalElement extends UmbModalBaseElement< const { data } = await this.#mediaDetailRepository.requestByUnique(this.#mediaUnique); if (!data) return; - this.value = { altText: data.variants[0].name, caption: undefined, url: data.urls[0]?.url ?? '' }; + this.value = { ...this.value, altText: this.value.altText ?? data.variants[0].name, url: data.urls[0]?.url ?? '' }; } override render() { @@ -46,11 +46,14 @@ export class UmbMediaCaptionAltTextModalElement extends UmbModalBaseElement< (this.value = { ...this.value, caption: e.target.value as string })}> - ${this.value?.altText - ${this.value?.caption ?? ''} +
    + ${this.value?.altText +
    ${this.value?.caption ?? ''}
    +
    @@ -64,7 +67,7 @@ export class UmbMediaCaptionAltTextModalElement extends UmbModalBaseElement< `; } - static override styles = [ + static override readonly styles = [ css` uui-input { margin-bottom: var(--uui-size-layout-1); @@ -74,6 +77,17 @@ export class UmbMediaCaptionAltTextModalElement extends UmbModalBaseElement< display: flex; flex-direction: column; } + + #mainobject { + display: flex; + flex-direction: column; + max-width: 100%; + + img { + max-width: 100%; + height: auto; + } + } `, ]; } From 658762354d4058616e995536af83ee32b47d5d42 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 13:59:33 +0200 Subject: [PATCH 131/241] feat: adds support for figure and figcaption --- .../extensions/tiptap-figcaption.extension.ts | 40 ++++++++++++++ .../extensions/tiptap-figure.extension.ts | 52 +++++++++++++++++++ src/external/tiptap/index.ts | 2 + .../extensions/core/figure.extension.ts | 6 +++ .../rte/tiptap/extensions/core/manifests.ts | 12 +++++ 5 files changed, 112 insertions(+) create mode 100644 src/external/tiptap/extensions/tiptap-figcaption.extension.ts create mode 100644 src/external/tiptap/extensions/tiptap-figure.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/figure.extension.ts diff --git a/src/external/tiptap/extensions/tiptap-figcaption.extension.ts b/src/external/tiptap/extensions/tiptap-figcaption.extension.ts new file mode 100644 index 0000000000..23b188b7bf --- /dev/null +++ b/src/external/tiptap/extensions/tiptap-figcaption.extension.ts @@ -0,0 +1,40 @@ +import { Node } from '@tiptap/core'; + +export interface FigcaptionOptions { + /** + * HTML attributes to add to the image element. + * @default {} + * @example { class: 'foo' } + */ + HTMLAttributes: Record; +} + +export const Figcaption = Node.create({ + name: 'figcaption', + + addOptions() { + return { + HTMLAttributes: {}, + }; + }, + + group: 'block', + + content: 'inline*', + + selectable: false, + + draggable: false, + + parseHTML() { + return [ + { + tag: 'figcaption', + }, + ]; + }, + + renderHTML({ HTMLAttributes }) { + return [this.name, HTMLAttributes, 0]; + }, +}); diff --git a/src/external/tiptap/extensions/tiptap-figure.extension.ts b/src/external/tiptap/extensions/tiptap-figure.extension.ts new file mode 100644 index 0000000000..0cae9f7b47 --- /dev/null +++ b/src/external/tiptap/extensions/tiptap-figure.extension.ts @@ -0,0 +1,52 @@ +import { mergeAttributes, Node } from '@tiptap/core'; + +export interface FigureOptions { + /** + * HTML attributes to add to the image element. + * @default {} + * @example { class: 'foo' } + */ + HTMLAttributes: Record; +} + +export const Figure = Node.create({ + name: 'figure', + group: 'block', + content: 'block+', + draggable: true, + selectable: true, + isolating: true, + atom: true, + + addAttributes() { + return { + figcaption: { + default: '', + }, + }; + }, + + addOptions() { + return { + HTMLAttributes: {}, + }; + }, + + parseHTML() { + return [ + { + tag: 'figure', + getAttrs: (dom) => { + const figcaption = dom.querySelector('figcaption'); + return { + figcaption: figcaption?.textContent || '', + }; + }, + }, + ]; + }, + + renderHTML({ HTMLAttributes }) { + return [this.name, mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]; + }, +}); diff --git a/src/external/tiptap/index.ts b/src/external/tiptap/index.ts index 5c6fabf333..a2c146f446 100644 --- a/src/external/tiptap/index.ts +++ b/src/external/tiptap/index.ts @@ -24,4 +24,6 @@ export { TextAlign } from '@tiptap/extension-text-align'; export { Underline } from '@tiptap/extension-underline'; export { Image } from '@tiptap/extension-image'; // CUSTOM EXTENSIONS +export * from './extensions/tiptap-figcaption.extension.js'; +export * from './extensions/tiptap-figure.extension.js'; export * from './extensions/tiptap-umb-image.extension.js'; diff --git a/src/packages/rte/tiptap/extensions/core/figure.extension.ts b/src/packages/rte/tiptap/extensions/core/figure.extension.ts new file mode 100644 index 0000000000..1da6bdec35 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/figure.extension.ts @@ -0,0 +1,6 @@ +import { UmbTiptapExtensionApiBase } from '../types.js'; +import { Figure, Figcaption } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapFigureExtensionApi extends UmbTiptapExtensionApiBase { + getTiptapExtensions = () => [Figcaption, Figure]; +} diff --git a/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/packages/rte/tiptap/extensions/core/manifests.ts index 9ef58cd081..b4c0961f2b 100644 --- a/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/packages/rte/tiptap/extensions/core/manifests.ts @@ -53,6 +53,18 @@ export const manifests: Array import('./figure.extension.js'), + weight: 955, + meta: { + alias: 'figure', + icon: 'icon-frame', + label: 'Figure', + }, + }, { type: 'tiptapExtension', kind: 'button', From 660cc949274225723caa4d2a4315e13c81d3a4ed Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:00:03 +0200 Subject: [PATCH 132/241] chore: rename umbImage to image --- .../extensions/tiptap-umb-image.extension.ts | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/external/tiptap/extensions/tiptap-umb-image.extension.ts b/src/external/tiptap/extensions/tiptap-umb-image.extension.ts index fb3d645b4e..81807bc9ad 100644 --- a/src/external/tiptap/extensions/tiptap-umb-image.extension.ts +++ b/src/external/tiptap/extensions/tiptap-umb-image.extension.ts @@ -1,7 +1,19 @@ import Image from '@tiptap/extension-image'; +export interface UmbImageAttributes { + src: string; + alt?: string; + title?: string; + width?: string; + height?: string; + loading?: string; + srcset?: string; + sizes?: string; + 'data-tmpimg'?: string; + 'data-udi'?: string; +} + export const UmbImage = Image.extend({ - name: 'umbImage', addAttributes() { return { ...this.parent?.(), @@ -22,7 +34,6 @@ export const UmbImage = Image.extend({ }, 'data-tmpimg': { default: null }, 'data-udi': { default: null }, - 'data-caption': { default: null }, }; }, }); @@ -38,19 +49,7 @@ declare module '@tiptap/core' { * .commands * .setImage({ src: 'https://tiptap.dev/logo.png', alt: 'tiptap', title: 'tiptap logo' }) */ - setImage: (options: { - src: string; - alt?: string; - title?: string; - width?: string; - height?: string; - loading?: string; - srcset?: string; - sizes?: string; - 'data-tmpimg'?: string; - 'data-udi'?: string; - 'data-caption'?: string; - }) => ReturnType; + setImage: (options: UmbImageAttributes) => ReturnType; }; } } From b868d25d85715149872661c57e89019f99f55edc Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:01:26 +0200 Subject: [PATCH 133/241] feat: adds the mediapicker to tiptap --- .../extensions/umb/mediapicker.extension.ts | 160 +++++++++++++----- 1 file changed, 120 insertions(+), 40 deletions(-) diff --git a/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts b/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts index 6947d772b8..bafba48bd4 100644 --- a/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts @@ -1,63 +1,143 @@ -import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import { mergeAttributes, Node } from '@umbraco-cms/backoffice/external/tiptap'; -import { UMB_MEDIA_PICKER_MODAL } from '@umbraco-cms/backoffice/media'; +import { UmbTiptapToolbarElementApiBase, type UmbTiptapExtensionArgs } from '../types.js'; +import { + UMB_MEDIA_CAPTION_ALT_TEXT_MODAL, + UMB_MEDIA_PICKER_MODAL, + type UmbMediaCaptionAltTextModalValue, +} from '@umbraco-cms/backoffice/media'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { getGuidFromUdi, imageSize } from '@umbraco-cms/backoffice/utils'; +import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; export default class UmbTiptapMediaPickerExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions() { - return [ - Node.create({ - name: 'umb-media', - priority: 1000, - group: 'block', - marks: '', - draggable: true, - addNodeView() { - return () => { - //console.log('umb-media.addNodeView'); - const dom = document.createElement('span'); - dom.innerText = '🖼️'; - return { dom }; - }; - }, - parseHTML() { - //console.log('umb-media.parseHTML'); - return [{ tag: 'umb-media' }]; - }, - renderHTML({ HTMLAttributes }) { - //console.log('umb-media.renderHTML'); - return ['umb-media', mergeAttributes(HTMLAttributes)]; - }, - }), - ]; + #modalManager?: typeof UMB_MODAL_MANAGER_CONTEXT.TYPE; + #configuration?: UmbPropertyEditorConfigCollection; + + /** + * @returns {number} The maximum width of uploaded images + */ + get maxWidth(): number { + const maxImageSize = parseInt(this.#configuration?.getValueByAlias('maxImageSize') ?? '', 10); + return isNaN(maxImageSize) ? 500 : maxImageSize; + } + + constructor(host: UmbControllerHost) { + super(host); + + this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (instance) => { + this.#modalManager = instance; + }); + } + + getTiptapExtensions(args: UmbTiptapExtensionArgs) { + this.#configuration = args?.configuration; + return []; } override isActive(editor?: Editor) { - return editor?.isActive('umb-media') === true || editor?.isActive('image') === true; + return editor?.isActive('image') === true || editor?.isActive('figure') === true; } - override async execute(editor?: Editor) { - console.log('umb-media.execute', editor); + override async execute(editor: Editor) { + const currentTarget = editor.getAttributes('image'); + const figure = editor.getAttributes('figure'); + + let currentMediaUdi: string | undefined = undefined; + if (currentTarget?.['data-udi']) { + currentMediaUdi = getGuidFromUdi(currentTarget['data-udi']); + } + + let currentAltText: string | undefined = undefined; + if (currentTarget?.alt) { + currentAltText = currentTarget.alt; + } + + let currentCaption: string | undefined = undefined; + if (figure?.figcaption) { + currentCaption = figure.figcaption; + } - const selection = await this.#openMediaPicker(); + const selection = await this.#openMediaPicker(currentMediaUdi); if (!selection || !selection.length) return; - editor?.chain().focus().insertContent(`${selection}`).run(); + const mediaGuid = selection[0]; + const media = await this.#showMediaCaptionAltText(mediaGuid, currentAltText, currentCaption); + if (!media) return; + + this.#insertInEditor(editor, mediaGuid, media); } - async #openMediaPicker() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalHandler = modalManager?.open(this, UMB_MEDIA_PICKER_MODAL, { - data: { multiple: false }, - value: { selection: [] }, + async #openMediaPicker(currentMediaUdi?: string) { + const modalHandler = this.#modalManager?.open(this, UMB_MEDIA_PICKER_MODAL, { + data: { + multiple: false, + //startNodeIsVirtual, + }, + value: { + selection: currentMediaUdi ? [currentMediaUdi] : [], + }, }); if (!modalHandler) return; const { selection } = await modalHandler.onSubmit().catch(() => ({ selection: undefined })); - //console.log('umb-media.selection', selection); return selection; } + + async #showMediaCaptionAltText(mediaUnique: string, altText?: string, caption?: string) { + const modalHandler = this.#modalManager?.open(this, UMB_MEDIA_CAPTION_ALT_TEXT_MODAL, { + data: { mediaUnique }, + value: { + url: '', + altText, + caption, + }, + }); + const mediaData = await modalHandler?.onSubmit().catch(() => null); + return mediaData; + } + + async #insertInEditor(editor: Editor, mediaUnique: string, media: UmbMediaCaptionAltTextModalValue) { + if (!media?.url) return; + + const { width, height } = await imageSize(media.url, { maxWidth: this.maxWidth }); + + const img = { + alt: media.altText, + src: media.url ? media.url : 'nothing.jpg', + 'data-udi': `umb://media/${mediaUnique.replace(/-/g, '')}`, + width: width.toString(), + height: height.toString(), + }; + + if (media.caption) { + return editor.commands.insertContent({ + type: 'figure', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'image', + attrs: img, + }, + ], + }, + { + type: 'figcaption', + content: [ + { + type: 'text', + text: media.caption, + }, + ], + }, + ], + }); + } + + return editor.commands.setImage(img); + } } From 0b5aaf1b693097d00973a7008811c060ed245b8d Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:02:18 +0200 Subject: [PATCH 134/241] feat: style figures in the editor to prevent clickthrough and to highlight selected figures and images --- .../input-tiptap/input-tiptap.element.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 85e18d8a67..b2eb2ecd14 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -180,6 +180,25 @@ export class UmbInputTiptapElement extends UmbFormControlMixin p, + img { + pointer-events: none; + margin: 0; + padding: 0; + } + + &.ProseMirror-selectednode { + outline: 3px solid #b4d7ff; + } + } + + img { + &.ProseMirror-selectednode { + outline: 3px solid #b4d7ff; + } + } } `, ]; From 8af82976c19b2a5cd4708c4b467d51b6619debda Mon Sep 17 00:00:00 2001 From: leekelleher Date: Thu, 26 Sep 2024 10:23:29 +0100 Subject: [PATCH 135/241] Tiptap extension: Table --- package-lock.json | 53 ++++++++++++++++ package.json | 4 ++ src/external/tiptap/index.ts | 12 +++- .../core/icon-registry/icon-dictionary.json | 4 ++ src/packages/core/icon-registry/icons.ts | 4 ++ .../core/icon-registry/icons/icon-table.ts | 17 ++++++ .../input-tiptap/input-tiptap.element.ts | 61 +++++++++++++++++++ .../rte/tiptap/extensions/core/manifests.ts | 13 ++++ .../tiptap/extensions/core/table.extension.ts | 11 ++++ 9 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 src/packages/core/icon-registry/icons/icon-table.ts create mode 100644 src/packages/rte/tiptap/extensions/core/table.extension.ts diff --git a/package-lock.json b/package-lock.json index 24c064470c..d1233645ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,10 @@ "@tiptap/extension-ordered-list": "^2.7.0", "@tiptap/extension-paragraph": "^2.7.0", "@tiptap/extension-strike": "^2.7.0", + "@tiptap/extension-table": "^2.7.3", + "@tiptap/extension-table-cell": "^2.7.3", + "@tiptap/extension-table-header": "^2.7.3", + "@tiptap/extension-table-row": "^2.7.3", "@tiptap/extension-text": "^2.7.0", "@tiptap/extension-text-align": "^2.6.6", "@tiptap/extension-underline": "^2.6.6", @@ -6731,6 +6735,55 @@ "@tiptap/core": "^2.7.0-pre.0" } }, + "node_modules/@tiptap/extension-table": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.7.3.tgz", + "integrity": "sha512-zv1SGgVywTY3vs+9EIMdYS7jZMovlfsraZ3Qdz1YkqN3dNZBUukXrfpZaJqzVwUvRehCVvjA+HG7zH12RU/XYQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-table-cell": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-2.7.3.tgz", + "integrity": "sha512-C6f2dAcatk/XROZ2Q1owv4DBrTyfVzfsK1Jh7rk3mkpEa8oh/lPKR8thYjmaLC/BlPYjtVuIbMIqp9lz6U/Ufw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-table-header": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-2.7.3.tgz", + "integrity": "sha512-eL1FVn+GBf0dRYmsE88QeJa3azwVKhyYDAFTmoGIwilHsjbNzb4ptUGi+ko2XpxLHvY+XfGLe3+UEZbQ3FDOIA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-table-row": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-2.7.3.tgz", + "integrity": "sha512-gB6gXYVCGWn6IDb/oV3ds1LI0yLLIwymcvcu1MWnT9p8qClZPaId/J6/+mQbSGCEc8G1SzYYUhnu2dsaVIsFsw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, "node_modules/@tiptap/extension-text": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.7.0.tgz", diff --git a/package.json b/package.json index d058a21df1..d50b98524f 100644 --- a/package.json +++ b/package.json @@ -212,6 +212,10 @@ "@tiptap/extension-ordered-list": "^2.7.0", "@tiptap/extension-paragraph": "^2.7.0", "@tiptap/extension-strike": "^2.7.0", + "@tiptap/extension-table": "^2.7.3", + "@tiptap/extension-table-cell": "^2.7.3", + "@tiptap/extension-table-header": "^2.7.3", + "@tiptap/extension-table-row": "^2.7.3", "@tiptap/extension-text": "^2.7.0", "@tiptap/extension-text-align": "^2.6.6", "@tiptap/extension-underline": "^2.6.6", diff --git a/src/external/tiptap/index.ts b/src/external/tiptap/index.ts index a2c146f446..e186f0ccf3 100644 --- a/src/external/tiptap/index.ts +++ b/src/external/tiptap/index.ts @@ -1,4 +1,4 @@ -// REQUIRED EXTENSIONS START +// REQUIRED EXTENSIONS export * from '@tiptap/core'; export { Document } from '@tiptap/extension-document'; export { Dropcursor } from '@tiptap/extension-dropcursor'; @@ -7,7 +7,8 @@ export { HardBreak } from '@tiptap/extension-hard-break'; export { History } from '@tiptap/extension-history'; export { Paragraph } from '@tiptap/extension-paragraph'; export { Text } from '@tiptap/extension-text'; -// REQUIRED EXTENSIONS END + +// OPTIONAL EXTENSIONS export { Blockquote } from '@tiptap/extension-blockquote'; export { Bold } from '@tiptap/extension-bold'; export { BulletList } from '@tiptap/extension-bullet-list'; @@ -15,14 +16,19 @@ export { Code } from '@tiptap/extension-code'; export { CodeBlock } from '@tiptap/extension-code-block'; export { Heading } from '@tiptap/extension-heading'; export { HorizontalRule } from '@tiptap/extension-horizontal-rule'; +export { Image } from '@tiptap/extension-image'; export { Italic } from '@tiptap/extension-italic'; export { Link } from '@tiptap/extension-link'; export { ListItem } from '@tiptap/extension-list-item'; export { OrderedList } from '@tiptap/extension-ordered-list'; export { Strike } from '@tiptap/extension-strike'; +export { Table } from '@tiptap/extension-table'; +export { TableCell } from '@tiptap/extension-table-cell'; +export { TableHeader } from '@tiptap/extension-table-header'; +export { TableRow } from '@tiptap/extension-table-row'; export { TextAlign } from '@tiptap/extension-text-align'; export { Underline } from '@tiptap/extension-underline'; -export { Image } from '@tiptap/extension-image'; + // CUSTOM EXTENSIONS export * from './extensions/tiptap-figcaption.extension.js'; export * from './extensions/tiptap-figure.extension.js'; diff --git a/src/packages/core/icon-registry/icon-dictionary.json b/src/packages/core/icon-registry/icon-dictionary.json index 4d592bfb10..9c53e18410 100644 --- a/src/packages/core/icon-registry/icon-dictionary.json +++ b/src/packages/core/icon-registry/icon-dictionary.json @@ -1982,6 +1982,10 @@ "name": "icon-tab-key", "file": "arrow-right-to-line.svg" }, + { + "name": "icon-table", + "file": "table.svg" + }, { "name": "icon-tag", "file": "tag.svg" diff --git a/src/packages/core/icon-registry/icons.ts b/src/packages/core/icon-registry/icons.ts index 3139743a15..1f55c16a7f 100644 --- a/src/packages/core/icon-registry/icons.ts +++ b/src/packages/core/icon-registry/icons.ts @@ -1867,6 +1867,10 @@ name: "icon-tab-key", path: () => import("./icons/icon-tab-key.js"), },{ +name: "icon-table", + +path: () => import("./icons/icon-table.js"), +},{ name: "icon-tag", path: () => import("./icons/icon-tag.js"), diff --git a/src/packages/core/icon-registry/icons/icon-table.ts b/src/packages/core/icon-registry/icons/icon-table.ts new file mode 100644 index 0000000000..283a8a901c --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-table.ts @@ -0,0 +1,17 @@ +export default ` + + + + + + +`; \ No newline at end of file diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 85e18d8a67..d6e9872427 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -180,6 +180,67 @@ export class UmbInputTiptapElement extends UmbFormControlMixin * { + margin-bottom: 0; + } + } + + th { + background-color: var(--uui-color-background); + font-weight: bold; + text-align: left; + } + + .selectedCell:after { + background: var(--uui-color-surface-emphasis); + content: ''; + left: 0; + right: 0; + top: 0; + bottom: 0; + pointer-events: none; + position: absolute; + z-index: 2; + } + + .column-resize-handle { + background-color: var(--uui-color-default); + bottom: -2px; + pointer-events: none; + position: absolute; + right: -2px; + top: 0; + width: 3px; + } + } + } + + .resize-cursor { + cursor: ew-resize; + cursor: col-resize; + } } `, ]; diff --git a/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/packages/rte/tiptap/extensions/core/manifests.ts index b4c0961f2b..309598a14f 100644 --- a/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/packages/rte/tiptap/extensions/core/manifests.ts @@ -165,6 +165,19 @@ export const manifests: Array import('./table.extension.js'), + weight: 909, + meta: { + alias: 'table', + icon: 'icon-table', + label: 'Table', + }, + }, { type: 'tiptapExtension', kind: 'button', diff --git a/src/packages/rte/tiptap/extensions/core/table.extension.ts b/src/packages/rte/tiptap/extensions/core/table.extension.ts new file mode 100644 index 0000000000..ae2d71fa73 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/table.extension.ts @@ -0,0 +1,11 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import { Table, TableHeader, TableRow, TableCell } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapTableExtensionApi extends UmbTiptapToolbarElementApiBase { + getTiptapExtensions = () => [Table.configure({ resizable: true }), TableHeader, TableRow, TableCell]; + + override execute(editor?: Editor) { + editor?.commands.insertTable({ rows: 3, cols: 3, withHeaderRow: true }); + } +} From c2e9b6f86e22e0198e6f7afe5ba7e2901dd0cdee Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:22:39 +0200 Subject: [PATCH 136/241] fix: send back width and height from the embedded modal --- .../embedded-media/modal/embedded-media-modal.element.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/packages/embedded-media/modal/embedded-media-modal.element.ts b/src/packages/embedded-media/modal/embedded-media-modal.element.ts index 62f8468c17..e91c24cbcd 100644 --- a/src/packages/embedded-media/modal/embedded-media-modal.element.ts +++ b/src/packages/embedded-media/modal/embedded-media-modal.element.ts @@ -44,8 +44,8 @@ export class UmbEmbeddedMediaModalElement extends UmbModalBaseElement< const { data } = await this.#oEmbedRepository.requestOEmbed({ url: this._url, - maxWidth: this._width, - maxHeight: this._height, + maxWidth: this._width > 0 ? this._width : undefined, + maxHeight: this._height > 0 ? this._height : undefined, }); if (data) { @@ -64,11 +64,13 @@ export class UmbEmbeddedMediaModalElement extends UmbModalBaseElement< #onWidthChange(e: UUIInputEvent) { this._width = parseInt(e.target.value as string, 10); + this.value = { ...this.value, width: this._width }; this.#getPreview(); } #onHeightChange(e: UUIInputEvent) { this._height = parseInt(e.target.value as string, 10); + this.value = { ...this.value, height: this._height }; this.#getPreview(); } From 8652a96688ff0c99254e41bb59f80484d30dc667 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:22:59 +0200 Subject: [PATCH 137/241] feat: allow storing width, height, and constrain as data attributes --- .../tiptap-umb-embedded-media.extension.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts b/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts index bcef5ee6eb..3a13add2cb 100644 --- a/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts +++ b/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts @@ -38,7 +38,13 @@ export const umbEmbeddedMedia = Node.create({ setEmbeddedMedia: (options) => ({ commands }) => { - const attrs = { markup: options.markup, 'data-embed-url': options.url }; + const attrs = { + markup: options.markup, + 'data-embed-url': options.url, + 'data-embed-width': options.width, + 'data-embed-height': options.height, + 'data-embed-constrain': options.constrain, + }; return commands.insertContent({ type: this.name, attrs }); }, }; @@ -48,7 +54,13 @@ export const umbEmbeddedMedia = Node.create({ declare module '@tiptap/core' { interface Commands { umbEmbeddedMedia: { - setEmbeddedMedia: (options: { markup: string; url: string }) => ReturnType; + setEmbeddedMedia: (options: { + markup: string; + url: string; + width?: string; + height?: string; + constrain?: boolean; + }) => ReturnType; }; } } From 422458fa154e21398345999b6e281007946817fc Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:26:30 +0200 Subject: [PATCH 138/241] feat: sets z-index on the fixed menu to make it overlay other elements inside the editor --- .../tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 0dadca09f2..457eb7ad8c 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -52,6 +52,7 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { right: 0px; padding: var(--uui-size-space-3); align-items: center; + z-index: 100; } :host([readonly]) { From 8b948e080af789ff714bc38c3a51882708f2687e Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:45:10 +0200 Subject: [PATCH 139/241] load extensions based on toolbar config --- .../components/input-tiptap/input-tiptap.element.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 8370dc7cdb..0a625ac6cd 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -63,13 +63,18 @@ export class UmbInputTiptapElement extends UmbFormControlMixin = []; protected override async firstUpdated() { + // TODO: we need some types here + this._toolbarConfig = (this.configuration?.getValueByAlias('toolbar') as any) ?? []; await Promise.all([await this.#loadExtensions(), await this.#loadEditor()]); } async #loadExtensions() { await new Promise((resolve) => { this.observe(umbExtensionsRegistry.byType('tiptapExtension'), async (manifests) => { - this._extensions = []; + manifests = manifests.filter((ext) => { + return !!this._toolbarConfig.find((x) => x.alias === ext.alias); + }); + for (const manifest of manifests) { if (manifest.api) { const extension = await loadManifestApi(manifest.api); @@ -89,7 +94,6 @@ export class UmbInputTiptapElement extends UmbFormControlMixin('maxWidth'); const maxHeight = this.configuration?.getValueByAlias('maxHeight'); - this._toolbarConfig = this.configuration?.getValueByAlias('toolbar') as any; this.setAttribute('style', `max-width: ${maxWidth}px;`); element.setAttribute('style', `max-height: ${maxHeight}px;`); From 9d0b811741829f84dd528605024f1c0b7e91ef21 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:49:30 +0200 Subject: [PATCH 140/241] build(deps): update all tiptap packages --- package-lock.json | 306 +++++++++++++++++++++++----------------------- package.json | 56 ++++----- 2 files changed, 181 insertions(+), 181 deletions(-) diff --git a/package-lock.json b/package-lock.json index d1233645ab..7f836c586a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,34 +12,34 @@ "./src/packages/*" ], "dependencies": { - "@tiptap/core": "^2.6.6", - "@tiptap/extension-blockquote": "^2.7.0", - "@tiptap/extension-bold": "^2.7.0", - "@tiptap/extension-bullet-list": "^2.7.0", - "@tiptap/extension-code": "^2.7.0", - "@tiptap/extension-code-block": "^2.7.0", - "@tiptap/extension-document": "^2.7.0", - "@tiptap/extension-dropcursor": "^2.7.0", - "@tiptap/extension-gapcursor": "^2.7.0", - "@tiptap/extension-hard-break": "^2.7.0", - "@tiptap/extension-heading": "^2.7.0", - "@tiptap/extension-history": "^2.7.0", - "@tiptap/extension-horizontal-rule": "^2.7.0", - "@tiptap/extension-image": "^2.7.0", - "@tiptap/extension-italic": "^2.7.0", - "@tiptap/extension-link": "^2.6.6", - "@tiptap/extension-list-item": "^2.7.0", - "@tiptap/extension-ordered-list": "^2.7.0", - "@tiptap/extension-paragraph": "^2.7.0", - "@tiptap/extension-strike": "^2.7.0", - "@tiptap/extension-table": "^2.7.3", - "@tiptap/extension-table-cell": "^2.7.3", - "@tiptap/extension-table-header": "^2.7.3", - "@tiptap/extension-table-row": "^2.7.3", - "@tiptap/extension-text": "^2.7.0", - "@tiptap/extension-text-align": "^2.6.6", - "@tiptap/extension-underline": "^2.6.6", - "@tiptap/pm": "^2.6.6", + "@tiptap/core": "^2.7.4", + "@tiptap/extension-blockquote": "^2.7.4", + "@tiptap/extension-bold": "^2.7.4", + "@tiptap/extension-bullet-list": "^2.7.4", + "@tiptap/extension-code": "^2.7.4", + "@tiptap/extension-code-block": "^2.7.4", + "@tiptap/extension-document": "^2.7.4", + "@tiptap/extension-dropcursor": "^2.7.4", + "@tiptap/extension-gapcursor": "^2.7.4", + "@tiptap/extension-hard-break": "^2.7.4", + "@tiptap/extension-heading": "^2.7.4", + "@tiptap/extension-history": "^2.7.4", + "@tiptap/extension-horizontal-rule": "^2.7.4", + "@tiptap/extension-image": "^2.7.4", + "@tiptap/extension-italic": "^2.7.4", + "@tiptap/extension-link": "^2.7.4", + "@tiptap/extension-list-item": "^2.7.4", + "@tiptap/extension-ordered-list": "^2.7.4", + "@tiptap/extension-paragraph": "^2.7.4", + "@tiptap/extension-strike": "^2.7.4", + "@tiptap/extension-table": "^2.7.4", + "@tiptap/extension-table-cell": "^2.7.4", + "@tiptap/extension-table-header": "^2.7.4", + "@tiptap/extension-table-row": "^2.7.4", + "@tiptap/extension-text": "^2.7.4", + "@tiptap/extension-text-align": "^2.7.4", + "@tiptap/extension-underline": "^2.7.4", + "@tiptap/pm": "^2.7.4", "@types/diff": "^5.2.1", "@types/dompurify": "^3.0.5", "@types/uuid": "^10.0.0", @@ -4641,9 +4641,9 @@ } }, "node_modules/@remirror/core-constants": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-2.0.2.tgz", - "integrity": "sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz", + "integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==" }, "node_modules/@rollup/plugin-commonjs": { "version": "26.0.1", @@ -6487,194 +6487,194 @@ } }, "node_modules/@tiptap/core": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.6.6.tgz", - "integrity": "sha512-VO5qTsjt6rwworkuo0s5AqYMfDA0ZwiTiH6FHKFSu2G/6sS7HKcc/LjPq+5Legzps4QYdBDl3W28wGsGuS1GdQ==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.7.4.tgz", + "integrity": "sha512-1VTQdNQChgxdVC8+b8QEW6cUxPSD9EDTzg9YRSLWtTtUDQ09sRSVs7eHIn1LcRHVs6PwcAsNgKE4FSjBw0sRlg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/pm": "^2.6.6" + "@tiptap/pm": "^2.7.0" } }, "node_modules/@tiptap/extension-blockquote": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.7.0.tgz", - "integrity": "sha512-cAJsQ2SrwiFMazZyG+CgG3Ljdc1DjFTGQyZxV/7smfO/pS/x8OgOStL1gJFBTbDbiR8sZVuJEV8m3h+95s3Rkw==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.7.4.tgz", + "integrity": "sha512-N6rhiwVRpsxRz4Qt8cvKgpqjBxdi8GTbU/v2MV/BTONWb7Ch9ajv9HO6koEDdOeb77JVhpWztzYysTjJo2KTyQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-bold": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.7.0.tgz", - "integrity": "sha512-/BCdlVHmYm1HmpxjUZ5Ba6+H23mmG3aDT2lg4x/DdPVEEudQkUEfHCiIfu+hFiBWqJBt43QmiR3Tt5hr/wfDvw==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.7.4.tgz", + "integrity": "sha512-Yq2ErekgpsOLCGYfQc1H3tUdmecKHDBWTPesVtqg0ct/3ZbKskhFoR6bPQWZH/ZRXQb1ARA+aMp/iqM/hqm+KQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-bullet-list": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.7.0.tgz", - "integrity": "sha512-wQy0EXM/Do5bjOMWUBAEBJiOAhAm038skPYCL7Glu5vV9IOr9aZ0uEBl324U8y/AB1HsFqAI1F+xqD+6XwzorQ==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.7.4.tgz", + "integrity": "sha512-uO08vui6uEgLEgLIYJSLrUb2An3u0If8XRW0Z0kB13zpwQ9pq0S1JOc0KwPTDPeIrgLQ7OOH87/bM9rGUFC3AQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-code": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.7.0.tgz", - "integrity": "sha512-QQpHDv5r9proQp0sMK8hR+sY+jCbXKdEHkle23ZESXP/k75eBJNIkkHUCOAJpYtkZdGjlQ1AjW9H/L513IE8cw==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.7.4.tgz", + "integrity": "sha512-GB7gR8tV1fz+70wcSN+hLVm1qET/YmkxIaOfczHEOLLH7Td0C3kyQ5Q+eQ8KN0Ds7NBHFXn3zn051Q8gk9+5tw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-code-block": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.7.0.tgz", - "integrity": "sha512-SDU+ZITxZfD3fsValCPnU+VwMrEmL0SedvuIqRTIWBd6qxwJD4suRcALYYFAmgmGXS794kjVLb8ONAw/69Svqw==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.7.4.tgz", + "integrity": "sha512-jRKVAEdy3G0SMphWXCTk9SnMuTmJE6blXglU66H89j9R+hG+G0dHfOWhlubhUy6nI2BLy8jJ/isnOzg97iZuQw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0", - "@tiptap/pm": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" } }, "node_modules/@tiptap/extension-document": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.7.0.tgz", - "integrity": "sha512-F00nBhp+IM/vJVr0G7iMcaanVGhYfTaF1kafxE6PYnKV4d4BDJeLq5OvPzJHaP3P1frqIEci7trUW1MqQANSjQ==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.7.4.tgz", + "integrity": "sha512-Vsq9e/uW7k/5l1K9bCmuccBSrHhK3i0fbfnTp33G1byTCizheUo3UWFl8MSDammlhRkW/soIZFGdflsj5AJWog==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-dropcursor": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.7.0.tgz", - "integrity": "sha512-sbeio2DlPdm0XRyqpJ9qv0Eg3MhWucqBH4olrasPtLxnxY9S2NX7ztKhk/dkXnG45ioq2HlgbLp/ZxbF8cYjfA==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.7.4.tgz", + "integrity": "sha512-hhE0RTluEEFxfqh8/jpmQRgy5AipTcd+WMK5cBw2zCa9If/qhY0EvysydEPwDU7yDEa13NDqV63x5oN9GKv2pg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0", - "@tiptap/pm": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" } }, "node_modules/@tiptap/extension-gapcursor": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.7.0.tgz", - "integrity": "sha512-sVf5wGXkhQIyU+qhjI79Ms6OkEjb6/1VLmTWVvE/5l1+TT4r7/PhcJdEX0XaePNabH3ArZNOgBbhkjw7HJvqyw==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.7.4.tgz", + "integrity": "sha512-1HTaCR6kcw5PvUJWEGKQ/Eh2HPXUmN6k1LK0rgJC4CxqiFxNNnPKGED9LcYheJbyMYk0Fz3rtaulxd3ipdIOsQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0", - "@tiptap/pm": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" } }, "node_modules/@tiptap/extension-hard-break": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.7.0.tgz", - "integrity": "sha512-IHzba0lWf+GU+9GToWi223aY8j/CSrg1mdNb2DvljP224a5MiE3aReT6E3ZfaxONhkrq93Q6PRlC9PUwLdyJdQ==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.7.4.tgz", + "integrity": "sha512-ut81vNPQyDYi8LhOzPfFZGnPToYGQbBR6bvFE0e8WY9sRfvUZHr/GvkMjPuWuA8M5sBMqS5cLNyqPrI8h4R7Jg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-heading": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.7.0.tgz", - "integrity": "sha512-J5jERidjtntZh4Du7n9PbMoD3ibiWjQH8Kc9AINOzK/bLBGIsDN15reOVGyto+ZYhTtU2pe7fWAjJYp+zCIcKQ==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.7.4.tgz", + "integrity": "sha512-ZLFHhFvmDD6YKPf4wftZd4wtT510yHjzG90A14wyKCpm0Bq9wOYzx4Q+owvlp5vMwenqHuq3KGz4Sf3w6N5gkw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-history": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.7.0.tgz", - "integrity": "sha512-33GtG+SIHi6c8briilU2OZ7pt7W8XoNscXokJsXqLlGZDC7mlLs6N9OMQ7qNbcycz7uxRqi5k7jWpxwxRTQiKQ==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.7.4.tgz", + "integrity": "sha512-xRgGXNrtjDGVOeLeZzGqw4/OtwIoloLU3QLn/qaOggVS7jr1HVTqMHw4nZVcUJfnB/UQ90yl53hBKZ8z3AxcCA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0", - "@tiptap/pm": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" } }, "node_modules/@tiptap/extension-horizontal-rule": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.7.0.tgz", - "integrity": "sha512-15N8+OxJa7yGN8PX5odBTCAjqwsDoQOBe/WWWy0viGdWSl0uBcThfm99YV+C72Qtv+PM4+2gkXbl6FEw5ZkwVg==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.7.4.tgz", + "integrity": "sha512-6mKkiGK9O+eGDeewpUHGyM2Xjlp69Oy+N/0o5zdzfN84YqVPqLV+Y7ub6fMxZUvmRt6L0kuv/ZoDoxeUk+QNKg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0", - "@tiptap/pm": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" } }, "node_modules/@tiptap/extension-image": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.7.0.tgz", - "integrity": "sha512-F1WvetjXxbvIMhfMDBh3dXKxJtvd8KUJH42V4wLgJaA+oKXHy8wG6eYKB4Y4kieKEzwwZuIE2PUAd1d8gmrlhA==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.7.4.tgz", + "integrity": "sha512-hUBN8q42pxrKR0erLTl5N0mq4HYP0aKIbZaxBui9DdlMaE1qkrm4bJ+Ori+OabUvhEbnky1HYhmrYaUDEPTfdQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-italic": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.7.0.tgz", - "integrity": "sha512-2qnX7e17Ppb4/R+ZlhNegse9NAcnlKVKa6izqqX7LNCm5Uf27PO0vxg7E2X6BTSyx+gsd9bBQRGNteVE01UdHQ==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.7.4.tgz", + "integrity": "sha512-j/86hNMRd2PbJX6DOs7CbrYgFJSXvZMnWkYRRol7XEELvEuIWoAgyJrW5HkDbVxmGfWPnLlqsoW7iTHml7P+Bg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-link": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.6.6.tgz", - "integrity": "sha512-NJSR5Yf/dI3do0+Mr6e6nkbxRQcqbL7NOPxo5Xw8VaKs2Oe8PX+c7hyqN3GZgn6uEbZdbVi1xjAniUokouwpFg==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.7.4.tgz", + "integrity": "sha512-nVzCEkK85JuNJH7oHW922V7LSjnZseihDsSCHCWjVNVgc+21s2ncGz16ZNOgiCOcnvxv7PtIB0EefXSuFZVPAQ==", "dependencies": { "linkifyjs": "^4.1.0" }, @@ -6683,62 +6683,62 @@ "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.6.6", - "@tiptap/pm": "^2.6.6" + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" } }, "node_modules/@tiptap/extension-list-item": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.7.0.tgz", - "integrity": "sha512-DbkijEOj7xl6T5NOTRw4GJoBTIwDdgBlSRMCliYJeKLFU/1TBIsi3MDdK08He/owdBnoF3qfMaKqvfi0HflwbQ==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.7.4.tgz", + "integrity": "sha512-2EiXAtkZdCUHCfYRQsslniQhUzvo8zEm+M6JHcsIRBRf27iE+nXrD6jq1WH2ZIUNLDUs4JsJhtc89aoSYkJGKw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-ordered-list": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.7.0.tgz", - "integrity": "sha512-jkac6lGvazqZFsIg1kLdR+5UdZRoaOA4v2+s8LXOxdZ0dr/hVuKPKcVoqnS7EJf4bLJ7ie1q8wV+59lQjpmadA==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.7.4.tgz", + "integrity": "sha512-Y7fnw3lTyOd1h6t5hKSkYqbJXteafIviRdmrQ/ERRayojV934DjRPBeMQnYcArE6nI178/wLI9YMt1HSMJklRw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-paragraph": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.7.0.tgz", - "integrity": "sha512-yCZ9srptAzZIragP0Evu6hvgUbhezYkhvlCU5w4Ecpp9FMU5FwjN1NnkkxnqSrp90LjsMRLFgUklybOfZ8EVQA==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.7.4.tgz", + "integrity": "sha512-Pv3zsyuE+RItlkZVFcjcnz+Omp/UCEO03n9daeHljMUl7Rt775fXtcTNKPqO65f2B2MPBxrSdJpTsoMK0bbcjA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-strike": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.7.0.tgz", - "integrity": "sha512-um+QCPYXQIcFMIR5mZgey3DbAmH1L+38+lwD5CHF7Xu3f9qkoHr+zGBVyEwvIi6VBo3ghMiGmDjm0EZZAlL0VA==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.7.4.tgz", + "integrity": "sha512-ELMFUCE9MlF0qsGzHJl0AxzGUVyS9rglk6pzidoB0iU1LuzUa/K1el5ID2ksSFdq2+STK17rOWQxUiv3X8C7gw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-table": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.7.3.tgz", - "integrity": "sha512-zv1SGgVywTY3vs+9EIMdYS7jZMovlfsraZ3Qdz1YkqN3dNZBUukXrfpZaJqzVwUvRehCVvjA+HG7zH12RU/XYQ==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.7.4.tgz", + "integrity": "sha512-zL9BKQFJDGkwKnr1MYzCfpBllhlL8pDR3Sf5WscbN66I+rXrAdpFl75AbWf7gE0Tk2YaJldshFkakgWN1tr+2A==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6749,9 +6749,9 @@ } }, "node_modules/@tiptap/extension-table-cell": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-2.7.3.tgz", - "integrity": "sha512-C6f2dAcatk/XROZ2Q1owv4DBrTyfVzfsK1Jh7rk3mkpEa8oh/lPKR8thYjmaLC/BlPYjtVuIbMIqp9lz6U/Ufw==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-2.7.4.tgz", + "integrity": "sha512-8/mM0lv8k6dRBjGHNh9HIGNaRuq+A/7h699GC7A9xuE7R1/xjDMKRZpPTmvogqfAq2U6mH16oxr/KpBuixji1w==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6761,9 +6761,9 @@ } }, "node_modules/@tiptap/extension-table-header": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-2.7.3.tgz", - "integrity": "sha512-eL1FVn+GBf0dRYmsE88QeJa3azwVKhyYDAFTmoGIwilHsjbNzb4ptUGi+ko2XpxLHvY+XfGLe3+UEZbQ3FDOIA==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-2.7.4.tgz", + "integrity": "sha512-ZChahHwx0WlPynbMc4zsIgAAOar695A1AYTkWes7Y454xJy1vkGw607w+DVEHCWxU5h943H2UF7DUgzQS9XbAg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6773,9 +6773,9 @@ } }, "node_modules/@tiptap/extension-table-row": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-2.7.3.tgz", - "integrity": "sha512-gB6gXYVCGWn6IDb/oV3ds1LI0yLLIwymcvcu1MWnT9p8qClZPaId/J6/+mQbSGCEc8G1SzYYUhnu2dsaVIsFsw==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-2.7.4.tgz", + "integrity": "sha512-JxR6PdLiXUjW8VC7YdVSIvd8D7RKVOPPPK7cFrawxS4tKz+1temsK8hNZ3RKhajwS5ya4IRT+iKhSRetVnjuww==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6785,49 +6785,49 @@ } }, "node_modules/@tiptap/extension-text": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.7.0.tgz", - "integrity": "sha512-v2Wh9XvLpBGWqPMD19BI4y8hGqinGNTnGRMph2NXDkx+aG/42pEktd2KCgouE/La8nusj1FyWurQ1hK5XUBMOw==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.7.4.tgz", + "integrity": "sha512-1bF9LdfUumqXOz0A6xnOo7UHx+YLshxjMnjoMXjv7cOFOjdHbLmwKNTKGd2ltoCy3bSajoCPhPZL2Id89XDZfQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0-pre.0" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-text-align": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.6.6.tgz", - "integrity": "sha512-WdyxULEEHfI3hRDHAFOUoeP84h9myabadfjtZrub7/zO2PKKPAZLBN2vWat5PowH8E8GYX8vqKr9vaX+slfh5g==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.7.4.tgz", + "integrity": "sha512-/zJFhFko6yztjVlXL+Rpb4cpfSHydtFXkj+eto3Mjs0r+xzAsgP7WmQU2oTq482X1uvbfHD9u9SGSKH4jPcmig==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.6.6" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/extension-underline": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.6.6.tgz", - "integrity": "sha512-3A4HqsDM/AFb2VaeWACpGexjgI257kz0yU4jNV8uyydDR2KhqeinuEnoSoOmx9T3pL006TWfPg4vaQYPO3qvrQ==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.7.4.tgz", + "integrity": "sha512-1WT2ZHjBoyW6MzKrLC1v2KJszuozh6jzIbcabslRRNaEJFfsjIFgfU3TBpaXF+JKEBCi3h1JpWMgmtnr0puFVA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.6.6" + "@tiptap/core": "^2.7.0" } }, "node_modules/@tiptap/pm": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.6.6.tgz", - "integrity": "sha512-56FGLPn3fwwUlIbLs+BO21bYfyqP9fKyZQbQyY0zWwA/AG2kOwoXaRn7FOVbjP6CylyWpFJnpRRmgn694QKHEg==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.7.4.tgz", + "integrity": "sha512-YXjgPLN6/msTkKakuzgBm6Dd/Li3ORtysSki3fHnOFcy8R4c5JZLkYECQk6aJHsxvl/vGvNgaJy5yCDbhnaTAg==", "dependencies": { "prosemirror-changeset": "^2.2.1", "prosemirror-collab": "^1.3.1", - "prosemirror-commands": "^1.5.2", + "prosemirror-commands": "^1.6.0", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", @@ -6835,14 +6835,14 @@ "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.0", "prosemirror-menu": "^1.2.4", - "prosemirror-model": "^1.22.2", + "prosemirror-model": "^1.22.3", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.4.0", - "prosemirror-trailing-node": "^2.0.9", - "prosemirror-transform": "^1.9.0", - "prosemirror-view": "^1.33.9" + "prosemirror-trailing-node": "^3.0.0", + "prosemirror-transform": "^1.10.0", + "prosemirror-view": "^1.33.10" }, "funding": { "type": "github", @@ -19514,11 +19514,11 @@ } }, "node_modules/prosemirror-trailing-node": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.9.tgz", - "integrity": "sha512-YvyIn3/UaLFlFKrlJB6cObvUhmwFNZVhy1Q8OpW/avoTbD/Y7H5EcjK4AZFKhmuS6/N6WkGgt7gWtBWDnmFvHg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz", + "integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==", "dependencies": { - "@remirror/core-constants": "^2.0.2", + "@remirror/core-constants": "3.0.0", "escape-string-regexp": "^4.0.0" }, "peerDependencies": { diff --git a/package.json b/package.json index d50b98524f..5db1596f1d 100644 --- a/package.json +++ b/package.json @@ -192,34 +192,34 @@ "npm": ">=10.1 < 11" }, "dependencies": { - "@tiptap/core": "^2.6.6", - "@tiptap/extension-blockquote": "^2.7.0", - "@tiptap/extension-bold": "^2.7.0", - "@tiptap/extension-bullet-list": "^2.7.0", - "@tiptap/extension-code": "^2.7.0", - "@tiptap/extension-code-block": "^2.7.0", - "@tiptap/extension-document": "^2.7.0", - "@tiptap/extension-dropcursor": "^2.7.0", - "@tiptap/extension-gapcursor": "^2.7.0", - "@tiptap/extension-hard-break": "^2.7.0", - "@tiptap/extension-heading": "^2.7.0", - "@tiptap/extension-history": "^2.7.0", - "@tiptap/extension-horizontal-rule": "^2.7.0", - "@tiptap/extension-image": "^2.7.0", - "@tiptap/extension-italic": "^2.7.0", - "@tiptap/extension-link": "^2.6.6", - "@tiptap/extension-list-item": "^2.7.0", - "@tiptap/extension-ordered-list": "^2.7.0", - "@tiptap/extension-paragraph": "^2.7.0", - "@tiptap/extension-strike": "^2.7.0", - "@tiptap/extension-table": "^2.7.3", - "@tiptap/extension-table-cell": "^2.7.3", - "@tiptap/extension-table-header": "^2.7.3", - "@tiptap/extension-table-row": "^2.7.3", - "@tiptap/extension-text": "^2.7.0", - "@tiptap/extension-text-align": "^2.6.6", - "@tiptap/extension-underline": "^2.6.6", - "@tiptap/pm": "^2.6.6", + "@tiptap/core": "^2.7.4", + "@tiptap/extension-blockquote": "^2.7.4", + "@tiptap/extension-bold": "^2.7.4", + "@tiptap/extension-bullet-list": "^2.7.4", + "@tiptap/extension-code": "^2.7.4", + "@tiptap/extension-code-block": "^2.7.4", + "@tiptap/extension-document": "^2.7.4", + "@tiptap/extension-dropcursor": "^2.7.4", + "@tiptap/extension-gapcursor": "^2.7.4", + "@tiptap/extension-hard-break": "^2.7.4", + "@tiptap/extension-heading": "^2.7.4", + "@tiptap/extension-history": "^2.7.4", + "@tiptap/extension-horizontal-rule": "^2.7.4", + "@tiptap/extension-image": "^2.7.4", + "@tiptap/extension-italic": "^2.7.4", + "@tiptap/extension-link": "^2.7.4", + "@tiptap/extension-list-item": "^2.7.4", + "@tiptap/extension-ordered-list": "^2.7.4", + "@tiptap/extension-paragraph": "^2.7.4", + "@tiptap/extension-strike": "^2.7.4", + "@tiptap/extension-table": "^2.7.4", + "@tiptap/extension-table-cell": "^2.7.4", + "@tiptap/extension-table-header": "^2.7.4", + "@tiptap/extension-table-row": "^2.7.4", + "@tiptap/extension-text": "^2.7.4", + "@tiptap/extension-text-align": "^2.7.4", + "@tiptap/extension-underline": "^2.7.4", + "@tiptap/pm": "^2.7.4", "@types/diff": "^5.2.1", "@types/dompurify": "^3.0.5", "@types/uuid": "^10.0.0", From 4f79f903520f50c339541809c81d5f11ae47a518 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:52:20 +0200 Subject: [PATCH 141/241] chore: eslint --- src/packages/rte/manifests.ts | 1 + src/packages/rte/tiptap/property-editors/manifests.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/packages/rte/manifests.ts b/src/packages/rte/manifests.ts index 5e69aa24fd..6b7ca9ed76 100644 --- a/src/packages/rte/manifests.ts +++ b/src/packages/rte/manifests.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line local-rules/no-relative-import-to-import-map-module import { manifests as tiptapManifests } from './tiptap/manifests.js'; import type { ManifestTypes, UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; diff --git a/src/packages/rte/tiptap/property-editors/manifests.ts b/src/packages/rte/tiptap/property-editors/manifests.ts index b9b660577c..71f28f1a7c 100644 --- a/src/packages/rte/tiptap/property-editors/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/manifests.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line local-rules/no-relative-import-to-import-map-module import { manifests as tiptapManifests } from './tiptap/manifests.js'; import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; From 5b93b0d3a10b8c7a38412480a5e5844622212984 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:56:26 +0200 Subject: [PATCH 142/241] feat: adds supported width and height attributes to embeds --- .../rte/tiptap/extensions/umb/embedded-media.extension.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts b/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts index ccd8ee60e0..9d0a1f71c8 100644 --- a/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts @@ -33,6 +33,12 @@ export default class UmbTiptapEmbedExtensionApi extends UmbTiptapToolbarElementA const result = await modalHandler.onSubmit().catch(() => undefined); if (!result) return; - editor?.commands.setEmbeddedMedia(result); + editor?.commands.setEmbeddedMedia({ + markup: result.markup, + url: result.url, + constrain: result.constrain, + height: result.height?.toString(), + width: result.width?.toString(), + }); } } From a3a089e2208806db03639d74111f8251ffb0dd52 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:59:23 +0200 Subject: [PATCH 143/241] chore(sonarcloud): mark properties as readonly --- .../tiptap/property-editor-ui-tiptap.element.ts | 4 ++-- .../tiny-mce/property-editor-ui-tiny-mce.element.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index fcd9ce238a..ade8960b91 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -86,8 +86,8 @@ export class UmbPropertyEditorUiTiptapElement extends UmbLitElement implements U private _markup = ''; private _latestMarkup = ''; // The latest value gotten from the Tiptap editor. - #managerContext = new UmbBlockRteManagerContext(this); - #entriesContext = new UmbBlockRteEntriesContext(this); + readonly #managerContext = new UmbBlockRteManagerContext(this); + readonly #entriesContext = new UmbBlockRteEntriesContext(this); constructor() { super(); diff --git a/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts index 7f95d97cdf..11a10a4862 100644 --- a/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts +++ b/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts @@ -84,8 +84,8 @@ export class UmbPropertyEditorUITinyMceElement extends UmbLitElement implements private _markup = ''; private _latestMarkup = ''; // The latest value gotten from the TinyMCE editor. - #managerContext = new UmbBlockRteManagerContext(this); - #entriesContext = new UmbBlockRteEntriesContext(this); + readonly #managerContext = new UmbBlockRteManagerContext(this); + readonly #entriesContext = new UmbBlockRteEntriesContext(this); constructor() { super(); From ffaef7d315695652c61759941fe158583816c42e Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 15:09:34 +0200 Subject: [PATCH 144/241] chore(sonarcloud): fix issues --- .../block-rte-entry.element.ts | 2 +- .../tiny-mce-block-picker.plugin.ts | 4 +-- .../temporary-file-manager.class.ts | 4 +-- .../media-caption-alt-text-modal.element.ts | 2 +- .../input-tiptap/input-tiptap.element.ts | 2 +- .../input-tiptap/tiptap-hover-menu.element.ts | 2 +- .../toolbar/tiptap-toolbar-button.element.ts | 2 +- .../tiptap-toolbar-dropdown-base.element.ts | 4 +-- .../extensions/umb/media-upload.extension.ts | 4 +-- .../extensions/umb/mediapicker.extension.ts | 2 +- ...ui-tiptap-toolbar-configuration.element.ts | 26 +++++++++---------- 11 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts b/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts index 1e6d55a034..435711cb3e 100644 --- a/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts +++ b/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts @@ -152,7 +152,7 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert return html``; } - #filterBlockCustomViews = (manifest: ManifestBlockEditorCustomView) => { + readonly #filterBlockCustomViews = (manifest: ManifestBlockEditorCustomView) => { const elementTypeAlias = this._contentElementTypeAlias ?? ''; const isForBlockEditor = !manifest.forBlockEditor || stringOrStringArrayContains(manifest.forBlockEditor, UMB_BLOCK_RTE); diff --git a/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts b/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts index 4abbf3c32a..642128893c 100644 --- a/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts +++ b/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts @@ -8,8 +8,8 @@ import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type'; import type { Editor } from '@umbraco-cms/backoffice/external/tinymce'; export default class UmbTinyMceMultiUrlPickerPlugin extends UmbTinyMcePluginBase { - #localize = new UmbLocalizationController(this._host); - #editor: Editor; + readonly #localize = new UmbLocalizationController(this._host); + readonly #editor: Editor; #blocks?: Array; #entriesContext?: typeof UMB_BLOCK_RTE_ENTRIES_CONTEXT.TYPE; diff --git a/src/packages/core/temporary-file/temporary-file-manager.class.ts b/src/packages/core/temporary-file/temporary-file-manager.class.ts index bade3a5086..e89f8979aa 100644 --- a/src/packages/core/temporary-file/temporary-file-manager.class.ts +++ b/src/packages/core/temporary-file/temporary-file-manager.class.ts @@ -19,9 +19,9 @@ export interface UmbTemporaryFileModel { export class UmbTemporaryFileManager< UploadableItem extends UmbTemporaryFileModel = UmbTemporaryFileModel, > extends UmbControllerBase { - #temporaryFileRepository = new UmbTemporaryFileRepository(this._host); + readonly #temporaryFileRepository = new UmbTemporaryFileRepository(this._host); - #queue = new UmbArrayState([], (item) => item.temporaryUnique); + readonly #queue = new UmbArrayState([], (item) => item.temporaryUnique); public readonly queue = this.#queue.asObservable(); async uploadOne(uploadableItem: UploadableItem): Promise { diff --git a/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts b/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts index 40581dd0a5..48acc80eb5 100644 --- a/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts +++ b/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts @@ -14,7 +14,7 @@ export class UmbMediaCaptionAltTextModalElement extends UmbModalBaseElement< UmbMediaCaptionAltTextModalValue > { #mediaUnique?: string; - #mediaDetailRepository = new UmbMediaDetailRepository(this); + readonly #mediaDetailRepository = new UmbMediaDetailRepository(this); override connectedCallback() { super.connectedCallback(); diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index cdce8380da..dc42f4fc16 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -24,7 +24,7 @@ const elementName = 'umb-input-tiptap'; @customElement(elementName) export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement) { - #requiredExtensions = [Document, Dropcursor, Gapcursor, HardBreak, History, Paragraph, Text]; + readonly #requiredExtensions = [Document, Dropcursor, Gapcursor, HardBreak, History, Paragraph, Text]; @state() private _extensions: Array = []; diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts index 30f1458cb7..36a75174bd 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts @@ -23,7 +23,7 @@ export class UmbTiptapHoverMenuElement extends LitElement { this.setAttribute('popover', ''); } - #onUpdate = () => { + readonly #onUpdate = () => { if (this.editor?.isActive('link')) { // show the popover this.showPopover(); diff --git a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts index d412ec4682..e7b2fd0b81 100644 --- a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts +++ b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts @@ -33,7 +33,7 @@ export class UmbTiptapToolbarButtonElement extends UmbLitElement { } } - #onEditorUpdate = () => { + readonly #onEditorUpdate = () => { if (this.api && this.editor && this.manifest) { this._isActive = this.api.isActive(this.editor); } diff --git a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown-base.element.ts b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown-base.element.ts index d9750baa8b..a5308a8539 100644 --- a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown-base.element.ts +++ b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown-base.element.ts @@ -15,13 +15,13 @@ export abstract class UmbTiptapToolbarDropdownBaseElement extends UmbLitElement protected abstract get items(): TiptapDropdownItem[]; protected abstract get label(): string; - #onMouseEnter = (popoverId: string) => { + readonly #onMouseEnter = (popoverId: string) => { const popover = this.shadowRoot?.querySelector(`#${this.makeAlias(popoverId)}`) as UUIPopoverContainerElement; if (!popover) return; popover.showPopover(); }; - #onMouseLeave = (popoverId: string) => { + readonly #onMouseLeave = (popoverId: string) => { popoverId = popoverId.replace(/\s/g, '-').toLowerCase(); const popover = this.shadowRoot?.querySelector(`#${this.makeAlias(popoverId)}`) as UUIPopoverContainerElement; if (!popover) return; diff --git a/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts b/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts index 2cfd79dca1..ac8fd6eb4f 100644 --- a/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts @@ -32,8 +32,8 @@ export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApi ); } - #manager = new UmbTemporaryFileManager(this); - #localize = new UmbLocalizationController(this); + readonly #manager = new UmbTemporaryFileManager(this); + readonly #localize = new UmbLocalizationController(this); #notificationContext?: typeof UMB_NOTIFICATION_CONTEXT.TYPE; constructor(host: UmbControllerHost) { diff --git a/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts b/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts index 6947d772b8..32f38d2300 100644 --- a/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts @@ -41,7 +41,7 @@ export default class UmbTiptapMediaPickerExtensionApi extends UmbTiptapToolbarEl console.log('umb-media.execute', editor); const selection = await this.#openMediaPicker(); - if (!selection || !selection.length) return; + if (!selection?.length) return; editor?.chain().focus().insertContent(`${selection}`).run(); } diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 1014ceda82..13a05350c9 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -37,14 +37,12 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement set value(value: string | string[] | null) { if (!value) { this.#selectedValues = []; + } else if (typeof value === 'string') { + this.#selectedValues = value.split(',').filter((x) => x.length > 0); + } else if (Array.isArray(value)) { + this.#selectedValues = value; } else { - if (typeof value === 'string') { - this.#selectedValues = value.split(',').filter((x) => x.length > 0); - } else if (Array.isArray(value)) { - this.#selectedValues = value; - } else { - this.#selectedValues = []; - } + this.#selectedValues = []; } this.requestUpdate('#selectedValuesNew'); @@ -60,7 +58,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement private _toolbarItems: ToolbarItems = []; @state() - private _toolbarConfig: Array = []; + private readonly _toolbarConfig: Array = []; @state() _selectedValuesNew: ToolbarConfig[][][] = [[[]]]; @@ -123,7 +121,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement this.requestUpdate('_selectedValuesNew'); } - #onChange = (item: ToolbarConfig) => { + readonly #onChange = (item: ToolbarConfig) => { const value = this._toolbarItems .flatMap((group) => group.items.map((i) => { @@ -141,17 +139,17 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement this.dispatchEvent(new UmbPropertyValueChangeEvent()); }; - #onDragStart = (event: DragEvent, alias: string) => { + readonly #onDragStart = (event: DragEvent, alias: string) => { event.dataTransfer!.setData('text/plain', alias); event.dataTransfer!.dropEffect = 'move'; event.dataTransfer!.effectAllowed = 'move'; }; - #onDragOver = (event: DragEvent) => { + readonly #onDragOver = (event: DragEvent) => { event.preventDefault(); }; - #onDragEnter = (event: DragEvent) => { + readonly #onDragEnter = (event: DragEvent) => { const dropzone = event .composedPath() .find((v) => v instanceof HTMLElement && v.classList.contains('toolbar-group') && v.hasAttribute('dropzone')); @@ -160,7 +158,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement console.log('hovered dropzone', this.#hoveredDropzone); }; - #onDrop = (event: DragEvent) => { + readonly #onDrop = (event: DragEvent) => { event.preventDefault(); const groupElement = event @@ -269,7 +267,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement `; } - static override styles = [ + static override readonly styles = [ UmbTextStyles, css` uui-icon { From a1fb7bc523a68a567f5d8ca1023dac29a8c94e16 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 15:09:53 +0200 Subject: [PATCH 145/241] styling, cleanup and icons --- ...ui-tiptap-toolbar-configuration.element.ts | 23 +++++------------ ...p-toolbar-groups-configuration2.element.ts | 25 +++++++++++++------ 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 9138d3847f..91d1a11c51 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -1,3 +1,4 @@ +import type UmbTiptapToolbarGroupsConfiguration2Element from './tiptap-toolbar-groups-configuration2.element.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import { customElement, css, html, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; @@ -8,14 +9,8 @@ import { type UmbPropertyEditorConfigCollection, } from '@umbraco-cms/backoffice/property-editor'; -import './tiptap-toolbar-groups-configuration.element.js'; import './tiptap-toolbar-groups-configuration2.element.js'; -import { tinymce } from '@umbraco-cms/backoffice/external/tinymce'; -import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; - -const tinyIconSet = tinymce.IconManager.get('default'); - // If an extension does not have a position, it is considered hidden in the toolbar type TestServerValue = Array<{ alias: string; @@ -66,15 +61,12 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement @state() private _extensionConfigs: ExtensionConfig[] = []; - @state() - private _extensions: ExtensionConfig[] = []; - protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); this.observe(umbExtensionsRegistry.byType('tiptapExtension'), (extensions) => { console.log('extensions', extensions); - this._extensions = extensions.map((ext) => { + this._extensionConfigs = extensions.map((ext) => { return { alias: ext.alias, label: ext.meta.label, @@ -87,10 +79,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement } #setupExtensionCategories() { - // console.log('exensions', this._extensions); - // const toolbarConfigValue = this.config?.getValueByAlias('toolbar'); - // if (!toolbarConfigValue) return; - const withSelected = this._extensions.map((v) => { + const withSelected = this._extensionConfigs.map((v) => { return { ...v, selected: this.value?.some((item) => item.alias === v.alias), @@ -130,7 +119,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement } #onChange(event: CustomEvent) { - this.value = event.target.value; + this.value = (event.target as UmbTiptapToolbarGroupsConfiguration2Element).value; // update the selected state of the extensions // TODO this should be done in a more efficient way @@ -145,7 +134,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement override render() { return html` - +
    ${repeat( this._extensionCategories, @@ -163,7 +152,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement label=${item.label} .value=${item.alias} @click=${() => this.#onExtensionSelect(item)} - > ${item.label}
    `, diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index 6d94938f87..c7ddbafb35 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts @@ -29,6 +29,9 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { return this.#originalFormat; } + @property({ attribute: false }) + extensionConfigs: Extension[] = []; + //TODO: Use the context again so that we can remove items from the extensions list from here. @state() @@ -118,8 +121,10 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { } private renderItem(alias: string) { + const extension = this.extensionConfigs.find((ext) => ext.alias === alias); + if (!extension) return nothing; return html`
    this.#onDragStart(e, alias)}> - ${alias} +
    `; } @@ -140,7 +145,7 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { return html`
    ${repeat(row, (group, groupIndex) => this.renderGroup(group, rowIndex, groupIndex))} - + this.#addGroup(rowIndex, row.length)}>+
    `; @@ -149,7 +154,7 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { override render() { return html` ${repeat(this._structuredData, (row, rowIndex) => this.renderRow(row, rowIndex))} - + this.#addRow(this._structuredData.length)}>+

    Extensions hidden from the toolbar

    @@ -247,16 +252,20 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { position: relative; display: flex; gap: 3px; - border: 1px solid var(--uui-color-border); + border-radius: var(--uui-border-radius); + background-color: var(--uui-color-surface-alt); padding: 6px; - min-height: 24px; - min-width: 24px; + min-height: 30px; + min-width: 30px; } .item { - padding: 3px; + padding: var(--uui-size-space-2); border: 1px solid var(--uui-color-border); - border-radius: 3px; + border-radius: var(--uui-border-radius); background-color: var(--uui-color-surface); + cursor: move; + display: flex; + align-items: baseline; } .remove-group-button { position: absolute; From cd82d83399f2775396aa2a298e78d770ca09dac0 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 26 Sep 2024 15:12:11 +0200 Subject: [PATCH 146/241] build(deps-dev): bump typescript-json-schema to support popovers --- package-lock.json | 40 ++++++++++++++++++---------------------- package.json | 2 +- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7f836c586a..bde89122d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -113,7 +113,7 @@ "typedoc": "^0.26.5", "typescript": "^5.5.3", "typescript-eslint": "^8.0.1", - "typescript-json-schema": "^0.64.0", + "typescript-json-schema": "^0.65.1", "vite": "^5.4.6", "vite-plugin-static-copy": "^1.0.6", "vite-tsconfig-paths": "^4.3.2", @@ -22117,18 +22117,18 @@ } }, "node_modules/typescript-json-schema": { - "version": "0.64.0", - "resolved": "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.64.0.tgz", - "integrity": "sha512-Sew8llkYSzpxaMoGjpjD6NMFCr6DoWFHLs7Bz1LU48pzzi8ok8W+GZs9cG87IMBpC0UI7qwBMUI2um0LGxxLOg==", + "version": "0.65.1", + "resolved": "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.65.1.tgz", + "integrity": "sha512-tuGH7ff2jPaUYi6as3lHyHcKpSmXIqN7/mu50x3HlYn0EHzLpmt3nplZ7EuhUkO0eqDRc9GqWNkfjgBPIS9kxg==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@types/node": "^16.9.2", + "@types/node": "^18.11.9", "glob": "^7.1.7", "path-equal": "^1.2.5", "safe-stable-stringify": "^2.2.0", "ts-node": "^10.9.1", - "typescript": "~5.1.0", + "typescript": "~5.5.0", "yargs": "^17.1.1" }, "bin": { @@ -22136,10 +22136,13 @@ } }, "node_modules/typescript-json-schema/node_modules/@types/node": { - "version": "16.18.105", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.105.tgz", - "integrity": "sha512-w2d0Z9yMk07uH3+Cx0N8lqFyi3yjXZxlbYappPj+AsOlT02OyxyiuNoNHdGt6EuiSm8Wtgp2YV7vWg+GMFrvFA==", - "dev": true + "version": "18.19.53", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.53.tgz", + "integrity": "sha512-GLxgUgHhDKO1Edw9Q0lvMbiO/IQXJwJlMaqxSGBXMpPy8uhkCs2iiPFaB2Q/gmobnFkckD3rqTBMVjXdwq+nKg==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/typescript-json-schema/node_modules/glob": { "version": "7.2.3", @@ -22162,18 +22165,11 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/typescript-json-schema/node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } + "node_modules/typescript-json-schema/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true }, "node_modules/typical": { "version": "4.0.0", diff --git a/package.json b/package.json index 5db1596f1d..02542051f5 100644 --- a/package.json +++ b/package.json @@ -293,7 +293,7 @@ "typedoc": "^0.26.5", "typescript": "^5.5.3", "typescript-eslint": "^8.0.1", - "typescript-json-schema": "^0.64.0", + "typescript-json-schema": "^0.65.1", "vite": "^5.4.6", "vite-plugin-static-copy": "^1.0.6", "vite-tsconfig-paths": "^4.3.2", From 546b179a79ff5ab94559d4dbb814ed2b9bbdc689 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 15:15:21 +0200 Subject: [PATCH 147/241] add WIP text --- .../tiptap-toolbar-groups-configuration2.element.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index c7ddbafb35..4da770c009 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts @@ -153,6 +153,11 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { override render() { return html` +

    + WIP Feature Rows, groups and order of items have no effect yet.
    + However adding and removing items from the toolbar is functional. Also hiding items from the toolbar by not + including them in the toolbar layout is functional. +

    ${repeat(this._structuredData, (row, rowIndex) => this.renderRow(row, rowIndex))} this.#addRow(this._structuredData.length)}>+ From fd87648ffacf772ea76363a45ead6ac0d4dfa738 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 15:21:49 +0200 Subject: [PATCH 148/241] update message --- .../tiptap-toolbar-groups-configuration2.element.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index 4da770c009..cccbdaa74d 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts @@ -153,10 +153,10 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { override render() { return html` -

    - WIP Feature Rows, groups and order of items have no effect yet.
    - However adding and removing items from the toolbar is functional. Also hiding items from the toolbar by not - including them in the toolbar layout is functional. +

    + WIP Feature Rows, groups, and item order have no effect yet.
    + However, adding and removing items from the toolbar is functional. Additionally, hiding items from the toolbar + while retaining their functionality by excluding them from the toolbar layout is also functional.

    ${repeat(this._structuredData, (row, rowIndex) => this.renderRow(row, rowIndex))} this.#addRow(this._structuredData.length)}>+ From d7865ba976512e8681c1c1c1a0b94f81d7ceff48 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:06:46 +0200 Subject: [PATCH 149/241] remove button styling and remove items --- ...p-toolbar-groups-configuration2.element.ts | 70 ++++++++++++++----- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index cccbdaa74d..fc42e3b242 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts @@ -43,11 +43,22 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { #onDragStart = (event: DragEvent, alias: string) => { this.#currentDragAlias = alias; - event.dataTransfer!.dropEffect = 'move'; + event.dataTransfer!.effectAllowed = 'move'; }; #onDragOver = (event: DragEvent) => { event.preventDefault(); + event.dataTransfer!.dropEffect = 'move'; + }; + + #onDragEnd = (event: DragEvent) => { + event.preventDefault(); + if (event.dataTransfer?.dropEffect === 'none') { + const fromPos = this.#originalFormat.find((item) => item.alias === this.#currentDragAlias)?.position; + if (!fromPos) return; + + this.removeItem(fromPos); + } }; #onDrop = (event: DragEvent, toPos: [number, number, number]) => { @@ -83,6 +94,16 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { this.dispatchEvent(new UmbChangeEvent()); }; + private removeItem(from: [number, number, number]) { + const [rowIndex, groupIndex, itemIndex] = from; + this._structuredData[rowIndex][groupIndex].splice(itemIndex, 1); + + this.#updateOriginalFormat(); + + this.requestUpdate('_structuredData'); + this.dispatchEvent(new UmbChangeEvent()); + } + #addGroup = (rowIndex: number, groupIndex: number) => { this._structuredData[rowIndex].splice(groupIndex, 0, []); this.requestUpdate('_structuredData'); @@ -123,7 +144,11 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { private renderItem(alias: string) { const extension = this.extensionConfigs.find((ext) => ext.alias === alias); if (!extension) return nothing; - return html`
    this.#onDragStart(e, alias)}> + return html`
    this.#onDragStart(e, alias)}>
    `; } @@ -136,7 +161,14 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { @dragover=${this.#onDragOver} @drop=${(e: DragEvent) => this.#onDrop(e, [rowIndex, groupIndex, group.length])}> ${group.map((alias) => this.renderItem(alias))} - + this.#removeGroup(rowIndex, groupIndex)}> + +
    `; } @@ -146,7 +178,14 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement {
    ${repeat(row, (group, groupIndex) => this.renderGroup(group, rowIndex, groupIndex))} this.#addGroup(rowIndex, row.length)}>+ - + this.#removeRow(rowIndex)}> + +
    `; } @@ -272,24 +311,21 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { display: flex; align-items: baseline; } + + .remove-row-button, .remove-group-button { - position: absolute; - top: -4px; - right: -4px; display: none; } - .group:hover .remove-group-button { - display: block; - } - - .remove-row-button { + .remove-group-button { position: absolute; - left: -25px; - top: 8px; - display: none; + top: -26px; + left: 50%; + transform: translateX(-50%); + z-index: 1; } - .row:hover .remove-row-button { - display: block; + .row:hover .remove-row-button, + .group:hover .remove-group-button { + display: flex; } `, ]; From 63e20d2e6acde66d429a39f2e2e7c015c93c3d3f Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:22:19 +0200 Subject: [PATCH 150/241] styling --- .../tiptap-toolbar-groups-configuration2.element.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index fc42e3b242..b978935be1 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts @@ -1,7 +1,6 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { customElement, css, html, property, repeat, nothing, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { Observable, UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; type Extension = { @@ -165,7 +164,7 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { look="primary" color="danger" compact - class="remove-group-button" + class="remove-group-button ${rowIndex === 0 && groupIndex === 0 && group.length === 0 ? 'hidden' : undefined}" @click=${() => this.#removeGroup(rowIndex, groupIndex)}> @@ -182,7 +181,7 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { look="primary" color="danger" compact - class="remove-row-button" + class="remove-row-button ${rowIndex === 0 && row[rowIndex].length === 0 ? 'hidden' : undefined}" @click=${() => this.#removeRow(rowIndex)}> @@ -323,8 +322,9 @@ export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { transform: translateX(-50%); z-index: 1; } - .row:hover .remove-row-button, - .group:hover .remove-group-button { + + .row:hover .remove-row-button:not(.hidden), + .group:hover .remove-group-button:not(.hidden) { display: flex; } `, From 1883cacd3e2202d90fe6817c3198bf0cfdc7ab29 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:27:15 +0200 Subject: [PATCH 151/241] cleanup --- ...ui-tiptap-toolbar-configuration.element.ts | 8 +- ...ap-toolbar-groups-configuration.element.ts | 352 +++++++++++------- ...p-toolbar-groups-configuration2.element.ts | 340 ----------------- 3 files changed, 227 insertions(+), 473 deletions(-) delete mode 100644 src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 91d1a11c51..4ecf695b94 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -1,4 +1,4 @@ -import type UmbTiptapToolbarGroupsConfiguration2Element from './tiptap-toolbar-groups-configuration2.element.js'; +import type UmbTiptapToolbarGroupsConfigurationElement from './tiptap-toolbar-groups-configuration.element.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import { customElement, css, html, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; @@ -9,7 +9,7 @@ import { type UmbPropertyEditorConfigCollection, } from '@umbraco-cms/backoffice/property-editor'; -import './tiptap-toolbar-groups-configuration2.element.js'; +import './tiptap-toolbar-groups-configuration.element.js'; // If an extension does not have a position, it is considered hidden in the toolbar type TestServerValue = Array<{ @@ -119,7 +119,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement } #onChange(event: CustomEvent) { - this.value = (event.target as UmbTiptapToolbarGroupsConfiguration2Element).value; + this.value = (event.target as UmbTiptapToolbarGroupsConfigurationElement).value; // update the selected state of the extensions // TODO this should be done in a more efficient way @@ -134,7 +134,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement override render() { return html` - +
    ${repeat( this._extensionCategories, diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index 8c9051ef06..9a04507cb3 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts @@ -1,7 +1,7 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { customElement, css, html, property, repeat, nothing, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { Observable, UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; type Extension = { alias: string; @@ -9,163 +9,165 @@ type Extension = { icon?: string; }; +type TestServerValue = Array<{ + alias: string; + position?: [number, number, number]; +}>; + @customElement('umb-tiptap-toolbar-groups-configuration') export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { @property({ attribute: false }) - availableExtensions: Array = []; - - @state() - private _toolbar: string[][][] = [[[]]]; - - #toolbarLayout: UmbArrayState | undefined; - - #testData: TestServerValue = [ - { - alias: 'bold', - position: [0, 0, 0], - }, - { - alias: 'italic', - position: [0, 0, 1], - }, - { - alias: 'undo', - position: [0, 1, 0], - }, - { - alias: 'redo', - position: [0, 1, 1], - }, - { - alias: 'copy', - position: [1, 0, 0], - }, - { - alias: 'paste', - position: [1, 2, 0], - }, - ]; + set value(value: TestServerValue) { + // if (this.#originalFormat === value) return; + // TODO: also check if the added values have positions, if not, there's no need to update the structured data. + this.#originalFormat = value; + this._structuredData = this.toStructuredData(value); + } - toStructuredData = (data: any): string[][][] => { - const structuredData: string[][][] = []; + get value(): TestServerValue { + return this.#originalFormat; + } - data.forEach(({ alias, position }) => { - const [rowIndex, groupIndex, aliasIndex] = position; + @property({ attribute: false }) + extensionConfigs: Extension[] = []; - // Ensure the row exists up to rowIndex - while (structuredData.length <= rowIndex) { - structuredData.push([]); - } + //TODO: Use the context again so that we can remove items from the extensions list from here. - const currentRow = structuredData[rowIndex]; + @state() + _structuredData: string[][][] = [[[]]]; - // Ensure the group exists up to groupIndex within the row - while (currentRow.length <= groupIndex) { - currentRow.push([]); - } + #originalFormat: TestServerValue = []; - const currentGroup = currentRow[groupIndex]; + #currentDragAlias?: string; - // Ensure the alias is placed at the correct position in the group - currentGroup[aliasIndex] = alias; - }); + #onDragStart = (event: DragEvent, alias: string) => { + this.#currentDragAlias = alias; + event.dataTransfer!.effectAllowed = 'move'; + }; - return structuredData; + #onDragOver = (event: DragEvent) => { + event.preventDefault(); + event.dataTransfer!.dropEffect = 'move'; }; - constructor() { - super(); + #onDragEnd = (event: DragEvent) => { + event.preventDefault(); + if (event.dataTransfer?.dropEffect === 'none') { + const fromPos = this.#originalFormat.find((item) => item.alias === this.#currentDragAlias)?.position; + if (!fromPos) return; - this.consumeContext( - 'umb-tiptap-toolbar-context', - (instance: { state: UmbArrayState; observable: Observable }) => { - this.#toolbarLayout = instance.state; + this.removeItem(fromPos); + } + }; - this.observe(instance.observable, (value) => { - this._toolbar = value.map((rows) => rows.map((groups) => [...groups])); - }); - }, - ); + #onDrop = (event: DragEvent, toPos: [number, number, number]) => { + event.preventDefault(); + const fromPos = this.#originalFormat.find((item) => item.alias === this.#currentDragAlias)?.position; - setTimeout(() => { - this._toolbar = this.toStructuredData(this.#testData); - }, 2000); - } + if (fromPos) { + this.moveItem(fromPos, toPos); + } else if (this.#currentDragAlias) { + this.insertItem(this.#currentDragAlias, toPos); + } + }; private moveItem = (from: [number, number, number], to: [number, number, number]) => { - const [fromRow, fromGroup, fromItem] = from; - const [toRow, toGroup, toItem] = to; + const [rowIndex, groupIndex, itemIndex] = from; // Get the item to move from the 'from' position - const itemToMove = this._toolbar[fromRow][fromGroup][fromItem]; + const itemToMove = this._structuredData[rowIndex][groupIndex][itemIndex]; // Remove the item from the original position - this._toolbar[fromRow][fromGroup].splice(fromItem, 1); + this._structuredData[rowIndex][groupIndex].splice(itemIndex, 1); + + this.insertItem(itemToMove, to); + }; + private insertItem = (alias: string, toPos: [number, number, number]) => { + const [rowIndex, groupIndex, itemIndex] = toPos; // Insert the item into the new position - this._toolbar[toRow][toGroup].splice(toItem, 0, itemToMove); + this._structuredData[rowIndex][groupIndex].splice(itemIndex, 0, alias); + this.#updateOriginalFormat(); - this.#toolbarLayout?.setValue(this._toolbar); + this.requestUpdate('_structuredData'); + this.dispatchEvent(new UmbChangeEvent()); }; + private removeItem(from: [number, number, number]) { + const [rowIndex, groupIndex, itemIndex] = from; + this._structuredData[rowIndex][groupIndex].splice(itemIndex, 1); + + this.#updateOriginalFormat(); + + this.requestUpdate('_structuredData'); + this.dispatchEvent(new UmbChangeEvent()); + } + #addGroup = (rowIndex: number, groupIndex: number) => { - this._toolbar[rowIndex].splice(groupIndex, 0, []); - this.#toolbarLayout?.setValue(this._toolbar); + this._structuredData[rowIndex].splice(groupIndex, 0, []); + this.requestUpdate('_structuredData'); }; #removeGroup = (rowIndex: number, groupIndex: number) => { - this._toolbar[rowIndex].splice(groupIndex, 1); - this.#toolbarLayout?.setValue(this._toolbar); + if (rowIndex === 0 && groupIndex === 0) { + // Prevent removing the last group + this._structuredData[rowIndex][groupIndex] = []; + } else { + this._structuredData[rowIndex].splice(groupIndex, 1); + } + this.requestUpdate('_structuredData'); + this.#updateOriginalFormat(); }; #addRow = (rowIndex: number) => { - this._toolbar.splice(rowIndex, 0, [[]]); - this.#toolbarLayout?.setValue(this._toolbar); + this._structuredData.splice(rowIndex, 0, [[]]); + this.requestUpdate('_structuredData'); }; #removeRow = (rowIndex: number) => { - this._toolbar.splice(rowIndex, 1); - this.#toolbarLayout?.setValue(this._toolbar); - }; - - #onDragStart = (event: DragEvent, pos: [number, number, number]) => { - event.dataTransfer!.setData('application/json', JSON.stringify(pos)); - event.dataTransfer!.dropEffect = 'move'; - }; - - #onDragOver = (event: DragEvent) => { - event.preventDefault(); + if (rowIndex === 0) { + // Prevent removing the last row + this._structuredData[rowIndex] = [[]]; + } else { + this._structuredData.splice(rowIndex, 1); + } + this.requestUpdate('_structuredData'); + this.#updateOriginalFormat(); }; - #onDrop = (event: DragEvent, toPos: [number, number, number]) => { - event.preventDefault(); - - const fromPos: [number, number, number] = JSON.parse(event.dataTransfer!.getData('application/json') ?? '[0,0,0]'); - this.moveItem(fromPos, toPos); - }; + #updateOriginalFormat() { + this.#originalFormat = this.toOriginalFormat(this._structuredData); + this.dispatchEvent(new UmbChangeEvent()); + } - private renderItem(alias: string, rowIndex: number, groupIndex: number, itemIndex: number) { - const extension = this.availableExtensions.find((ext) => ext.alias === alias); + private renderItem(alias: string) { + const extension = this.extensionConfigs.find((ext) => ext.alias === alias); if (!extension) return nothing; - return html`
    this.#onDragStart(e, [rowIndex, groupIndex, itemIndex])}> - ${extension.label} + @dragend=${this.#onDragEnd} + @dragstart=${(e: DragEvent) => this.#onDragStart(e, alias)}> +
    `; } private renderGroup(group: string[], rowIndex: number, groupIndex: number) { - console.log('group', group); return html`
    this.#onDrop(e, [rowIndex, groupIndex, 0])}> - ${group.map((alias, itemIndex) => this.renderItem(alias, rowIndex, groupIndex, itemIndex))} - + @drop=${(e: DragEvent) => this.#onDrop(e, [rowIndex, groupIndex, group.length])}> + ${group.map((alias) => this.renderItem(alias))} + this.#removeGroup(rowIndex, groupIndex)}> + +
    `; } @@ -174,17 +176,101 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { return html`
    ${repeat(row, (group, groupIndex) => this.renderGroup(group, rowIndex, groupIndex))} - - + this.#addGroup(rowIndex, row.length)}>+ + this.#removeRow(rowIndex)}> + +
    `; } override render() { - return html`${repeat(this._toolbar, (row, rowIndex) => this.renderRow(row, rowIndex))} - `; + return html` +

    + WIP Feature Rows, groups, and item order have no effect yet.
    + However, adding and removing items from the toolbar is functional. Additionally, hiding items from the toolbar + while retaining their functionality by excluding them from the toolbar layout is also functional. +

    + ${repeat(this._structuredData, (row, rowIndex) => this.renderRow(row, rowIndex))} + this.#addRow(this._structuredData.length)}>+ + +

    Extensions hidden from the toolbar

    +
    + ${this.#originalFormat?.filter((item) => !item.position).map((item) => this.renderItem(item.alias))} +
    + `; } + toStructuredData = (data: TestServerValue) => { + if (!data?.length) return [[[]]]; + + const structuredData: string[][][] = [[[]]]; + data.forEach(({ alias, position }) => { + if (!position) return; + + const [rowIndex, groupIndex, aliasIndex] = position; + + while (structuredData.length <= rowIndex) { + structuredData.push([]); + } + + const currentRow = structuredData[rowIndex]; + + while (currentRow.length <= groupIndex) { + currentRow.push([]); + } + + const currentGroup = currentRow[groupIndex]; + + currentGroup[aliasIndex] = alias; + }); + + return structuredData; + }; + + toOriginalFormat = (structuredData: string[][][]) => { + const originalData: TestServerValue = []; + + structuredData.forEach((row, rowIndex) => { + row.forEach((group, groupIndex) => { + group.forEach((alias, aliasIndex) => { + if (alias) { + originalData.push({ + alias, + position: [rowIndex, groupIndex, aliasIndex], + }); + } + }); + }); + }); + + // add items from this.#originalFormat only if they are not already in the structured data. and if they have a position property set, unset it. + this.#originalFormat.forEach((item) => { + if (!originalData.some((i) => i.alias === item.alias)) { + originalData.push({ + alias: item.alias, + }); + } + }); + + // TODO: this code removes the items completely, while the one above just puts them back into the hidden extensions list. Which one do we prefer? + // this.#originalFormat.forEach((item) => { + // if (!item.position) { + // const exists = originalData.find((i) => i.alias === item.alias); + // if (!exists) { + // originalData.push(item); + // } + // } + // }); + + return originalData; + }; + static override styles = [ UmbTextStyles, css` @@ -193,6 +279,13 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { flex-direction: column; gap: 6px; } + .hidden-extensions { + display: flex; + gap: 6px; + } + .hidden-extensions-header { + margin-bottom: 3px; + } .row { position: relative; display: flex; @@ -202,36 +295,37 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { position: relative; display: flex; gap: 3px; - border: 1px solid #ccc; + border-radius: var(--uui-border-radius); + background-color: var(--uui-color-surface-alt); padding: 6px; - min-height: 24px; - min-width: 24px; + min-height: 30px; + min-width: 30px; } .item { - padding: 3px; - border: 1px solid #ccc; - border-radius: 3px; - background-color: #f9f9f9; + padding: var(--uui-size-space-2); + border: 1px solid var(--uui-color-border); + border-radius: var(--uui-border-radius); + background-color: var(--uui-color-surface); + cursor: move; + display: flex; + align-items: baseline; } + .remove-row-button, .remove-group-button { - position: absolute; - top: -4px; - right: -4px; display: none; } - .group:hover .remove-group-button { - display: block; - } - - .remove-row-button { + .remove-group-button { position: absolute; - left: -25px; - top: 8px; - display: none; + top: -26px; + left: 50%; + transform: translateX(-50%); + z-index: 1; } - .row:hover .remove-row-button { - display: block; + + .row:hover .remove-row-button:not(.hidden), + .group:hover .remove-group-button:not(.hidden) { + display: flex; } `, ]; diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts deleted file mode 100644 index b978935be1..0000000000 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ /dev/null @@ -1,340 +0,0 @@ -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { customElement, css, html, property, repeat, nothing, state } from '@umbraco-cms/backoffice/external/lit'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; - -type Extension = { - alias: string; - label: string; - icon?: string; -}; - -type TestServerValue = Array<{ - alias: string; - position?: [number, number, number]; -}>; - -@customElement('umb-tiptap-toolbar-groups-configuration2') -export class UmbTiptapToolbarGroupsConfiguration2Element extends UmbLitElement { - @property({ attribute: false }) - set value(value: TestServerValue) { - // if (this.#originalFormat === value) return; - // TODO: also check if the added values have positions, if not, there's no need to update the structured data. - this.#originalFormat = value; - this._structuredData = this.toStructuredData(value); - } - - get value(): TestServerValue { - return this.#originalFormat; - } - - @property({ attribute: false }) - extensionConfigs: Extension[] = []; - - //TODO: Use the context again so that we can remove items from the extensions list from here. - - @state() - _structuredData: string[][][] = [[[]]]; - - #originalFormat: TestServerValue = []; - - #currentDragAlias?: string; - - #onDragStart = (event: DragEvent, alias: string) => { - this.#currentDragAlias = alias; - event.dataTransfer!.effectAllowed = 'move'; - }; - - #onDragOver = (event: DragEvent) => { - event.preventDefault(); - event.dataTransfer!.dropEffect = 'move'; - }; - - #onDragEnd = (event: DragEvent) => { - event.preventDefault(); - if (event.dataTransfer?.dropEffect === 'none') { - const fromPos = this.#originalFormat.find((item) => item.alias === this.#currentDragAlias)?.position; - if (!fromPos) return; - - this.removeItem(fromPos); - } - }; - - #onDrop = (event: DragEvent, toPos: [number, number, number]) => { - event.preventDefault(); - const fromPos = this.#originalFormat.find((item) => item.alias === this.#currentDragAlias)?.position; - - if (fromPos) { - this.moveItem(fromPos, toPos); - } else if (this.#currentDragAlias) { - this.insertItem(this.#currentDragAlias, toPos); - } - }; - - private moveItem = (from: [number, number, number], to: [number, number, number]) => { - const [rowIndex, groupIndex, itemIndex] = from; - - // Get the item to move from the 'from' position - const itemToMove = this._structuredData[rowIndex][groupIndex][itemIndex]; - - // Remove the item from the original position - this._structuredData[rowIndex][groupIndex].splice(itemIndex, 1); - - this.insertItem(itemToMove, to); - }; - - private insertItem = (alias: string, toPos: [number, number, number]) => { - const [rowIndex, groupIndex, itemIndex] = toPos; - // Insert the item into the new position - this._structuredData[rowIndex][groupIndex].splice(itemIndex, 0, alias); - this.#updateOriginalFormat(); - - this.requestUpdate('_structuredData'); - this.dispatchEvent(new UmbChangeEvent()); - }; - - private removeItem(from: [number, number, number]) { - const [rowIndex, groupIndex, itemIndex] = from; - this._structuredData[rowIndex][groupIndex].splice(itemIndex, 1); - - this.#updateOriginalFormat(); - - this.requestUpdate('_structuredData'); - this.dispatchEvent(new UmbChangeEvent()); - } - - #addGroup = (rowIndex: number, groupIndex: number) => { - this._structuredData[rowIndex].splice(groupIndex, 0, []); - this.requestUpdate('_structuredData'); - }; - - #removeGroup = (rowIndex: number, groupIndex: number) => { - if (rowIndex === 0 && groupIndex === 0) { - // Prevent removing the last group - this._structuredData[rowIndex][groupIndex] = []; - } else { - this._structuredData[rowIndex].splice(groupIndex, 1); - } - this.requestUpdate('_structuredData'); - this.#updateOriginalFormat(); - }; - - #addRow = (rowIndex: number) => { - this._structuredData.splice(rowIndex, 0, [[]]); - this.requestUpdate('_structuredData'); - }; - - #removeRow = (rowIndex: number) => { - if (rowIndex === 0) { - // Prevent removing the last row - this._structuredData[rowIndex] = [[]]; - } else { - this._structuredData.splice(rowIndex, 1); - } - this.requestUpdate('_structuredData'); - this.#updateOriginalFormat(); - }; - - #updateOriginalFormat() { - this.#originalFormat = this.toOriginalFormat(this._structuredData); - this.dispatchEvent(new UmbChangeEvent()); - } - - private renderItem(alias: string) { - const extension = this.extensionConfigs.find((ext) => ext.alias === alias); - if (!extension) return nothing; - return html`
    this.#onDragStart(e, alias)}> - -
    `; - } - - private renderGroup(group: string[], rowIndex: number, groupIndex: number) { - return html` -
    this.#onDrop(e, [rowIndex, groupIndex, group.length])}> - ${group.map((alias) => this.renderItem(alias))} - this.#removeGroup(rowIndex, groupIndex)}> - - -
    - `; - } - - private renderRow(row: string[][], rowIndex: number) { - return html` -
    - ${repeat(row, (group, groupIndex) => this.renderGroup(group, rowIndex, groupIndex))} - this.#addGroup(rowIndex, row.length)}>+ - this.#removeRow(rowIndex)}> - - -
    - `; - } - - override render() { - return html` -

    - WIP Feature Rows, groups, and item order have no effect yet.
    - However, adding and removing items from the toolbar is functional. Additionally, hiding items from the toolbar - while retaining their functionality by excluding them from the toolbar layout is also functional. -

    - ${repeat(this._structuredData, (row, rowIndex) => this.renderRow(row, rowIndex))} - this.#addRow(this._structuredData.length)}>+ - -

    Extensions hidden from the toolbar

    -
    - ${this.#originalFormat?.filter((item) => !item.position).map((item) => this.renderItem(item.alias))} -
    - `; - } - - toStructuredData = (data: TestServerValue) => { - if (!data?.length) return [[[]]]; - - const structuredData: string[][][] = [[[]]]; - data.forEach(({ alias, position }) => { - if (!position) return; - - const [rowIndex, groupIndex, aliasIndex] = position; - - while (structuredData.length <= rowIndex) { - structuredData.push([]); - } - - const currentRow = structuredData[rowIndex]; - - while (currentRow.length <= groupIndex) { - currentRow.push([]); - } - - const currentGroup = currentRow[groupIndex]; - - currentGroup[aliasIndex] = alias; - }); - - return structuredData; - }; - - toOriginalFormat = (structuredData: string[][][]) => { - const originalData: TestServerValue = []; - - structuredData.forEach((row, rowIndex) => { - row.forEach((group, groupIndex) => { - group.forEach((alias, aliasIndex) => { - if (alias) { - originalData.push({ - alias, - position: [rowIndex, groupIndex, aliasIndex], - }); - } - }); - }); - }); - - // add items from this.#originalFormat only if they are not already in the structured data. and if they have a position property set, unset it. - this.#originalFormat.forEach((item) => { - if (!originalData.some((i) => i.alias === item.alias)) { - originalData.push({ - alias: item.alias, - }); - } - }); - - // TODO: this code removes the items completely, while the one above just puts them back into the hidden extensions list. Which one do we prefer? - // this.#originalFormat.forEach((item) => { - // if (!item.position) { - // const exists = originalData.find((i) => i.alias === item.alias); - // if (!exists) { - // originalData.push(item); - // } - // } - // }); - - return originalData; - }; - - static override styles = [ - UmbTextStyles, - css` - :host { - display: flex; - flex-direction: column; - gap: 6px; - } - .hidden-extensions { - display: flex; - gap: 6px; - } - .hidden-extensions-header { - margin-bottom: 3px; - } - .row { - position: relative; - display: flex; - gap: 12px; - } - .group { - position: relative; - display: flex; - gap: 3px; - border-radius: var(--uui-border-radius); - background-color: var(--uui-color-surface-alt); - padding: 6px; - min-height: 30px; - min-width: 30px; - } - .item { - padding: var(--uui-size-space-2); - border: 1px solid var(--uui-color-border); - border-radius: var(--uui-border-radius); - background-color: var(--uui-color-surface); - cursor: move; - display: flex; - align-items: baseline; - } - - .remove-row-button, - .remove-group-button { - display: none; - } - .remove-group-button { - position: absolute; - top: -26px; - left: 50%; - transform: translateX(-50%); - z-index: 1; - } - - .row:hover .remove-row-button:not(.hidden), - .group:hover .remove-group-button:not(.hidden) { - display: flex; - } - `, - ]; -} - -export default UmbTiptapToolbarGroupsConfiguration2Element; - -declare global { - interface HTMLElementTagNameMap { - 'umb-tiptap-toolbar-groups-configuration2': UmbTiptapToolbarGroupsConfiguration2Element; - } -} From f5251a0e4c3b3f300827f9fba5e48dadd7158998 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:37:39 +0200 Subject: [PATCH 152/241] cleanup --- src/packages/rte/tiptap/property-editors/tiptap/manifests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 1e00e10acc..29db309aa0 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -1,4 +1,4 @@ -import { umbExtensionsRegistry, type ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; +import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; export const manifests: Array = [ { From 6519068c07c48b70800dac491ee8c7791ccd5002 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:04:13 +0200 Subject: [PATCH 153/241] cleanup --- ...=> input-tiptap-toolbar-layout.element.ts} | 22 +++++-- ...ui-tiptap-toolbar-configuration.element.ts | 7 +-- .../property-editors/tiptap/manifests.ts | 60 ------------------- 3 files changed, 19 insertions(+), 70 deletions(-) rename src/packages/rte/tiptap/property-editors/{tiptap-toolbar-groups-configuration.element.ts => input-tiptap-toolbar-layout.element.ts} (94%) diff --git a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts similarity index 94% rename from src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts rename to src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts index 9a04507cb3..e274abda5b 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts @@ -14,7 +14,7 @@ type TestServerValue = Array<{ position?: [number, number, number]; }>; -@customElement('umb-tiptap-toolbar-groups-configuration') +@customElement('umb-input-tiptap-toolbar-layout') export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { @property({ attribute: false }) set value(value: TestServerValue) { @@ -198,11 +198,21 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement {

    ${repeat(this._structuredData, (row, rowIndex) => this.renderRow(row, rowIndex))} this.#addRow(this._structuredData.length)}>+ + ${this.#renderHiddenExtensions()} + `; + } -

    Extensions hidden from the toolbar

    -
    - ${this.#originalFormat?.filter((item) => !item.position).map((item) => this.renderItem(item.alias))} -
    + #renderHiddenExtensions() { + const hiddenExtensions = this.#originalFormat?.filter((item) => !item.position); + + if (!hiddenExtensions?.length) return nothing; + + return html` +

    + Extensions hidden from the toolbar
    Drag and drop buttons into the toolbar to add them +

    + +
    ${hiddenExtensions.map((item) => this.renderItem(item.alias))}
    `; } @@ -335,6 +345,6 @@ export default UmbTiptapToolbarGroupsConfigurationElement; declare global { interface HTMLElementTagNameMap { - 'umb-tiptap-toolbar-groups-configuration': UmbTiptapToolbarGroupsConfigurationElement; + 'umb-input-tiptap-toolbar-layout': UmbTiptapToolbarGroupsConfigurationElement; } } diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index f03d85412a..cf90d1a615 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -1,4 +1,4 @@ -import type UmbTiptapToolbarGroupsConfigurationElement from './tiptap-toolbar-groups-configuration.element.js'; +import type UmbTiptapToolbarGroupsConfigurationElement from './input-tiptap-toolbar-layout.element.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import { customElement, css, html, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; @@ -9,7 +9,7 @@ import { type UmbPropertyEditorConfigCollection, } from '@umbraco-cms/backoffice/property-editor'; -import './tiptap-toolbar-groups-configuration.element.js'; +import './input-tiptap-toolbar-layout.element.js'; // If an extension does not have a position, it is considered hidden in the toolbar type TestServerValue = Array<{ @@ -65,7 +65,6 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement super.firstUpdated(_changedProperties); this.observe(umbExtensionsRegistry.byType('tiptapExtension'), (extensions) => { - console.log('extensions', extensions); this._extensionConfigs = extensions.map((ext) => { return { alias: ext.alias, @@ -134,7 +133,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement override render() { return html` - +
    ${repeat( this._extensionCategories, diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 29db309aa0..a263cdb25c 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -19,66 +19,6 @@ export const manifests: Array = [ description: 'Pick the toolbar options that should be available when editing', propertyEditorUiAlias: 'Umb.PropertyEditorUi.Tiptap.ToolbarConfiguration', weight: 10, - // config: [ - // { - // alias: 'toolbar', - // value: [ - // // Clipboard Group - // { alias: 'undo', label: 'Undo', icon: 'undo', category: 'clipboard' }, - // { alias: 'redo', label: 'Redo', icon: 'redo', category: 'clipboard' }, - // { alias: 'cut', label: 'Cut', icon: 'cut', category: 'clipboard' }, - // { alias: 'copy', label: 'Copy', icon: 'copy', category: 'clipboard' }, - // { alias: 'paste', label: 'Paste', icon: 'paste', category: 'clipboard' }, - - // // Formatting Group - // { alias: 'bold', label: 'Bold', icon: 'bold', category: 'formatting' }, - // { alias: 'italic', label: 'Italic', icon: 'italic', category: 'formatting' }, - // { alias: 'underline', label: 'Underline', icon: 'underline', category: 'formatting' }, - // { alias: 'strikethrough', label: 'Strikethrough', icon: 'strike-through', category: 'formatting' }, - // { alias: 'removeformat', label: 'Remove format', icon: 'remove-formatting', category: 'formatting' }, - - // // Color Group - // { alias: 'forecolor', label: 'Text color', icon: 'text-color', category: 'color' }, - // { alias: 'backcolor', label: 'Background color', icon: 'highlight-bg-color', category: 'color' }, - - // // Alignment Group - // { alias: 'alignleft', label: 'Align left', icon: 'align-left', category: 'alignment' }, - // { alias: 'aligncenter', label: 'Align center', icon: 'align-center', category: 'alignment' }, - // { alias: 'alignright', label: 'Align right', icon: 'align-right', category: 'alignment' }, - // { alias: 'alignjustify', label: 'Justify justify', icon: 'align-justify', category: 'alignment' }, - - // // List Group - // { alias: 'bullist', label: 'Bullet list', icon: 'unordered-list', category: 'list' }, - // { alias: 'numlist', label: 'Numbered list', icon: 'ordered-list', category: 'list' }, - - // // Indentation Group - // { alias: 'outdent', label: 'Outdent', icon: 'outdent', category: 'indentation' }, - // { alias: 'indent', label: 'Indent', icon: 'indent', category: 'indentation' }, - - // // Insert Elements Group - // { alias: 'anchor', label: 'Anchor', icon: 'bookmark', category: 'insert' }, - // { alias: 'table', label: 'Table', icon: 'table', category: 'insert' }, - // { alias: 'hr', label: 'Horizontal rule', icon: 'horizontal-rule', category: 'insert' }, - // { alias: 'charmap', label: 'Character map', icon: 'insert-character', category: 'insert' }, - - // // Direction Group - // { alias: 'rtl', label: 'Right to left', icon: 'rtl', category: 'direction' }, - // { alias: 'ltr', label: 'Left to right', icon: 'ltr', category: 'direction' }, - - // // Text Transformation Group - // { alias: 'subscript', label: 'Subscript', icon: 'subscript', category: 'text-transformation' }, - // { alias: 'superscript', label: 'Superscript', icon: 'superscript', category: 'text-transformation' }, - - // // Styling and Font Group - // { alias: 'fontname', label: 'Font select', icon: 'text-color', category: 'styling' }, - // { alias: 'fontsize', label: 'Font size', icon: 'text-color', category: 'styling' }, - - // // Block Element Group - // { alias: 'blockquote', label: 'Blockquote', icon: 'quote', category: 'block-elements' }, - // { alias: 'formatblock', label: 'Format block', icon: 'format', category: 'block-elements' }, - // ], - // }, - // ], }, { alias: 'maxWidth', From f8d02771355f00a2a95a24cda4aa8fbfb9f69a34 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:21:56 +0200 Subject: [PATCH 154/241] cleanup --- .../input-tiptap-toolbar-layout.element.ts | 52 +++++++------------ .../property-editors/tiptap/manifests.ts | 2 +- 2 files changed, 21 insertions(+), 33 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts b/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts index e274abda5b..e5e30948d0 100644 --- a/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts +++ b/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts @@ -18,10 +18,10 @@ type TestServerValue = Array<{ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { @property({ attribute: false }) set value(value: TestServerValue) { - // if (this.#originalFormat === value) return; + if (this.#originalFormat === value) return; // TODO: also check if the added values have positions, if not, there's no need to update the structured data. this.#originalFormat = value; - this._structuredData = this.toStructuredData(value); + this._structuredData = this.#toStructuredData(value); } get value(): TestServerValue { @@ -31,8 +31,6 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { @property({ attribute: false }) extensionConfigs: Extension[] = []; - //TODO: Use the context again so that we can remove items from the extensions list from here. - @state() _structuredData: string[][][] = [[[]]]; @@ -56,7 +54,7 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { const fromPos = this.#originalFormat.find((item) => item.alias === this.#currentDragAlias)?.position; if (!fromPos) return; - this.removeItem(fromPos); + this.#moveItemToHiddenExtensions(fromPos); } }; @@ -65,13 +63,13 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { const fromPos = this.#originalFormat.find((item) => item.alias === this.#currentDragAlias)?.position; if (fromPos) { - this.moveItem(fromPos, toPos); + this.#moveItem(fromPos, toPos); } else if (this.#currentDragAlias) { - this.insertItem(this.#currentDragAlias, toPos); + this.#insertItem(this.#currentDragAlias, toPos); } }; - private moveItem = (from: [number, number, number], to: [number, number, number]) => { + #moveItem = (from: [number, number, number], to: [number, number, number]) => { const [rowIndex, groupIndex, itemIndex] = from; // Get the item to move from the 'from' position @@ -80,10 +78,10 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { // Remove the item from the original position this._structuredData[rowIndex][groupIndex].splice(itemIndex, 1); - this.insertItem(itemToMove, to); + this.#insertItem(itemToMove, to); }; - private insertItem = (alias: string, toPos: [number, number, number]) => { + #insertItem = (alias: string, toPos: [number, number, number]) => { const [rowIndex, groupIndex, itemIndex] = toPos; // Insert the item into the new position this._structuredData[rowIndex][groupIndex].splice(itemIndex, 0, alias); @@ -93,7 +91,7 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { this.dispatchEvent(new UmbChangeEvent()); }; - private removeItem(from: [number, number, number]) { + #moveItemToHiddenExtensions(from: [number, number, number]) { const [rowIndex, groupIndex, itemIndex] = from; this._structuredData[rowIndex][groupIndex].splice(itemIndex, 1); @@ -136,11 +134,11 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { }; #updateOriginalFormat() { - this.#originalFormat = this.toOriginalFormat(this._structuredData); + this.#originalFormat = this.#toOriginalFormat(this._structuredData); this.dispatchEvent(new UmbChangeEvent()); } - private renderItem(alias: string) { + #renderItem(alias: string) { const extension = this.extensionConfigs.find((ext) => ext.alias === alias); if (!extension) return nothing; return html`
    `; } - private renderGroup(group: string[], rowIndex: number, groupIndex: number) { + #renderGroup(group: string[], rowIndex: number, groupIndex: number) { return html`
    this.#onDrop(e, [rowIndex, groupIndex, group.length])}> - ${group.map((alias) => this.renderItem(alias))} + ${group.map((alias) => this.#renderItem(alias))} - ${repeat(row, (group, groupIndex) => this.renderGroup(group, rowIndex, groupIndex))} + ${repeat(row, (group, groupIndex) => this.#renderGroup(group, rowIndex, groupIndex))} this.#addGroup(rowIndex, row.length)}>+ - ${repeat(this._structuredData, (row, rowIndex) => this.renderRow(row, rowIndex))} + ${repeat(this._structuredData, (row, rowIndex) => this.#renderRow(row, rowIndex))} this.#addRow(this._structuredData.length)}>+ ${this.#renderHiddenExtensions()} `; @@ -212,11 +210,11 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { Extensions hidden from the toolbar
    Drag and drop buttons into the toolbar to add them

    -
    ${hiddenExtensions.map((item) => this.renderItem(item.alias))}
    +
    ${hiddenExtensions.map((item) => this.#renderItem(item.alias))}
    `; } - toStructuredData = (data: TestServerValue) => { + #toStructuredData(data: TestServerValue) { if (!data?.length) return [[[]]]; const structuredData: string[][][] = [[[]]]; @@ -241,9 +239,9 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { }); return structuredData; - }; + } - toOriginalFormat = (structuredData: string[][][]) => { + #toOriginalFormat = (structuredData: string[][][]) => { const originalData: TestServerValue = []; structuredData.forEach((row, rowIndex) => { @@ -268,16 +266,6 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { } }); - // TODO: this code removes the items completely, while the one above just puts them back into the hidden extensions list. Which one do we prefer? - // this.#originalFormat.forEach((item) => { - // if (!item.position) { - // const exists = originalData.find((i) => i.alias === item.alias); - // if (!exists) { - // originalData.push(item); - // } - // } - // }); - return originalData; }; diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index a263cdb25c..5de70b8af0 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -15,7 +15,7 @@ export const manifests: Array = [ properties: [ { alias: 'toolbar', - label: 'Toolbar - NOT IMPLEMENTED', + label: 'Toolbar', description: 'Pick the toolbar options that should be available when editing', propertyEditorUiAlias: 'Umb.PropertyEditorUi.Tiptap.ToolbarConfiguration', weight: 10, From b10b89e14974b2c0eaf37d75ead9cfa89501a614 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:43:31 +0200 Subject: [PATCH 155/241] cleanup --- .../input-tiptap-toolbar-layout.element.ts | 2 +- ...y-editor-ui-tiptap-toolbar-configuration.element.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts b/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts index e5e30948d0..b7a404fd83 100644 --- a/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts +++ b/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts @@ -257,7 +257,7 @@ export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { }); }); - // add items from this.#originalFormat only if they are not already in the structured data. and if they have a position property set, unset it. + // Add the hidden extensions so they are not lost this.#originalFormat.forEach((item) => { if (!originalData.some((i) => i.alias === item.alias)) { originalData.push({ diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index cf90d1a615..c70e4c0518 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -24,7 +24,7 @@ type ExtensionConfig = { category: string; }; -type Extension = { +type ExtensionCategoryItem = { alias: string; label: string; icon?: string; @@ -33,7 +33,7 @@ type Extension = { type ExtensionCategory = { category: string; - extensions: Extension[]; + extensions: ExtensionCategoryItem[]; }; @customElement('umb-property-editor-ui-tiptap-toolbar-configuration') @@ -78,14 +78,14 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement } #setupExtensionCategories() { - const withSelected = this._extensionConfigs.map((v) => { + const withSelectedProperty = this._extensionConfigs.map((v) => { return { ...v, selected: this.value?.some((item) => item.alias === v.alias), }; }); - const grouped = withSelected.reduce((acc: any, item) => { + const grouped = withSelectedProperty.reduce((acc: any, item) => { const group = item.category || 'miscellaneous'; // Assign to "miscellaneous" if no group if (!acc[group]) { acc[group] = []; @@ -99,7 +99,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement })); } - #onExtensionSelect(item: Extension) { + #onExtensionSelect(item: ExtensionCategoryItem) { item.selected = !item.selected; if (item.selected) { From d48348c9f295b00fe1ae91efa11418a852799456 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 07:57:05 +0200 Subject: [PATCH 156/241] fix test that failed because of self-import --- .../media/media/components/input-media/input-media.test.ts | 1 + .../media-caption-alt-text-modal.element.ts | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/packages/media/media/components/input-media/input-media.test.ts b/src/packages/media/media/components/input-media/input-media.test.ts index 0a0fe51e55..3fed06548a 100644 --- a/src/packages/media/media/components/input-media/input-media.test.ts +++ b/src/packages/media/media/components/input-media/input-media.test.ts @@ -1,6 +1,7 @@ import { UmbInputMediaElement } from './input-media.element.js'; import { expect, fixture, html } from '@open-wc/testing'; import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; + describe('UmbInputMediaElement', () => { let element: UmbInputMediaElement; diff --git a/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts b/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts index 48acc80eb5..06ad404ab8 100644 --- a/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts +++ b/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts @@ -1,11 +1,10 @@ +import { UmbMediaDetailRepository } from '../../repository/index.js'; import type { UmbMediaCaptionAltTextModalData, UmbMediaCaptionAltTextModalValue, } from './media-caption-alt-text-modal.token.js'; import { css, html, customElement } from '@umbraco-cms/backoffice/external/lit'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; -import '@umbraco-cms/backoffice/block-type'; -import { UmbMediaDetailRepository } from '@umbraco-cms/backoffice/media'; import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; @customElement('umb-media-caption-alt-text-modal') @@ -27,7 +26,7 @@ export class UmbMediaCaptionAltTextModalElement extends UmbModalBaseElement< const { data } = await this.#mediaDetailRepository.requestByUnique(this.#mediaUnique); if (!data) return; - this.value = { ...this.value, altText: this.value.altText ?? data.variants[0].name, url: data.urls[0]?.url ?? '' }; + this.value = { ...this.value, altText: this.value?.altText ?? data.variants[0].name, url: data.urls[0]?.url ?? '' }; } override render() { From b9b816cbb4afa9c198e2dc962ffbd25985031f7c Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 07:57:22 +0200 Subject: [PATCH 157/241] feat: remove deprecated UmbMEdiaPickerContext --- .../media/media/components/input-media/input-media.context.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/packages/media/media/components/input-media/input-media.context.ts b/src/packages/media/media/components/input-media/input-media.context.ts index fe8fcec48b..1fabdd6526 100644 --- a/src/packages/media/media/components/input-media/input-media.context.ts +++ b/src/packages/media/media/components/input-media/input-media.context.ts @@ -15,6 +15,3 @@ export class UmbMediaPickerInputContext extends UmbPickerInputContext< super(host, UMB_MEDIA_ITEM_REPOSITORY_ALIAS, UMB_MEDIA_PICKER_MODAL); } } - -/** @deprecated Use `UmbMediaPickerInputContext` instead. This method will be removed in Umbraco 15. */ -export { UmbMediaPickerInputContext as UmbMediaPickerContext }; From 9da46b686cd036a301bd093d20a316555d5fc03c Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 07:57:33 +0200 Subject: [PATCH 158/241] fix: allow tinymce to restore altText and caption --- .../tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index b8ab127669..bc53032243 100644 --- a/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts @@ -156,14 +156,21 @@ export default class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase { const { selection } = await modalHandler.onSubmit().catch(() => ({ selection: undefined })); if (!selection || !selection.length) return; - this.#showMediaCaptionAltText(selection[0]); + this.#showMediaCaptionAltText(selection[0], currentTarget); this.editor.dispatch('Change'); } - async #showMediaCaptionAltText(mediaUnique: string | null) { + async #showMediaCaptionAltText(mediaUnique: string | null, currentTarget: MediaPickerTargetData) { if (!mediaUnique) return; - const modalHandler = this.#modalManager?.open(this, UMB_MEDIA_CAPTION_ALT_TEXT_MODAL, { data: { mediaUnique } }); + const modalHandler = this.#modalManager?.open(this, UMB_MEDIA_CAPTION_ALT_TEXT_MODAL, { + data: { mediaUnique }, + value: { + url: '', + altText: currentTarget.altText, + caption: currentTarget.caption, + }, + }); const mediaData = await modalHandler?.onSubmit().catch(() => null); if (!mediaData) return; From d44628c9d518f8bda0bac1eb6180c99205ca7810 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Wed, 25 Sep 2024 18:04:33 +0100 Subject: [PATCH 159/241] Tiptap extension: Link/Unlink uses Umbraco URL Picker modal --- .../extensions/tiptap-umb-link.extension.ts | 54 +++++++++++ src/external/tiptap/index.ts | 1 + .../block-rte/tiptap-extension/manifests.ts | 1 + .../core/icon-registry/icon-dictionary.json | 4 + src/packages/core/icon-registry/icons.ts | 4 + .../core/icon-registry/icons/icon-unlink.ts | 19 ++++ .../tiptap-toolbar-button-disabled.element.ts | 33 +++++++ .../toolbar/tiptap-toolbar-button.element.ts | 6 +- .../rte/tiptap/extensions/core/manifests.ts | 14 +++ .../extensions/core/unlink.extension.ts | 14 +++ .../rte/tiptap/extensions/manifests.ts | 54 +++++------ src/packages/rte/tiptap/extensions/types.ts | 6 +- .../tiptap/extensions/umb/link.extension.ts | 91 +++++++++++++++++++ .../extensions/umb/urlpicker.extension.ts | 27 ------ 14 files changed, 269 insertions(+), 59 deletions(-) create mode 100644 src/external/tiptap/extensions/tiptap-umb-link.extension.ts create mode 100644 src/packages/core/icon-registry/icons/icon-unlink.ts create mode 100644 src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button-disabled.element.ts create mode 100644 src/packages/rte/tiptap/extensions/core/unlink.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/umb/link.extension.ts delete mode 100644 src/packages/rte/tiptap/extensions/umb/urlpicker.extension.ts diff --git a/src/external/tiptap/extensions/tiptap-umb-link.extension.ts b/src/external/tiptap/extensions/tiptap-umb-link.extension.ts new file mode 100644 index 0000000000..1576004644 --- /dev/null +++ b/src/external/tiptap/extensions/tiptap-umb-link.extension.ts @@ -0,0 +1,54 @@ +import Link from '@tiptap/extension-link'; + +export const UmbLink = Link.extend({ + name: 'umbLink', + + addAttributes() { + return { + ...this.parent?.(), + 'data-anchor': { default: null }, + title: { default: null }, + type: { default: 'external' }, + }; + }, + + addOptions() { + return { + ...this.parent?.(), + HTMLAttributes: { + target: '', + }, + }; + }, + + addCommands() { + return { + setUmbLink: (attributes) => { + return ({ chain }) => { + return chain().setMark(this.name, attributes).setMeta('preventAutolink', true).run(); + }; + }, + unsetUmbLink: () => { + return ({ chain }) => { + return chain().unsetMark(this.name, { extendEmptyMarkRange: true }).setMeta('preventAutolink', true).run(); + }; + }, + }; + }, +}); + +declare module '@tiptap/core' { + interface Commands { + umbLink: { + setUmbLink: (options: { + type: string; + href: string; + 'data-anchor'?: string | null; + target?: string | null; + title?: string | null; + }) => ReturnType; + + unsetUmbLink: () => ReturnType; + }; + } +} diff --git a/src/external/tiptap/index.ts b/src/external/tiptap/index.ts index 92b7ceddbb..e17d84cb3a 100644 --- a/src/external/tiptap/index.ts +++ b/src/external/tiptap/index.ts @@ -34,3 +34,4 @@ export * from './extensions/tiptap-umb-embedded-media.extension.js'; export * from './extensions/tiptap-figcaption.extension.js'; export * from './extensions/tiptap-figure.extension.js'; export * from './extensions/tiptap-umb-image.extension.js'; +export * from './extensions/tiptap-umb-link.extension.js'; diff --git a/src/packages/block/block-rte/tiptap-extension/manifests.ts b/src/packages/block/block-rte/tiptap-extension/manifests.ts index 2181248d85..5b539cfb80 100644 --- a/src/packages/block/block-rte/tiptap-extension/manifests.ts +++ b/src/packages/block/block-rte/tiptap-extension/manifests.ts @@ -7,6 +7,7 @@ export const manifests: ManifestTiptapExtensionButtonKind[] = [ alias: 'Umb.TiptapExtension.BlockPicker', name: 'Block Picker Tiptap Extension Button', api: () => import('./block-picker.extension.js'), + weight: 90, meta: { alias: 'umbblockpicker', icon: 'icon-plugin', diff --git a/src/packages/core/icon-registry/icon-dictionary.json b/src/packages/core/icon-registry/icon-dictionary.json index 9c53e18410..450cc944d9 100644 --- a/src/packages/core/icon-registry/icon-dictionary.json +++ b/src/packages/core/icon-registry/icon-dictionary.json @@ -2161,6 +2161,10 @@ "name": "icon-underline", "file": "underline.svg" }, + { + "name": "icon-unlink", + "file": "unlink.svg" + }, { "name": "icon-unlocked", "file": "lock-open.svg" diff --git a/src/packages/core/icon-registry/icons.ts b/src/packages/core/icon-registry/icons.ts index 1f55c16a7f..0b0df45501 100644 --- a/src/packages/core/icon-registry/icons.ts +++ b/src/packages/core/icon-registry/icons.ts @@ -2035,6 +2035,10 @@ name: "icon-underline", path: () => import("./icons/icon-underline.js"), },{ +name: "icon-unlink", + +path: () => import("./icons/icon-unlink.js"), +},{ name: "icon-unlocked", path: () => import("./icons/icon-unlocked.js"), diff --git a/src/packages/core/icon-registry/icons/icon-unlink.ts b/src/packages/core/icon-registry/icons/icon-unlink.ts new file mode 100644 index 0000000000..d770affee6 --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-unlink.ts @@ -0,0 +1,19 @@ +export default ` + + + + + + + + +`; \ No newline at end of file diff --git a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button-disabled.element.ts b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button-disabled.element.ts new file mode 100644 index 0000000000..a2311fa601 --- /dev/null +++ b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button-disabled.element.ts @@ -0,0 +1,33 @@ +import { UmbTiptapToolbarButtonElement } from './tiptap-toolbar-button.element.js'; +import { customElement, html, ifDefined, when } from '@umbraco-cms/backoffice/external/lit'; + +const elementName = 'umb-tiptap-toolbar-button-disabled'; + +@customElement(elementName) +export class UmbTiptapToolbarButtonDisabledElement extends UmbTiptapToolbarButtonElement { + override render() { + return html` + (this.api && this.editor ? this.api.execute(this.editor) : null)}> + ${when( + this.manifest?.meta.icon, + () => html``, + () => html`${this.manifest?.meta.label}`, + )} + + `; + } +} + +export { UmbTiptapToolbarButtonDisabledElement as element }; + +declare global { + interface HTMLElementTagNameMap { + [elementName]: UmbTiptapToolbarButtonDisabledElement; + } +} diff --git a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts index e7b2fd0b81..c193554a81 100644 --- a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts +++ b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts @@ -13,7 +13,7 @@ export class UmbTiptapToolbarButtonElement extends UmbLitElement { public manifest?: ManifestTiptapExtensionButtonKind; @state() - private _isActive = false; + protected isActive = false; override connectedCallback() { super.connectedCallback(); @@ -35,7 +35,7 @@ export class UmbTiptapToolbarButtonElement extends UmbLitElement { readonly #onEditorUpdate = () => { if (this.api && this.editor && this.manifest) { - this._isActive = this.api.isActive(this.editor); + this.isActive = this.api.isActive(this.editor); } }; @@ -43,7 +43,7 @@ export class UmbTiptapToolbarButtonElement extends UmbLitElement { return html` (this.api && this.editor ? this.api.execute(this.editor) : null)}> diff --git a/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/packages/rte/tiptap/extensions/core/manifests.ts index 309598a14f..3f4b66173e 100644 --- a/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/packages/rte/tiptap/extensions/core/manifests.ts @@ -243,4 +243,18 @@ export const manifests: Array import('./unlink.extension.js'), + element: () => import('../../components/toolbar/tiptap-toolbar-button-disabled.element.js'), + weight: 101, + meta: { + alias: 'unlink', + icon: 'icon-unlink', + label: 'Unlink', + }, + }, ]; diff --git a/src/packages/rte/tiptap/extensions/core/unlink.extension.ts b/src/packages/rte/tiptap/extensions/core/unlink.extension.ts new file mode 100644 index 0000000000..5645c154bc --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/unlink.extension.ts @@ -0,0 +1,14 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapUnlinkExtensionApi extends UmbTiptapToolbarElementApiBase { + getTiptapExtensions() { + return []; + } + + override isActive = (editor?: Editor) => editor?.isActive('umbLink') ?? false; + + override execute(editor?: Editor) { + editor?.chain().focus().unsetUmbLink().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index 827e80d2fa..7fc3abe7c2 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -14,7 +14,6 @@ const kinds: Array = [ }, ]; -// TODO: [LK] Move each of these to their corresponding packages, e.g. "code-editor", "embedded-media", "media", "multi-url-picker" const umbExtensions: Array = [ { type: 'tiptapExtension', @@ -29,16 +28,29 @@ const umbExtensions: Array import('./umb/media-upload.extension.js'), + weight: 900, + meta: { + alias: 'umbMediaUpload', + icon: 'icon-image-up', + label: 'Media upload', + }, + }, { type: 'tiptapExtension', kind: 'button', - alias: 'Umb.Tiptap.Embed', - name: 'Embed Tiptap Extension', - api: () => import('./umb/embedded-media.extension.js'), + alias: 'Umb.Tiptap.Link', + name: 'Link Tiptap Extension', + api: () => import('./umb/link.extension.js'), + weight: 102, meta: { - alias: 'umb-embedded-media', - icon: 'icon-embed', - label: '#general_embed', + alias: 'umbLink', + icon: 'icon-link', + label: '#defaultdialogs_urlLinkPicker', }, }, { @@ -47,34 +59,24 @@ const umbExtensions: Array import('./umb/mediapicker.extension.js'), + weight: 80, meta: { - alias: 'umb-media', + alias: 'umbMedia', icon: 'icon-picture', label: 'Media picker', }, }, - { - type: 'tiptapExtension', - alias: 'Umb.Tiptap.MediaUpload', - name: 'Media Upload Tiptap Extension', - weight: 900, - api: () => import('./umb/media-upload.extension.js'), - meta: { - alias: 'umb-media-upload', - icon: 'icon-image-up', - label: 'Media upload', - }, - }, { type: 'tiptapExtension', kind: 'button', - alias: 'Umb.Tiptap.UrlPicker', - name: 'URL Picker Tiptap Extension', - api: () => import('./umb/urlpicker.extension.js'), + alias: 'Umb.Tiptap.Embed', + name: 'Embed Tiptap Extension', + api: () => import('./umb/embedded-media.extension.js'), + weight: 70, meta: { - alias: 'umb-link', - icon: 'icon-link', - label: 'URL picker', + alias: 'umbEmbeddedMedia', + icon: 'icon-embed', + label: '#general_embed', }, }, ]; diff --git a/src/packages/rte/tiptap/extensions/types.ts b/src/packages/rte/tiptap/extensions/types.ts index 2ab9a0179e..919bd3092b 100644 --- a/src/packages/rte/tiptap/extensions/types.ts +++ b/src/packages/rte/tiptap/extensions/types.ts @@ -20,7 +20,7 @@ export abstract class UmbTiptapExtensionApiBase extends UmbControllerBase implem /** * The manifest for the extension. */ - protected _manifest?: ManifestTiptapExtension; + protected manifest?: ManifestTiptapExtension; /** * The editor instance. @@ -74,7 +74,7 @@ export abstract class UmbTiptapToolbarElementApiBase * Informs the toolbar element if it is active or not. It uses the manifest meta alias to check if the toolbar element is active. * @see {ManifestTiptapExtension} */ - public isActive(editor?: Editor) { - return editor && this._manifest?.meta.alias ? editor?.isActive(this._manifest.meta.alias) : false; + public isActive(editor: Editor) { + return editor && this.manifest?.meta.alias ? editor?.isActive(this.manifest.meta.alias) : false; } } diff --git a/src/packages/rte/tiptap/extensions/umb/link.extension.ts b/src/packages/rte/tiptap/extensions/umb/link.extension.ts new file mode 100644 index 0000000000..3ea28a010d --- /dev/null +++ b/src/packages/rte/tiptap/extensions/umb/link.extension.ts @@ -0,0 +1,91 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import { UmbLink } from '@umbraco-cms/backoffice/external/tiptap'; +import { UMB_LINK_PICKER_MODAL } from '@umbraco-cms/backoffice/multi-url-picker'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; +import type { UmbLinkPickerLink } from '@umbraco-cms/backoffice/multi-url-picker'; + +export default class UmbTiptapLinkExtensionApi extends UmbTiptapToolbarElementApiBase { + getTiptapExtensions() { + return [UmbLink.configure({ openOnClick: false })]; + } + + override async execute(editor?: Editor) { + const attrs = editor?.getAttributes(UmbLink.name) ?? {}; + const link = this.#getLinkData(attrs); + const data = { config: {}, index: null }; + const value = { link }; + + const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const modalHandler = modalManager.open(this, UMB_LINK_PICKER_MODAL, { data, value }); + + if (!modalHandler) return; + + const result = await modalHandler.onSubmit().catch(() => undefined); + if (!result?.link) return; + + const linkAttrs = this.#parseLinkData(result.link); + + if (linkAttrs) { + editor?.chain().focus().extendMarkRange(UmbLink.name).setUmbLink(linkAttrs).run(); + } else { + editor?.chain().focus().extendMarkRange(UmbLink.name).unsetLink().run(); + } + } + + #getLinkData(attrs: Record): UmbLinkPickerLink { + const queryString = attrs['data-anchor']; + const url = attrs.href?.substring(0, attrs.href.length - (queryString?.length ?? 0)); + const unique = url?.includes('localLink:') ? url.substring(url.indexOf(':') + 1, url.indexOf('}')) : null; + + return { + name: attrs.title, + queryString, + target: attrs.target, + type: attrs.type, + unique, + url, + }; + } + + #parseLinkData(link: UmbLinkPickerLink) { + const { name, target, type, unique } = link; + let { queryString, url } = link; + + // If an anchor exists, check that it is appropriately prefixed + if (queryString && !queryString?.startsWith('?') && !queryString?.startsWith('#')) { + queryString = (queryString?.startsWith('=') ? '#' : '?') + queryString; + } + + // The href might be an external url, so check the value for an anchor/querystring; + // `href` has the anchor re-appended later, hence the reset here to avoid duplicating the anchor + if (!queryString) { + const urlParts = url?.split(/(#|\?)/); + if (urlParts?.length === 3) { + url = urlParts[0]; + queryString = urlParts[1] + urlParts[2]; + } + } + + // If we have a unique id, it must be a `/{localLink:guid}` + if (unique) { + url = `/{localLink:${unique}}`; + } + + // If it's an email address and not `//user@domain.com` and protocol (e.g. mailto:, sip:) is not specified; + // then we'll assume it should be a "mailto" link. + if (url?.includes('@') && !url.includes('//') && !url.includes(':')) { + url = `mailto:${url}`; + } + + // If the URL is prefixed "www.", then prepend "http://" protocol scheme. + if (url && /^\s*www\./i.test(url)) { + url = `http://${url}`; + } + + const anchor = queryString?.startsWith('#') || queryString?.startsWith('?') ? queryString : null; + const href = url + (anchor ?? ''); + + return href ? { type: type ?? 'external', href, 'data-anchor': anchor, target, title: name ?? url } : null; + } +} diff --git a/src/packages/rte/tiptap/extensions/umb/urlpicker.extension.ts b/src/packages/rte/tiptap/extensions/umb/urlpicker.extension.ts deleted file mode 100644 index 7bedab735b..0000000000 --- a/src/packages/rte/tiptap/extensions/umb/urlpicker.extension.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import { Link } from '@umbraco-cms/backoffice/external/tiptap'; -import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; - -export default class UmbTiptapUrlPickerExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions() { - return [Link.extend({ openOnClick: false })]; - } - - override async execute(editor?: Editor) { - console.log('umb-link.execute', editor); - - const text = prompt('Enter the text'); - const url = prompt('Enter the URL'); - - if (url && text && editor) { - const { from } = editor.state.selection; - editor - .chain() - .focus() - .insertContent(text) - .setTextSelection({ from: from, to: from + text.length }) - .setLink({ href: url, target: '_blank' }) - .run(); - } - } -} From f3b1670506b6bcf1a773a03dbc7d409beba83c24 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 10:08:04 +0200 Subject: [PATCH 160/241] feat: move 'tiny-mce' into the 'rte' package --- package-lock.json | 7 +- package.json | 2 +- src/apps/backoffice/backoffice.element.ts | 1 - src/packages/rte/index.ts | 1 + src/packages/rte/manifests.ts | 3 +- .../{ => rte}/tiny-mce/components/index.ts | 0 .../components/input-tiny-mce/index.ts | 0 .../input-tiny-mce/input-tiny-mce.defaults.ts | 155 ++++++++++++++++++ .../input-tiny-mce/input-tiny-mce.element.ts | 0 .../input-tiny-mce/input-tiny-mce.handlers.ts | 0 .../input-tiny-mce.languages.ts | 0 .../input-tiny-mce.sanitizer.ts | 0 .../input-tiny-mce/tiny-mce-plugin.ts | 0 src/packages/{ => rte}/tiny-mce/index.ts | 0 src/packages/{ => rte}/tiny-mce/manifests.ts | 0 .../{ => rte}/tiny-mce/plugins/manifests.ts | 0 .../plugins/tiny-mce-code-editor.plugin.ts | 0 .../plugins/tiny-mce-embeddedmedia.plugin.ts | 0 .../plugins/tiny-mce-mediapicker.plugin.ts | 0 .../property-editors/block/manifests.ts | 0 ...ui-block-rte-type-configuration.element.ts | 0 ...ui-block-rte-type-configuration.stories.ts | 0 ...or-ui-block-rte-type-configuration.test.ts | 0 ...ny-mce-dimensions-configuration.element.ts | 0 ...ny-mce-dimensions-configuration.stories.ts | 0 ...-tiny-mce-dimensions-configuration.test.ts | 0 .../tiny-mce/property-editors/manifests.ts | 0 ...editor-ui-tiny-mce-maximagesize.element.ts | 0 ...editor-ui-tiny-mce-maximagesize.stories.ts | 0 ...ty-editor-ui-tiny-mce-maximagesize.test.ts | 0 ...y-mce-stylesheets-configuration.element.ts | 0 ...y-mce-stylesheets-configuration.stories.ts | 0 ...tiny-mce-stylesheets-configuration.test.ts | 0 .../tiny-mce/Umbraco.RichText.ts | 0 .../property-editors/tiny-mce/manifests.ts | 0 .../property-editor-ui-tiny-mce.element.ts | 0 .../property-editor-ui-tiny-mce.stories.ts | 0 .../property-editor-ui-tiny-mce.test.ts | 0 ...-tiny-mce-toolbar-configuration.element.ts | 0 ...-tiny-mce-toolbar-configuration.stories.ts | 0 ...-ui-tiny-mce-toolbar-configuration.test.ts | 0 src/packages/rte/vite.config.ts | 1 + src/packages/tiny-mce/package.json | 8 - src/packages/tiny-mce/umbraco-package.ts | 9 - src/packages/tiny-mce/vite.config.ts | 12 -- tsconfig.json | 2 +- 46 files changed, 163 insertions(+), 38 deletions(-) create mode 100644 src/packages/rte/index.ts rename src/packages/{ => rte}/tiny-mce/components/index.ts (100%) rename src/packages/{ => rte}/tiny-mce/components/input-tiny-mce/index.ts (100%) create mode 100644 src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts rename src/packages/{ => rte}/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts (100%) rename src/packages/{ => rte}/tiny-mce/components/input-tiny-mce/input-tiny-mce.handlers.ts (100%) rename src/packages/{ => rte}/tiny-mce/components/input-tiny-mce/input-tiny-mce.languages.ts (100%) rename src/packages/{ => rte}/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts (100%) rename src/packages/{ => rte}/tiny-mce/components/input-tiny-mce/tiny-mce-plugin.ts (100%) rename src/packages/{ => rte}/tiny-mce/index.ts (100%) rename src/packages/{ => rte}/tiny-mce/manifests.ts (100%) rename src/packages/{ => rte}/tiny-mce/plugins/manifests.ts (100%) rename src/packages/{ => rte}/tiny-mce/plugins/tiny-mce-code-editor.plugin.ts (100%) rename src/packages/{ => rte}/tiny-mce/plugins/tiny-mce-embeddedmedia.plugin.ts (100%) rename src/packages/{ => rte}/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/block/manifests.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.stories.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.test.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.test.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/manifests.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.element.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.test.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.test.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/tiny-mce/manifests.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.stories.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.test.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts (100%) rename src/packages/{ => rte}/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.test.ts (100%) delete mode 100644 src/packages/tiny-mce/package.json delete mode 100644 src/packages/tiny-mce/umbraco-package.ts delete mode 100644 src/packages/tiny-mce/vite.config.ts diff --git a/package-lock.json b/package-lock.json index bde89122d0..2d9d650673 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8295,10 +8295,6 @@ "resolved": "src/packages/templating", "link": true }, - "node_modules/@umbraco-backoffice/tiny-mce": { - "resolved": "src/packages/tiny-mce", - "link": true - }, "node_modules/@umbraco-backoffice/ufm": { "resolved": "src/packages/ufm", "link": true @@ -23758,7 +23754,8 @@ "name": "@umbraco-backoffice/templating" }, "src/packages/tiny-mce": { - "name": "@umbraco-backoffice/tiny-mce" + "name": "@umbraco-backoffice/tiny-mce", + "extraneous": true }, "src/packages/ufm": { "name": "@umbraco-backoffice/ufm" diff --git a/package.json b/package.json index 02542051f5..65331dcba2 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "./template": "./dist-cms/packages/templating/templates/index.js", "./temporary-file": "./dist-cms/packages/core/temporary-file/index.js", "./themes": "./dist-cms/packages/core/themes/index.js", - "./tiny-mce": "./dist-cms/packages/tiny-mce/index.js", + "./tiny-mce": "./dist-cms/packages/rte/tiny-mce/index.js", "./tiptap": "./dist-cms/packages/rte/tiptap/index.js", "./tree": "./dist-cms/packages/core/tree/index.js", "./ufm": "./dist-cms/packages/ufm/index.js", diff --git a/src/apps/backoffice/backoffice.element.ts b/src/apps/backoffice/backoffice.element.ts index eee608f8e6..4129f330a7 100644 --- a/src/apps/backoffice/backoffice.element.ts +++ b/src/apps/backoffice/backoffice.element.ts @@ -41,7 +41,6 @@ const CORE_PACKAGES = [ import('../../packages/tags/umbraco-package.js'), import('../../packages/telemetry/umbraco-package.js'), import('../../packages/templating/umbraco-package.js'), - import('../../packages/tiny-mce/umbraco-package.js'), import('../../packages/ufm/umbraco-package.js'), import('../../packages/umbraco-news/umbraco-package.js'), import('../../packages/user/umbraco-package.js'), diff --git a/src/packages/rte/index.ts b/src/packages/rte/index.ts new file mode 100644 index 0000000000..ff0b559694 --- /dev/null +++ b/src/packages/rte/index.ts @@ -0,0 +1 @@ +export * from './tiptap/index.js'; diff --git a/src/packages/rte/manifests.ts b/src/packages/rte/manifests.ts index 6b7ca9ed76..81d70617d7 100644 --- a/src/packages/rte/manifests.ts +++ b/src/packages/rte/manifests.ts @@ -1,5 +1,6 @@ // eslint-disable-next-line local-rules/no-relative-import-to-import-map-module import { manifests as tiptapManifests } from './tiptap/manifests.js'; +import { manifests as tinyMceManifests } from './tiny-mce/manifests.js'; import type { ManifestTypes, UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; -export const manifests: Array = [...tiptapManifests]; +export const manifests: Array = [...tinyMceManifests, ...tiptapManifests]; diff --git a/src/packages/tiny-mce/components/index.ts b/src/packages/rte/tiny-mce/components/index.ts similarity index 100% rename from src/packages/tiny-mce/components/index.ts rename to src/packages/rte/tiny-mce/components/index.ts diff --git a/src/packages/tiny-mce/components/input-tiny-mce/index.ts b/src/packages/rte/tiny-mce/components/input-tiny-mce/index.ts similarity index 100% rename from src/packages/tiny-mce/components/input-tiny-mce/index.ts rename to src/packages/rte/tiny-mce/components/input-tiny-mce/index.ts diff --git a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts new file mode 100644 index 0000000000..a161e267e1 --- /dev/null +++ b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts @@ -0,0 +1,155 @@ +import { UMB_CONTENT_REQUEST_EVENT_TYPE, type UmbContextRequestEvent } from '@umbraco-cms/backoffice/context-api'; +import type { RawEditorOptions } from '@umbraco-cms/backoffice/external/tinymce'; +import { UUIIconRequestEvent } from '@umbraco-cms/backoffice/external/uui'; + +//export const UMB_BLOCK_ENTRY_WEB_COMPONENTS_ABSOLUTE_PATH = '/umbraco/backoffice/packages/block/block-rte/index.js'; +export const UMB_BLOCK_ENTRY_WEB_COMPONENTS_ABSOLUTE_PATH = '@umbraco-cms/backoffice/block-rte'; + +//we put these as extended elements because they get merged on top of the normal allowed elements by tiny mce +//so we don't have to specify all the normal elements again +export const defaultFallbackConfig: RawEditorOptions = { + plugins: ['anchor', 'charmap', 'table', 'lists', 'advlist', 'autolink', 'directionality', 'searchreplace'], + valid_elements: + '+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-s[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*],figure,figcaption,cite,video[*],audio[*],picture[*],source[*],canvas[*]', + invalid_elements: 'font', + extended_valid_elements: + '@[id|class|style],+umb-rte-block[!data-content-udi],+umb-rte-block-inline[!data-content-udi],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align],span[id|class|style|lang],figure,figcaption', + custom_elements: 'umb-rte-block,~umb-rte-block-inline', + toolbar: [ + 'styles', + 'bold', + 'italic', + 'alignleft', + 'aligncenter', + 'alignright', + 'bullist', + 'numlist', + 'outdent', + 'indent', + 'link', + 'umbmediapicker', + 'umbembeddialog', + ], + + init_instance_callback: function (editor) { + // The following code is the context api proxy. [NL] + // It re-dispatches the context api request event to the origin target of this modal, in other words the element that initiated the modal. [NL] + editor.dom.doc.addEventListener(UMB_CONTENT_REQUEST_EVENT_TYPE, ((event: UmbContextRequestEvent) => { + if (!editor.iframeElement) return; + + event.stopImmediatePropagation(); + editor.iframeElement.dispatchEvent(event.clone()); + }) as EventListener); + + // Proxy for retrieving icons from outside the iframe [NL] + editor.dom.doc.addEventListener(UUIIconRequestEvent.ICON_REQUEST, ((event: UUIIconRequestEvent) => { + if (!editor.iframeElement) return; + + const newEvent = new UUIIconRequestEvent(UUIIconRequestEvent.ICON_REQUEST, { + detail: event.detail, + }); + editor.iframeElement.dispatchEvent(newEvent); + if (newEvent.icon !== null) { + event.acceptRequest(newEvent.icon); + } + }) as EventListener); + + // Transfer our import-map to the iframe: [NL] + const importMapTag = document.head.querySelector('script[type="importmap"]'); + if (importMapTag) { + const importMap = document.createElement('script'); + importMap.type = 'importmap'; + importMap.text = importMapTag.innerHTML; + editor.dom.doc.head.appendChild(importMap); + } + + // Transfer our stylesheets to the iframe: [NL] + const stylesheetTags = document.head.querySelectorAll('link[rel="stylesheet"]'); + stylesheetTags.forEach((stylesheetTag) => { + const stylesheet = document.createElement('link'); + stylesheet.rel = 'stylesheet'; + stylesheet.href = stylesheetTag.href; + editor.dom.doc.head.appendChild(stylesheet); + }); + + // TODO: Lets use/adapt the router-slot logic so we do not need to add this here [NL] + // TODO: When transferring this code, make sure that we check for target='_parent' or target='top' if its happening within a iframe. [NL] + editor.dom.doc.addEventListener('click', (e: MouseEvent) => { + // If we try to open link in a new tab, then we want to skip skip: + //if ((isWindows && e.ctrlKey) || (!isWindows && e.metaKey)) return; + + // Find the target by using the composed path to get the element through the shadow boundaries. + // Notice the difference here compared to RouterSlots implementation [NL] + const $anchor: HTMLAnchorElement = (('composedPath' in e) as any) + ? (e + .composedPath() + .find(($elem) => $elem instanceof HTMLAnchorElement || ($elem as any).tagName === 'A') as HTMLAnchorElement) + : (e.target as HTMLAnchorElement); + + // Abort if the event is not about the anchor tag + if ($anchor == null || !($anchor instanceof HTMLAnchorElement || ($anchor as any).tagName === 'A')) { + return; + } + + // Get the HREF value from the anchor tag + const href = $anchor.href; + + // Only handle the anchor tag if the follow holds true: + // - The HREF is relative to the origin of the current location. + // - The target is targeting the current frame. + // - The anchor doesn't have the attribute [data-router-slot]="disabled" + if ( + !href.startsWith(location.origin) || + ($anchor.target !== '' && $anchor.target !== '_self') || + $anchor.dataset['routerSlot'] === 'disabled' + ) { + return; + } + + // Remove the origin from the start of the HREF to get the path + const path = $anchor.pathname + $anchor.search + $anchor.hash; + + // Prevent the default behavior + e.preventDefault(); + + // Change the history! + window.history.pushState(null, '', path); + }); + + // Load backoffice JS so we can get the umb-rte-block component registered inside the iframe [NL] + const script = document.createElement('script'); + script.type = 'text/javascript'; + script.setAttribute('type', 'module'); + // TODO: Check that we actually get the same extension registry, or find a way so we can make it do so. — It could be some kind of iframe detection? [NL] + script.text = `import "@umbraco-cms/backoffice/extension-registry";`; + script.text = `import "${UMB_BLOCK_ENTRY_WEB_COMPONENTS_ABSOLUTE_PATH}";`; + editor.dom.doc.head.appendChild(script); + }, + + style_formats: [ + { + title: 'Headers', + items: [ + { title: 'Page header', block: 'h2' }, + { title: 'Section header', block: 'h3' }, + { title: 'Paragraph header', block: 'h4' }, + ], + }, + { + title: 'Blocks', + items: [{ title: 'Paragraph', block: 'p' }], + }, + { + title: 'Containers', + items: [ + { title: 'Quote', block: 'blockquote' }, + { title: 'Code', block: 'code' }, + ], + }, + ], + /** + * @description The maximum image size in pixels that can be inserted into the editor. + * @remarks This is registered and used by the UmbMediaPicker plugin + */ + maxImageSize: 500, +}; diff --git a/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts similarity index 100% rename from src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts rename to src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts diff --git a/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.handlers.ts b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.handlers.ts similarity index 100% rename from src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.handlers.ts rename to src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.handlers.ts diff --git a/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.languages.ts b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.languages.ts similarity index 100% rename from src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.languages.ts rename to src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.languages.ts diff --git a/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts similarity index 100% rename from src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts rename to src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts diff --git a/src/packages/tiny-mce/components/input-tiny-mce/tiny-mce-plugin.ts b/src/packages/rte/tiny-mce/components/input-tiny-mce/tiny-mce-plugin.ts similarity index 100% rename from src/packages/tiny-mce/components/input-tiny-mce/tiny-mce-plugin.ts rename to src/packages/rte/tiny-mce/components/input-tiny-mce/tiny-mce-plugin.ts diff --git a/src/packages/tiny-mce/index.ts b/src/packages/rte/tiny-mce/index.ts similarity index 100% rename from src/packages/tiny-mce/index.ts rename to src/packages/rte/tiny-mce/index.ts diff --git a/src/packages/tiny-mce/manifests.ts b/src/packages/rte/tiny-mce/manifests.ts similarity index 100% rename from src/packages/tiny-mce/manifests.ts rename to src/packages/rte/tiny-mce/manifests.ts diff --git a/src/packages/tiny-mce/plugins/manifests.ts b/src/packages/rte/tiny-mce/plugins/manifests.ts similarity index 100% rename from src/packages/tiny-mce/plugins/manifests.ts rename to src/packages/rte/tiny-mce/plugins/manifests.ts diff --git a/src/packages/tiny-mce/plugins/tiny-mce-code-editor.plugin.ts b/src/packages/rte/tiny-mce/plugins/tiny-mce-code-editor.plugin.ts similarity index 100% rename from src/packages/tiny-mce/plugins/tiny-mce-code-editor.plugin.ts rename to src/packages/rte/tiny-mce/plugins/tiny-mce-code-editor.plugin.ts diff --git a/src/packages/tiny-mce/plugins/tiny-mce-embeddedmedia.plugin.ts b/src/packages/rte/tiny-mce/plugins/tiny-mce-embeddedmedia.plugin.ts similarity index 100% rename from src/packages/tiny-mce/plugins/tiny-mce-embeddedmedia.plugin.ts rename to src/packages/rte/tiny-mce/plugins/tiny-mce-embeddedmedia.plugin.ts diff --git a/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts similarity index 100% rename from src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts rename to src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts diff --git a/src/packages/tiny-mce/property-editors/block/manifests.ts b/src/packages/rte/tiny-mce/property-editors/block/manifests.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/block/manifests.ts rename to src/packages/rte/tiny-mce/property-editors/block/manifests.ts diff --git a/src/packages/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts b/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts rename to src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts diff --git a/src/packages/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.stories.ts b/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.stories.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.stories.ts rename to src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.stories.ts diff --git a/src/packages/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.test.ts b/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.test.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.test.ts rename to src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.test.ts diff --git a/src/packages/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts b/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts rename to src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts diff --git a/src/packages/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts b/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts rename to src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts diff --git a/src/packages/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.test.ts b/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.test.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.test.ts rename to src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.test.ts diff --git a/src/packages/tiny-mce/property-editors/manifests.ts b/src/packages/rte/tiny-mce/property-editors/manifests.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/manifests.ts rename to src/packages/rte/tiny-mce/property-editors/manifests.ts diff --git a/src/packages/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.element.ts b/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.element.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.element.ts rename to src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.element.ts diff --git a/src/packages/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts b/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts rename to src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts diff --git a/src/packages/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.test.ts b/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.test.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.test.ts rename to src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.test.ts diff --git a/src/packages/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts b/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts rename to src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts diff --git a/src/packages/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts b/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts rename to src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts diff --git a/src/packages/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.test.ts b/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.test.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.test.ts rename to src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.test.ts diff --git a/src/packages/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts b/src/packages/rte/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts rename to src/packages/rte/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts diff --git a/src/packages/tiny-mce/property-editors/tiny-mce/manifests.ts b/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/tiny-mce/manifests.ts rename to src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts diff --git a/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts rename to src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts diff --git a/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.stories.ts b/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.stories.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.stories.ts rename to src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.stories.ts diff --git a/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.test.ts b/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.test.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.test.ts rename to src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.test.ts diff --git a/src/packages/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts b/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts rename to src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts diff --git a/src/packages/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts b/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts rename to src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts diff --git a/src/packages/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.test.ts b/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.test.ts similarity index 100% rename from src/packages/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.test.ts rename to src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.test.ts diff --git a/src/packages/rte/vite.config.ts b/src/packages/rte/vite.config.ts index 231d180c29..20ec618303 100644 --- a/src/packages/rte/vite.config.ts +++ b/src/packages/rte/vite.config.ts @@ -11,6 +11,7 @@ export default defineConfig({ ...getDefaultConfig({ dist, entry: { + 'tiny-mce/index': 'tiny-mce/index.ts', 'tiptap/index': 'tiptap/index.ts', manifests: 'manifests.ts', 'umbraco-package': 'umbraco-package.ts', diff --git a/src/packages/tiny-mce/package.json b/src/packages/tiny-mce/package.json deleted file mode 100644 index 6362dc5347..0000000000 --- a/src/packages/tiny-mce/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "@umbraco-backoffice/tiny-mce", - "private": true, - "type": "module", - "scripts": { - "build": "vite build" - } -} diff --git a/src/packages/tiny-mce/umbraco-package.ts b/src/packages/tiny-mce/umbraco-package.ts deleted file mode 100644 index a5de5f04cd..0000000000 --- a/src/packages/tiny-mce/umbraco-package.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const name = 'Umbraco.Core.TinyMce'; -export const extensions = [ - { - name: 'TinyMce Bundle', - alias: 'Umb.Bundle.TinyMce', - type: 'bundle', - js: () => import('./manifests.js'), - }, -]; diff --git a/src/packages/tiny-mce/vite.config.ts b/src/packages/tiny-mce/vite.config.ts deleted file mode 100644 index d0c457d1e9..0000000000 --- a/src/packages/tiny-mce/vite.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { defineConfig } from 'vite'; -import { rmSync } from 'fs'; -import { getDefaultConfig } from '../../vite-config-base'; - -const dist = '../../../dist-cms/packages/tiny-mce'; - -// delete the unbundled dist folder -rmSync(dist, { recursive: true, force: true }); - -export default defineConfig({ - ...getDefaultConfig({ dist }), -}); diff --git a/tsconfig.json b/tsconfig.json index 9951bfe191..94463e8e5f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -119,7 +119,7 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js "@umbraco-cms/backoffice/template": ["./src/packages/templating/templates/index.ts"], "@umbraco-cms/backoffice/temporary-file": ["./src/packages/core/temporary-file/index.ts"], "@umbraco-cms/backoffice/themes": ["./src/packages/core/themes/index.ts"], - "@umbraco-cms/backoffice/tiny-mce": ["./src/packages/tiny-mce/index.ts"], + "@umbraco-cms/backoffice/tiny-mce": ["./src/packages/rte/tiny-mce/index.ts"], "@umbraco-cms/backoffice/tiptap": ["./src/packages/rte/tiptap/index.ts"], "@umbraco-cms/backoffice/tree": ["./src/packages/core/tree/index.ts"], "@umbraco-cms/backoffice/ufm": ["./src/packages/ufm/index.ts"], From 5bc697db3bc4d5f777f2c54bc7123a4bb9f4243e Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 10:08:40 +0200 Subject: [PATCH 161/241] chore: sort imports --- .../tiptap/property-editor-ui-tiptap.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index ade8960b91..dd806ff0c5 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -6,9 +6,9 @@ import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-ed import type { UmbBlockRteLayoutModel, UmbBlockRteTypeModel } from '@umbraco-cms/backoffice/block-rte'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; +import type { UmbBlockValueType } from '@umbraco-cms/backoffice/block'; import '../../components/input-tiptap/input-tiptap.element.js'; -import type { UmbBlockValueType } from '@umbraco-cms/backoffice/block'; export interface UmbRichTextEditorValueType { markup: string; From b9eeb4b79668fae90e2d7a262e18470e32d58731 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 10:13:42 +0200 Subject: [PATCH 162/241] feat: move common const for RTE to types --- .../tiny-mce/property-editor-ui-tiny-mce.element.ts | 3 +-- .../tiptap/property-editor-ui-tiptap.element.ts | 3 +-- src/packages/rte/types.ts | 1 + 3 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 src/packages/rte/types.ts diff --git a/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts index 11a10a4862..897842ce27 100644 --- a/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts +++ b/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts @@ -1,4 +1,5 @@ import type { UmbInputTinyMceElement } from '../../components/input-tiny-mce/input-tiny-mce.element.js'; +import { UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS } from '../../../types.js'; import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; @@ -19,8 +20,6 @@ export interface UmbRichTextEditorValueType { blocks: UmbBlockValueType; } -const UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS = 'Umbraco.TinyMCE'; // Not rich text, cause this has not been migrated [NL] - /** * @element umb-property-editor-ui-tiny-mce */ diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index dd806ff0c5..b3794c9369 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -1,4 +1,5 @@ import type { UmbInputTiptapElement } from '../../components/input-tiptap/input-tiptap.element.js'; +import { UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS } from '../../../types.js'; import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbBlockRteEntriesContext, UmbBlockRteManagerContext } from '@umbraco-cms/backoffice/block-rte'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @@ -15,8 +16,6 @@ export interface UmbRichTextEditorValueType { blocks: UmbBlockValueType; } -const UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS = 'Umbraco.TinyMCE'; - const elementName = 'umb-property-editor-ui-tiptap'; /** diff --git a/src/packages/rte/types.ts b/src/packages/rte/types.ts new file mode 100644 index 0000000000..1691cf4aea --- /dev/null +++ b/src/packages/rte/types.ts @@ -0,0 +1 @@ +export const UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS = 'Umbraco.TinyMCE'; From 0fec14aedb006ccab1b0c63b8715b394038cf5ae Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 10:41:10 +0200 Subject: [PATCH 163/241] feat: create base element for rte property editor UIs to ensure blocks especially are treated the same way --- .../rte/components/rte-base.element.ts | 129 ++++++++++++++++++ .../property-editor-ui-tiny-mce.element.ts | 120 +--------------- .../property-editor-ui-tiptap.element.ts | 121 +--------------- src/packages/rte/types.ts | 1 - 4 files changed, 141 insertions(+), 230 deletions(-) create mode 100644 src/packages/rte/components/rte-base.element.ts delete mode 100644 src/packages/rte/types.ts diff --git a/src/packages/rte/components/rte-base.element.ts b/src/packages/rte/components/rte-base.element.ts new file mode 100644 index 0000000000..ee6387c39f --- /dev/null +++ b/src/packages/rte/components/rte-base.element.ts @@ -0,0 +1,129 @@ +import type { UmbBlockValueType } from '@umbraco-cms/backoffice/block'; +import { + UmbBlockRteEntriesContext, + UmbBlockRteManagerContext, + type UmbBlockRteLayoutModel, + type UmbBlockRteTypeModel, +} from '@umbraco-cms/backoffice/block-rte'; +import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; +import { property, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { + UmbPropertyValueChangeEvent, + type UmbPropertyEditorConfigCollection, +} from '@umbraco-cms/backoffice/property-editor'; + +export interface UmbRichTextEditorValueType { + markup: string; + blocks: UmbBlockValueType; +} + +export const UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS = 'Umbraco.TinyMCE'; + +export abstract class UmbRteBaseElement extends UmbLitElement implements UmbPropertyEditorUiElement { + public set config(config: UmbPropertyEditorConfigCollection | undefined) { + if (!config) return; + + this._config = config; + + const blocks = config.getValueByAlias>('blocks') ?? []; + this.#managerContext.setBlockTypes(blocks); + + this.#managerContext.setEditorConfiguration(config); + } + + /** + * Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content. + * @default false + */ + @property({ type: Boolean, reflect: true }) + readonly = false; + + @property({ + attribute: false, + type: Object, + hasChanged(value?: UmbRichTextEditorValueType, oldValue?: UmbRichTextEditorValueType) { + return value?.markup !== oldValue?.markup; + }, + }) + public set value(value: UmbRichTextEditorValueType | undefined) { + const buildUpValue: Partial = value ? { ...value } : {}; + buildUpValue.markup ??= ''; + buildUpValue.blocks ??= { layout: {}, contentData: [], settingsData: [] }; + buildUpValue.blocks.layout ??= {}; + buildUpValue.blocks.contentData ??= []; + buildUpValue.blocks.settingsData ??= []; + this._value = buildUpValue as UmbRichTextEditorValueType; + + // Only update the actual editor markup if it is not the same as the value. + if (this._latestMarkup !== this._value.markup) { + this._markup = this._value.markup; + } + + this.#managerContext.setLayouts(buildUpValue.blocks.layout[UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS] ?? []); + this.#managerContext.setContents(buildUpValue.blocks.contentData); + this.#managerContext.setSettings(buildUpValue.blocks.settingsData); + } + public get value(): UmbRichTextEditorValueType { + return this._value; + } + + @state() + protected _config?: UmbPropertyEditorConfigCollection; + + @state() + protected _value: UmbRichTextEditorValueType = { + markup: '', + blocks: { layout: {}, contentData: [], settingsData: [] }, + }; + + /** + * Separate state for markup, to avoid re-rendering/re-setting the value of the Tiptap editor when the value does not really change. + */ + @state() + protected _markup = ''; + + /** + * The latest value gotten from the RTE editor. + */ + protected _latestMarkup = ''; + + readonly #managerContext = new UmbBlockRteManagerContext(this); + readonly #entriesContext = new UmbBlockRteEntriesContext(this); + + constructor() { + super(); + + this.observe(this.#entriesContext.layoutEntries, (layouts) => { + // Update manager: + this.#managerContext.setLayouts(layouts); + }); + + this.observe(this.#managerContext.layouts, (layouts) => { + this._value = { + ...this._value, + blocks: { ...this._value.blocks, layout: { [UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS]: layouts } }, + }; + this._fireChangeEvent(); + }); + this.observe(this.#managerContext.contents, (contents) => { + this._value = { ...this._value, blocks: { ...this._value.blocks, contentData: contents } }; + this._fireChangeEvent(); + }); + this.observe(this.#managerContext.settings, (settings) => { + this._value = { ...this._value, blocks: { ...this._value.blocks, settingsData: settings } }; + this._fireChangeEvent(); + }); + } + + protected _filterUnusedBlocks(usedContentUdis: (string | null)[]) { + const unusedBlocks = this.#managerContext.getLayouts().filter((x) => usedContentUdis.indexOf(x.contentUdi) === -1); + unusedBlocks.forEach((blockLayout) => { + this.#managerContext.removeOneLayout(blockLayout.contentUdi); + }); + } + + protected _fireChangeEvent() { + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + } +} diff --git a/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts index 897842ce27..ea0ce31cf1 100644 --- a/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts +++ b/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts @@ -1,120 +1,14 @@ +import { UmbRteBaseElement } from '../../../components/rte-base.element.js'; import type { UmbInputTinyMceElement } from '../../components/input-tiny-mce/input-tiny-mce.element.js'; -import { UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS } from '../../../types.js'; -import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; -import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; -import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; -import { - UmbBlockRteEntriesContext, - type UmbBlockRteLayoutModel, - UmbBlockRteManagerContext, - type UmbBlockRteTypeModel, -} from '@umbraco-cms/backoffice/block-rte'; -import type { UmbBlockValueType } from '@umbraco-cms/backoffice/block'; +import { customElement, html } from '@umbraco-cms/backoffice/external/lit'; import '../../components/input-tiny-mce/input-tiny-mce.element.js'; -export interface UmbRichTextEditorValueType { - markup: string; - blocks: UmbBlockValueType; -} - /** * @element umb-property-editor-ui-tiny-mce */ @customElement('umb-property-editor-ui-tiny-mce') -export class UmbPropertyEditorUITinyMceElement extends UmbLitElement implements UmbPropertyEditorUiElement { - // - // No need to registerer as a LIT-property, as we are calling it directly and no need for it to be reactive [NL] - public set config(config: UmbPropertyEditorConfigCollection | undefined) { - if (!config) return; - - this._config = config; - - const blocks = config.getValueByAlias>('blocks') ?? []; - this.#managerContext.setBlockTypes(blocks); - - this.#managerContext.setEditorConfiguration(config); - } - - @property({ attribute: false }) - public set value(value: UmbRichTextEditorValueType | undefined) { - const buildUpValue: Partial = value ? { ...value } : {}; - buildUpValue.markup ??= ''; - buildUpValue.blocks ??= { layout: {}, contentData: [], settingsData: [] }; - buildUpValue.blocks.layout ??= {}; - buildUpValue.blocks.contentData ??= []; - buildUpValue.blocks.settingsData ??= []; - this._value = buildUpValue as UmbRichTextEditorValueType; - - if (this._latestMarkup !== this._value.markup) { - this._markup = this._value.markup; - } - - this.#managerContext.setLayouts(buildUpValue.blocks.layout[UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS] ?? []); - this.#managerContext.setContents(buildUpValue.blocks.contentData); - this.#managerContext.setSettings(buildUpValue.blocks.settingsData); - } - public get value(): UmbRichTextEditorValueType { - return this._value; - } - - /** - * Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content. - * @type {boolean} - * @attr - * @default false - */ - @property({ type: Boolean, reflect: true }) - readonly = false; - - @state() - _config?: UmbPropertyEditorConfigCollection; - - @state() - private _value: UmbRichTextEditorValueType = { - markup: '', - blocks: { layout: {}, contentData: [], settingsData: [] }, - }; - - // Separate state for markup, to avoid re-rendering/re-setting the value of the TinyMCE editor when the value does not really change. - @state() - private _markup = ''; - private _latestMarkup = ''; // The latest value gotten from the TinyMCE editor. - - readonly #managerContext = new UmbBlockRteManagerContext(this); - readonly #entriesContext = new UmbBlockRteEntriesContext(this); - - constructor() { - super(); - - this.observe(this.#entriesContext.layoutEntries, (layouts) => { - // Update manager: - this.#managerContext.setLayouts(layouts); - }); - - this.observe(this.#managerContext.layouts, (layouts) => { - this._value = { - ...this._value, - blocks: { ...this._value.blocks, layout: { [UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS]: layouts } }, - }; - this.#fireChangeEvent(); - }); - this.observe(this.#managerContext.contents, (contents) => { - this._value = { ...this._value, blocks: { ...this._value.blocks, contentData: contents } }; - this.#fireChangeEvent(); - }); - this.observe(this.#managerContext.settings, (settings) => { - this._value = { ...this._value, blocks: { ...this._value.blocks, settingsData: settings } }; - this.#fireChangeEvent(); - }); - } - - #fireChangeEvent() { - this.dispatchEvent(new UmbPropertyValueChangeEvent()); - } - +export class UmbPropertyEditorUITinyMceElement extends UmbRteBaseElement { #onChange(event: CustomEvent & { target: UmbInputTinyMceElement }) { const value = event.target.value; @@ -134,10 +28,8 @@ export class UmbPropertyEditorUITinyMceElement extends UmbLitElement implements // Remove unused Blocks of Blocks Layout. Leaving only the Blocks that are present in Markup. //const blockElements = editor.dom.select(`umb-rte-block, umb-rte-block-inline`); const usedContentUdis = Array.from(blockEls).map((blockElement) => blockElement.getAttribute('data-content-udi')); - const unusedBlocks = this.#managerContext.getLayouts().filter((x) => usedContentUdis.indexOf(x.contentUdi) === -1); - unusedBlocks.forEach((blockLayout) => { - this.#managerContext.removeOneLayout(blockLayout.contentUdi); - }); + + this._filterUnusedBlocks(usedContentUdis); // Then get the content of the editor and update the value. // maybe in this way doc.body.innerHTML; @@ -149,7 +41,7 @@ export class UmbPropertyEditorUITinyMceElement extends UmbLitElement implements markup: markup, }; - this.#fireChangeEvent(); + this._fireChangeEvent(); } override render() { diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index b3794c9369..105f57d563 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -1,122 +1,16 @@ import type { UmbInputTiptapElement } from '../../components/input-tiptap/input-tiptap.element.js'; -import { UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS } from '../../../types.js'; -import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; -import { UmbBlockRteEntriesContext, UmbBlockRteManagerContext } from '@umbraco-cms/backoffice/block-rte'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; -import type { UmbBlockRteLayoutModel, UmbBlockRteTypeModel } from '@umbraco-cms/backoffice/block-rte'; -import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; -import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; -import type { UmbBlockValueType } from '@umbraco-cms/backoffice/block'; +import { UmbRteBaseElement } from '../../../components/rte-base.element.js'; +import { customElement, html } from '@umbraco-cms/backoffice/external/lit'; import '../../components/input-tiptap/input-tiptap.element.js'; -export interface UmbRichTextEditorValueType { - markup: string; - blocks: UmbBlockValueType; -} - const elementName = 'umb-property-editor-ui-tiptap'; /** * @element umb-property-editor-ui-tiptap */ @customElement(elementName) -export class UmbPropertyEditorUiTiptapElement extends UmbLitElement implements UmbPropertyEditorUiElement { - public set config(config: UmbPropertyEditorConfigCollection | undefined) { - if (!config) return; - - this._config = config; - - const blocks = config.getValueByAlias>('blocks') ?? []; - this.#managerContext.setBlockTypes(blocks); - - this.#managerContext.setEditorConfiguration(config); - } - - @property({ - attribute: false, - type: Object, - hasChanged(value?: UmbRichTextEditorValueType, oldValue?: UmbRichTextEditorValueType) { - return value?.markup !== oldValue?.markup; - }, - }) - public set value(value: UmbRichTextEditorValueType | undefined) { - const buildUpValue: Partial = value ? { ...value } : {}; - buildUpValue.markup ??= ''; - buildUpValue.blocks ??= { layout: {}, contentData: [], settingsData: [] }; - buildUpValue.blocks.layout ??= {}; - buildUpValue.blocks.contentData ??= []; - buildUpValue.blocks.settingsData ??= []; - this._value = buildUpValue as UmbRichTextEditorValueType; - - // Only update the actual editor markup if it is not the same as the value. - if (this._latestMarkup !== this._value.markup) { - this._markup = this._value.markup; - } - - this.#managerContext.setLayouts(buildUpValue.blocks.layout[UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS] ?? []); - this.#managerContext.setContents(buildUpValue.blocks.contentData); - this.#managerContext.setSettings(buildUpValue.blocks.settingsData); - } - public get value(): UmbRichTextEditorValueType { - return this._value; - } - - /** - * Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content. - * @attr - * @default false - */ - @property({ type: Boolean, reflect: true }) - readonly = false; - - @state() - _config?: UmbPropertyEditorConfigCollection; - - @state() - private _value: UmbRichTextEditorValueType = { - markup: '', - blocks: { layout: {}, contentData: [], settingsData: [] }, - }; - - // Separate state for markup, to avoid re-rendering/re-setting the value of the Tiptap editor when the value does not really change. - @state() - private _markup = ''; - private _latestMarkup = ''; // The latest value gotten from the Tiptap editor. - - readonly #managerContext = new UmbBlockRteManagerContext(this); - readonly #entriesContext = new UmbBlockRteEntriesContext(this); - - constructor() { - super(); - - this.observe(this.#entriesContext.layoutEntries, (layouts) => { - // Update manager: - this.#managerContext.setLayouts(layouts); - }); - - this.observe(this.#managerContext.layouts, (layouts) => { - this._value = { - ...this._value, - blocks: { ...this._value.blocks, layout: { [UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS]: layouts } }, - }; - this.#fireChangeEvent(); - }); - this.observe(this.#managerContext.contents, (contents) => { - this._value = { ...this._value, blocks: { ...this._value.blocks, contentData: contents } }; - this.#fireChangeEvent(); - }); - this.observe(this.#managerContext.settings, (settings) => { - this._value = { ...this._value, blocks: { ...this._value.blocks, settingsData: settings } }; - this.#fireChangeEvent(); - }); - } - - #fireChangeEvent() { - this.dispatchEvent(new UmbPropertyValueChangeEvent()); - } - +export class UmbPropertyEditorUiTiptapElement extends UmbRteBaseElement { #onChange(event: CustomEvent & { target: UmbInputTiptapElement }) { const value = event.target.value; this._latestMarkup = value; @@ -140,19 +34,16 @@ export class UmbPropertyEditorUiTiptapElement extends UmbLitElement implements U } } - const unusedBlocks = this.#managerContext.getLayouts().filter((x) => usedContentUdis.indexOf(x.contentUdi) === -1); - unusedBlocks.forEach((blockLayout) => { - this.#managerContext.removeOneLayout(blockLayout.contentUdi); - }); + this._filterUnusedBlocks(usedContentUdis); - this.#fireChangeEvent(); + this._fireChangeEvent(); } override render() { return html` `; diff --git a/src/packages/rte/types.ts b/src/packages/rte/types.ts deleted file mode 100644 index 1691cf4aea..0000000000 --- a/src/packages/rte/types.ts +++ /dev/null @@ -1 +0,0 @@ -export const UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS = 'Umbraco.TinyMCE'; From 1a8b32cbc2d5cff31ee4e2af68a3b378b3515999 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 10:44:28 +0200 Subject: [PATCH 164/241] chore: remove TODO comments --- .../components/input-tiny-mce/input-tiny-mce.element.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts index e2d3c3b877..cbc5d418b0 100644 --- a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts +++ b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts @@ -23,7 +23,6 @@ import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/ * Handles the resize event * @param e */ -// TODO: This does somehow not belong as a utility method as it is very specific to this implementation. [NL] async function onResize( e: EditorEvent<{ target: HTMLElement; @@ -336,7 +335,6 @@ export class UmbInputTinyMceElement extends UUIFormControlMixin(UmbLitElement, ' /** * Prevent injecting arbitrary JavaScript execution in on-attributes. * - * TODO: This used to be toggleable through server variables with window.Umbraco?.Sys.ServerVariables.umbracoSettings.sanitizeTinyMce */ const allNodes = Array.from(editor.dom.doc.getElementsByTagName('*')); allNodes.forEach((node) => { From 5400208ea86f18dcda5dd2e190d2632f9e24ed29 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 10:45:03 +0200 Subject: [PATCH 165/241] chore: remove TODO comments --- .../components/input-tiny-mce/input-tiny-mce.defaults.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts index a161e267e1..a26ab76fc0 100644 --- a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts +++ b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts @@ -72,8 +72,6 @@ export const defaultFallbackConfig: RawEditorOptions = { editor.dom.doc.head.appendChild(stylesheet); }); - // TODO: Lets use/adapt the router-slot logic so we do not need to add this here [NL] - // TODO: When transferring this code, make sure that we check for target='_parent' or target='top' if its happening within a iframe. [NL] editor.dom.doc.addEventListener('click', (e: MouseEvent) => { // If we try to open link in a new tab, then we want to skip skip: //if ((isWindows && e.ctrlKey) || (!isWindows && e.metaKey)) return; @@ -120,7 +118,7 @@ export const defaultFallbackConfig: RawEditorOptions = { const script = document.createElement('script'); script.type = 'text/javascript'; script.setAttribute('type', 'module'); - // TODO: Check that we actually get the same extension registry, or find a way so we can make it do so. — It could be some kind of iframe detection? [NL] + script.text = `import "@umbraco-cms/backoffice/extension-registry";`; script.text = `import "${UMB_BLOCK_ENTRY_WEB_COMPONENTS_ABSOLUTE_PATH}";`; editor.dom.doc.head.appendChild(script); From d6bc36f1a0dad453a1b4df423278bbaba36c37c5 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 10:49:18 +0200 Subject: [PATCH 166/241] chore: fix sonarcloud issues --- .../input-tiny-mce/input-tiny-mce.sanitizer.ts | 18 ------------------ .../plugins/tiny-mce-mediapicker.plugin.ts | 12 +----------- ...-ui-block-rte-type-configuration.element.ts | 2 +- ...iny-mce-dimensions-configuration.element.ts | 2 +- ...ny-mce-stylesheets-configuration.element.ts | 2 +- ...i-tiny-mce-toolbar-configuration.element.ts | 2 +- .../tiptap/extensions/umb/link.extension.ts | 2 +- 7 files changed, 6 insertions(+), 34 deletions(-) diff --git a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts index fbc5e0ebd9..e43b022d5d 100644 --- a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts +++ b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts @@ -1,4 +1,3 @@ -// TODO: clean up this file /* eslint-disable @typescript-eslint/no-unused-vars */ import type { Editor } from '@umbraco-cms/backoffice/external/tinymce'; @@ -47,21 +46,4 @@ export const uriAttributeSanitizer = (editor: Editor) => { return uri; }; })(); - - // TODO: sanitizeTinyMce is not defined in the global scope, so this will not work. Instead we need to get this setting from somewhere else: - /* - if (window.Umbraco?.Sys.ServerVariables.umbracoSettings.sanitizeTinyMce) { - uriAttributesToSanitize.forEach((attribute) => { - editor.serializer.addAttributeFilter(attribute, (nodes: AstNode[]) => { - nodes.forEach((node: AstNode) => { - node.attributes?.forEach((attr) => { - if (uriAttributesToSanitize.includes(attr.name.toLowerCase())) { - attr.value = parseUri(attr.value, node.name) ?? ''; - } - }); - }); - }); - }); - } - */ }; diff --git a/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index bc53032243..bdd97a523a 100644 --- a/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts @@ -43,13 +43,6 @@ export default class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase { this.#modalManager = instance; }); - // TODO => this breaks tests. disabling for now - // will ignore user media start nodes - // this.host.consumeContext(UMB_CURRENT_USER_CONTEXT, (instance) => { - // this.#currentUserContext = instance; - // this.#observeCurrentUser(); - // }); - this.editor.ui.registry.addToggleButton('umbmediapicker', { icon: 'image', tooltip: localize.term('general_mediaPicker'), @@ -124,7 +117,6 @@ export default class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase { async #showMediaPicker(currentTarget: MediaPickerTargetData) { /* - // TODO: I dont think we should parse this one... it should be up to the modal to get this information, and then we could parse some configs on to affect this. let startNodeId; let startNodeIsVirtual; @@ -139,8 +131,6 @@ export default class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase { } */ - // TODO => startNodeId and startNodeIsVirtual do not exist on ContentTreeItemResponseModel - const modalHandler = this.#modalManager?.open(this, UMB_MEDIA_PICKER_MODAL, { data: { multiple: false, @@ -241,7 +231,7 @@ export default class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase { }); } - #uploadImageHandler: RawEditorOptions['images_upload_handler'] = (blobInfo, progress) => { + readonly #uploadImageHandler: RawEditorOptions['images_upload_handler'] = (blobInfo, progress) => { return new Promise((resolve, reject) => { // Fetch does not support progress, so we need to fake it. progress(0); diff --git a/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts b/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts index 174e14fe02..7b839a0372 100644 --- a/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts +++ b/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts @@ -18,7 +18,7 @@ export class UmbPropertyEditorUIBlockRteBlockConfigurationElement extends UmbLitElement implements UmbPropertyEditorUiElement { - #blockTypeWorkspaceModalRegistration?: UmbModalRouteRegistrationController< + readonly #blockTypeWorkspaceModalRegistration?: UmbModalRouteRegistrationController< typeof UMB_WORKSPACE_MODAL.DATA, typeof UMB_WORKSPACE_MODAL.VALUE >; diff --git a/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts b/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts index b8b9b82d8b..e47c74dce5 100644 --- a/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts +++ b/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts @@ -38,7 +38,7 @@ export class UmbPropertyEditorUITinyMceDimensionsConfigurationElement extends Um pixels`; } - static override styles = [UmbTextStyles]; + static override readonly styles = [UmbTextStyles]; } export default UmbPropertyEditorUITinyMceDimensionsConfigurationElement; diff --git a/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts b/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts index 9fc27a5360..5c0659620d 100644 --- a/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts +++ b/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts @@ -14,7 +14,7 @@ export class UmbPropertyEditorUITinyMceStylesheetsConfigurationElement extends UmbLitElement implements UmbPropertyEditorUiElement { - #serverFilePathUniqueSerializer = new UmbServerFilePathUniqueSerializer(); + readonly #serverFilePathUniqueSerializer = new UmbServerFilePathUniqueSerializer(); @property({ type: Array }) public set value(value: Array) { diff --git a/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts b/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts index cc2238a2eb..11f550c8ab 100644 --- a/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts +++ b/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts @@ -59,7 +59,7 @@ export class UmbPropertyEditorUITinyMceToolbarConfigurationElement config?: UmbPropertyEditorConfigCollection; @state() - private _toolbarConfig: ToolbarConfig[] = []; + private readonly _toolbarConfig: ToolbarConfig[] = []; #selectedValues: string[] = []; diff --git a/src/packages/rte/tiptap/extensions/umb/link.extension.ts b/src/packages/rte/tiptap/extensions/umb/link.extension.ts index 3ea28a010d..78146a0da5 100644 --- a/src/packages/rte/tiptap/extensions/umb/link.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/link.extension.ts @@ -60,7 +60,7 @@ export default class UmbTiptapLinkExtensionApi extends UmbTiptapToolbarElementAp // The href might be an external url, so check the value for an anchor/querystring; // `href` has the anchor re-appended later, hence the reset here to avoid duplicating the anchor if (!queryString) { - const urlParts = url?.split(/(#|\?)/); + const urlParts = url?.split(/([#?])/); if (urlParts?.length === 3) { url = urlParts[0]; queryString = urlParts[1] + urlParts[2]; From 1f52327efece4679c8375a72c985aa2f3bedf745 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 11:21:12 +0200 Subject: [PATCH 167/241] fix: make the `getProcessedImageUrl` return a path --- .../utils/get-processed-image-url.function.ts | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/packages/core/utils/get-processed-image-url.function.ts b/src/packages/core/utils/get-processed-image-url.function.ts index 674e6de91b..51a43983d5 100644 --- a/src/packages/core/utils/get-processed-image-url.function.ts +++ b/src/packages/core/utils/get-processed-image-url.function.ts @@ -1,18 +1,25 @@ -// TODO: This does not feel like a utility, but should instead become a repository/data-source/resource something in that direction [NL] +import type { GetImagingResizeUrlsData } from '@umbraco-cms/backoffice/external/backend-api'; /** - * Returns the URL of the processed image - * @param imagePath - * @param options + * Returns the URL of the processed image. + * @param {string} imagePath The path to the image. + * @param {GetImagingResizeUrlsData} options The options for resizing the image. + * @returns {Promise} The URL of the processed image. */ -export async function getProcessedImageUrl(imagePath: string, options: any) { +export async function getProcessedImageUrl(imagePath: string, options: GetImagingResizeUrlsData): Promise { if (!options) { return imagePath; } - // TODO => use backend cli when available - const result = await fetch('/umbraco/management/api/v1/images/GetProcessedImageUrl'); - const url = (await result.json()) as string; + const searchParams = new URLSearchParams({ + width: options.width?.toString() ?? '', + height: options.height?.toString() ?? '', + mode: options.mode ?? '', + }); + + // This should ideally use the ImagingService.getImagingResizeUrls method, but + // that would require the GUID of the media item, which is not available here. + const url = `${imagePath}?${searchParams.toString()}`; return url; } From f8f9877abf8e583b1605c51b119e958c29606c29 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 11:21:39 +0200 Subject: [PATCH 168/241] chore(sonarcloud): fix issues --- .../input-tiny-mce/input-tiny-mce.element.ts | 22 +++++++++---------- .../input-tiny-mce.sanitizer.ts | 12 ++++++++++ .../plugins/tiny-mce-mediapicker.plugin.ts | 14 +++++------- ...-tiny-mce-toolbar-configuration.element.ts | 2 +- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts index cbc5d418b0..8c20112d9a 100644 --- a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts +++ b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts @@ -18,6 +18,7 @@ import { renderEditor, } from '@umbraco-cms/backoffice/external/tinymce'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; +import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api'; /** * Handles the resize event @@ -41,7 +42,7 @@ async function onResize( const resizedPath = await getProcessedImageUrl(path, { width: e.width, height: e.height, - mode: 'max', + mode: ImageCropModeModel.MAX, }); e.target.setAttribute('data-mce-src', resizedPath); @@ -54,8 +55,8 @@ export class UmbInputTinyMceElement extends UUIFormControlMixin(UmbLitElement, ' #plugins: Array | undefined> = []; #editorRef?: Editor | null = null; - #stylesheetRepository = new UmbStylesheetDetailRepository(this); - #umbStylesheetRuleManager = new UmbStylesheetRuleManager(); + readonly #stylesheetRepository = new UmbStylesheetDetailRepository(this); + readonly #umbStylesheetRuleManager = new UmbStylesheetRuleManager(); protected override getFormElement() { return this._editorElement?.querySelector('iframe') ?? undefined; @@ -93,14 +94,13 @@ export class UmbInputTinyMceElement extends UUIFormControlMixin(UmbLitElement, ' #readonly = false; @query('.editor', true) - private _editorElement?: HTMLElement; + private readonly _editorElement?: HTMLElement; getEditor() { return this.#editorRef; } - constructor() { - super(); + override firstUpdated() { this.#loadEditor(); } @@ -226,8 +226,8 @@ export class UmbInputTinyMceElement extends UUIFormControlMixin(UmbLitElement, ' // set the configured toolbar if any, otherwise false const toolbar = this.configuration?.getValueByAlias('toolbar'); - if (toolbar && toolbar.length) { - configurationOptions.toolbar = toolbar?.join(' '); + if (toolbar?.length) { + configurationOptions.toolbar = toolbar.join(' '); } else { configurationOptions.toolbar = false; } @@ -338,9 +338,9 @@ export class UmbInputTinyMceElement extends UUIFormControlMixin(UmbLitElement, ' */ const allNodes = Array.from(editor.dom.doc.getElementsByTagName('*')); allNodes.forEach((node) => { - for (let i = 0; i < node.attributes.length; i++) { - if (node.attributes[i].name.startsWith('on')) { - node.removeAttribute(node.attributes[i].name); + for (const attr of node.attributes) { + if (attr.name.startsWith('on')) { + node.removeAttribute(attr.name); } } }); diff --git a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts index e43b022d5d..57f799f9ed 100644 --- a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts +++ b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts @@ -46,4 +46,16 @@ export const uriAttributeSanitizer = (editor: Editor) => { return uri; }; })(); + + editor.serializer.addAttributeFilter('uriAttributesToSanitize', function (nodes) { + nodes.forEach(function (node) { + if (!node.attributes) return; + for (const attr of node.attributes) { + const attrName = attr.name.toLowerCase(); + if (uriAttributesToSanitize.indexOf(attrName) !== -1) { + attr.value = parseUri(attr.value, node.name) ?? ''; + } + } + }); + }); }; diff --git a/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index bdd97a523a..e7e4ca1556 100644 --- a/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts @@ -31,7 +31,7 @@ interface MediaPickerResultData { export default class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase { #modalManager?: typeof UMB_MODAL_MANAGER_CONTEXT.TYPE; - #temporaryFileRepository; + readonly #temporaryFileRepository; constructor(args: TinyMcePluginArguments) { super(args); @@ -144,7 +144,7 @@ export default class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase { if (!modalHandler) return; const { selection } = await modalHandler.onSubmit().catch(() => ({ selection: undefined })); - if (!selection || !selection.length) return; + if (!selection?.length) return; this.#showMediaCaptionAltText(selection[0], currentTarget); this.editor.dispatch('Change'); @@ -200,13 +200,11 @@ export default class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase { } else { parentElement.innerHTML = combined; } - } else { + } else if (parentElement?.nodeName === 'FIGURE' && parentElement.parentElement) { //if caption is removed, remove the figure element - if (parentElement?.nodeName === 'FIGURE' && parentElement.parentElement) { - parentElement.parentElement.innerHTML = newImage; - } else { - this.editor.selection.setContent(newImage); - } + parentElement.parentElement.innerHTML = newImage; + } else { + this.editor.selection.setContent(newImage); } // Using settimeout to wait for a DoM-render, so we can find the new element by ID. diff --git a/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts b/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts index 11f550c8ab..1201d39626 100644 --- a/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts +++ b/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts @@ -128,7 +128,7 @@ export class UmbPropertyEditorUITinyMceToolbarConfigurationElement `; } - static override styles = [ + static override readonly styles = [ UmbTextStyles, css` ul { From c4a53c6cd4d4b153910e16edc4f702f7991bdad8 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 11:42:23 +0200 Subject: [PATCH 169/241] chore(sonarcloud): fix issues --- .../components/input-tiny-mce/input-tiny-mce.element.ts | 4 ++-- .../components/input-tiny-mce/input-tiny-mce.sanitizer.ts | 8 +------- .../tiny-mce/property-editor-ui-tiny-mce.element.ts | 4 ++-- src/packages/rte/tiptap/extensions/umb/link.extension.ts | 2 +- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts index 8c20112d9a..3da7c60d86 100644 --- a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts +++ b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts @@ -64,7 +64,7 @@ export class UmbInputTinyMceElement extends UUIFormControlMixin(UmbLitElement, ' override set value(newValue: FormDataEntryValue | FormData) { super.value = newValue; - const newContent = newValue?.toString() ?? ''; + const newContent = typeof newValue === 'string' ? newValue : ''; if (this.#editorRef && this.#editorRef.getContent() != newContent) { this.#editorRef.setContent(newContent); @@ -362,7 +362,7 @@ export class UmbInputTinyMceElement extends UUIFormControlMixin(UmbLitElement, ' //enable browser based spell checking editor.getBody().setAttribute('spellcheck', 'true'); uriAttributeSanitizer(editor); - editor.setContent(this.value?.toString() ?? ''); + editor.setContent(typeof this.value === 'string' ? this.value : ''); } #onChange(value: string) { diff --git a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts index 57f799f9ed..d50a6ef6e3 100644 --- a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts +++ b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts @@ -27,13 +27,7 @@ export const uriAttributeSanitizer = (editor: Editor) => { return function parseUri(uri: string, tagName: string) { uri = uri.replace(trimRegExp, ''); - try { - // Might throw malformed URI sequence - uri = decodeURIComponent(uri); - } catch (ex) { - // Fallback to non UTF-8 decoder - uri = unescape(uri); - } + uri = decodeURIComponent(uri); if (scriptUriRegExp.test(uri)) { return; diff --git a/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts index ea0ce31cf1..e84e389779 100644 --- a/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts +++ b/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts @@ -10,11 +10,11 @@ import '../../components/input-tiny-mce/input-tiny-mce.element.js'; @customElement('umb-property-editor-ui-tiny-mce') export class UmbPropertyEditorUITinyMceElement extends UmbRteBaseElement { #onChange(event: CustomEvent & { target: UmbInputTinyMceElement }) { - const value = event.target.value; + const value = typeof event.target.value === 'string' ? event.target.value : ''; // Clone the DOM, to remove the classes and attributes on the original: const div = document.createElement('div'); - div.innerHTML = value.toString(); + div.innerHTML = value; // Loop through used, to remove the classes on these. const blockEls = div.querySelectorAll(`umb-rte-block, umb-rte-block-inline`); diff --git a/src/packages/rte/tiptap/extensions/umb/link.extension.ts b/src/packages/rte/tiptap/extensions/umb/link.extension.ts index 78146a0da5..e7ef26bf85 100644 --- a/src/packages/rte/tiptap/extensions/umb/link.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/link.extension.ts @@ -53,7 +53,7 @@ export default class UmbTiptapLinkExtensionApi extends UmbTiptapToolbarElementAp let { queryString, url } = link; // If an anchor exists, check that it is appropriately prefixed - if (queryString && !queryString?.startsWith('?') && !queryString?.startsWith('#')) { + if (!queryString?.startsWith('?') && !queryString?.startsWith('#')) { queryString = (queryString?.startsWith('=') ? '#' : '?') + queryString; } From 9dd0f9e47ad413d38cc7961672a7df5ff1cafcb3 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 13:01:35 +0200 Subject: [PATCH 170/241] feat: create new extension type --- ...n.ts => block-picker-toolbar.extension.ts} | 87 +------ .../tiptap-extension/block.extension.ts | 88 +++++++ .../block-rte/tiptap-extension/manifests.ts | 18 +- .../input-tiptap/input-tiptap.element.ts | 5 +- .../input-tiptap/tiptap-fixed-menu.element.ts | 6 +- .../extensions/core/blockquote.extension.ts | 9 +- .../tiptap/extensions/core/bold.extension.ts | 9 +- .../extensions/core/code-block.extension.ts | 10 +- .../core/embedded-media.extension.ts | 6 + .../extensions/core/heading.extension.ts | 6 + .../core/horizontal-rule.extension.ts | 9 +- .../extensions/core/italic.extension.ts | 9 +- .../tiptap/extensions/core/link.extension.ts | 8 + .../tiptap/extensions/core/list.extension.ts | 6 + .../rte/tiptap/extensions/core/manifests.ts | 173 ++++--------- .../extensions/core/strike.extension.ts | 9 +- .../tiptap/extensions/core/table.extension.ts | 9 +- .../extensions/core/text-align.extension.ts | 10 + .../extensions/core/underline.extension.ts | 9 +- .../rte/tiptap/extensions/manifests.ts | 59 +++-- .../rte/tiptap/extensions/tiptap-extension.ts | 18 +- .../extensions/tiptap-toolbar-extension.ts | 29 +++ .../toolbar/blockquote.extension.ts | 8 + .../extensions/toolbar/bold.extension.ts | 8 + .../bullet-list.extension.ts | 3 - .../toolbar/code-block.extension.ts | 9 + .../{core => toolbar}/heading1.extension.ts | 3 - .../{core => toolbar}/heading2.extension.ts | 3 - .../{core => toolbar}/heading3.extension.ts | 3 - .../toolbar/horizontal-rule.extension.ts | 8 + .../extensions/toolbar/italic.extension.ts | 8 + .../tiptap/extensions/toolbar/manifests.ts | 242 ++++++++++++++++++ .../ordered-list.extension.ts | 0 .../extensions/toolbar/strike.extension.ts | 8 + .../extensions/toolbar/table.extension.ts | 8 + .../text-align-center.extension.ts | 7 - .../text-align-justify.extension.ts | 7 - .../text-align-left.extension.ts | 7 - .../text-align-right.extension.ts | 7 - .../extensions/toolbar/underline.extension.ts | 8 + .../{core => toolbar}/unlink.extension.ts | 4 - src/packages/rte/tiptap/extensions/types.ts | 29 ++- .../extensions/umb/code-editor.extension.ts | 2 - .../umb/embedded-media.extension.ts | 2 - .../tiptap/extensions/umb/link.extension.ts | 4 - 45 files changed, 602 insertions(+), 378 deletions(-) rename src/packages/block/block-rte/tiptap-extension/{block-picker.extension.ts => block-picker-toolbar.extension.ts} (61%) create mode 100644 src/packages/block/block-rte/tiptap-extension/block.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/embedded-media.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/heading.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/link.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/list.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/text-align.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/tiptap-toolbar-extension.ts create mode 100644 src/packages/rte/tiptap/extensions/toolbar/blockquote.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/toolbar/bold.extension.ts rename src/packages/rte/tiptap/extensions/{core => toolbar}/bullet-list.extension.ts (70%) create mode 100644 src/packages/rte/tiptap/extensions/toolbar/code-block.extension.ts rename src/packages/rte/tiptap/extensions/{core => toolbar}/heading1.extension.ts (80%) rename src/packages/rte/tiptap/extensions/{core => toolbar}/heading2.extension.ts (80%) rename src/packages/rte/tiptap/extensions/{core => toolbar}/heading3.extension.ts (80%) create mode 100644 src/packages/rte/tiptap/extensions/toolbar/horizontal-rule.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/toolbar/italic.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/toolbar/manifests.ts rename src/packages/rte/tiptap/extensions/{core => toolbar}/ordered-list.extension.ts (100%) create mode 100644 src/packages/rte/tiptap/extensions/toolbar/strike.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/toolbar/table.extension.ts rename src/packages/rte/tiptap/extensions/{core => toolbar}/text-align-center.extension.ts (65%) rename src/packages/rte/tiptap/extensions/{core => toolbar}/text-align-justify.extension.ts (65%) rename src/packages/rte/tiptap/extensions/{core => toolbar}/text-align-left.extension.ts (65%) rename src/packages/rte/tiptap/extensions/{core => toolbar}/text-align-right.extension.ts (65%) create mode 100644 src/packages/rte/tiptap/extensions/toolbar/underline.extension.ts rename src/packages/rte/tiptap/extensions/{core => toolbar}/unlink.extension.ts (90%) diff --git a/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts b/src/packages/block/block-rte/tiptap-extension/block-picker-toolbar.extension.ts similarity index 61% rename from src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts rename to src/packages/block/block-rte/tiptap-extension/block-picker-toolbar.extension.ts index 42e48b8a7d..52a16ef213 100644 --- a/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts +++ b/src/packages/block/block-rte/tiptap-extension/block-picker-toolbar.extension.ts @@ -4,90 +4,11 @@ import type { UmbBlockDataType } from '../../block/types.js'; import { UMB_DATA_CONTENT_UDI, type UmbBlockRteLayoutModel } from '../types.js'; import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type'; import { UmbTiptapToolbarElementApiBase } from '@umbraco-cms/backoffice/tiptap'; -import { Node, type Editor } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { distinctUntilChanged } from '@umbraco-cms/backoffice/external/rxjs'; -declare module '@tiptap/core' { - interface Commands { - umbRteBlock: { - setBlock: (options: { contentUdi: string }) => ReturnType; - }; - umbRteBlockInline: { - setBlockInline: (options: { contentUdi: string }) => ReturnType; - }; - } -} - -const umbRteBlock = Node.create({ - name: 'umbRteBlock', - group: 'block', - content: undefined, // The block does not have any content, it is just a wrapper. - atom: true, // The block is an atom, meaning it is a single unit that cannot be split. - marks: '', // We do not allow marks on the block - draggable: true, - selectable: true, - - addAttributes() { - return { - [UMB_DATA_CONTENT_UDI]: { - isRequired: true, - }, - }; - }, - - parseHTML() { - return [{ tag: 'umb-rte-block' }]; - }, - - renderHTML({ HTMLAttributes }) { - return ['umb-rte-block', HTMLAttributes]; - }, - - addCommands() { - return { - setBlock: - (options) => - ({ commands }) => { - const attrs = { [UMB_DATA_CONTENT_UDI]: options.contentUdi }; - return commands.insertContent({ - type: this.name, - attrs, - }); - }, - }; - }, -}); - -const umbRteBlockInline = umbRteBlock.extend({ - name: 'umbRteBlockInline', - group: 'inline', - inline: true, - - parseHTML() { - return [{ tag: 'umb-rte-block-inline' }]; - }, - - renderHTML({ HTMLAttributes }) { - return ['umb-rte-block-inline', HTMLAttributes]; - }, - - addCommands() { - return { - setBlockInline: - (options) => - ({ commands }) => { - const attrs = { [UMB_DATA_CONTENT_UDI]: options.contentUdi }; - return commands.insertContent({ - type: this.name, - attrs, - }); - }, - }; - }, -}); - -export default class UmbTiptapBlockPickerExtension extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapBlockPickerToolbarExtension extends UmbTiptapToolbarElementApiBase { #blocks?: Array; #entriesContext?: typeof UMB_BLOCK_RTE_ENTRIES_CONTEXT.TYPE; @@ -117,10 +38,6 @@ export default class UmbTiptapBlockPickerExtension extends UmbTiptapToolbarEleme }); } - getTiptapExtensions() { - return [umbRteBlock, umbRteBlockInline]; - } - override isActive(editor: Editor) { return editor.isActive('umbRteBlock') || editor.isActive('umbRteBlockInline'); } diff --git a/src/packages/block/block-rte/tiptap-extension/block.extension.ts b/src/packages/block/block-rte/tiptap-extension/block.extension.ts new file mode 100644 index 0000000000..c447be0c83 --- /dev/null +++ b/src/packages/block/block-rte/tiptap-extension/block.extension.ts @@ -0,0 +1,88 @@ +import { UMB_DATA_CONTENT_UDI } from '../types.js'; +import { UmbTiptapExtensionApiBase } from '@umbraco-cms/backoffice/tiptap'; +import { Node } from '@umbraco-cms/backoffice/external/tiptap'; + +declare module '@tiptap/core' { + interface Commands { + umbRteBlock: { + setBlock: (options: { contentUdi: string }) => ReturnType; + }; + umbRteBlockInline: { + setBlockInline: (options: { contentUdi: string }) => ReturnType; + }; + } +} + +const umbRteBlock = Node.create({ + name: 'umbRteBlock', + group: 'block', + content: undefined, // The block does not have any content, it is just a wrapper. + atom: true, // The block is an atom, meaning it is a single unit that cannot be split. + marks: '', // We do not allow marks on the block + draggable: true, + selectable: true, + + addAttributes() { + return { + [UMB_DATA_CONTENT_UDI]: { + isRequired: true, + }, + }; + }, + + parseHTML() { + return [{ tag: 'umb-rte-block' }]; + }, + + renderHTML({ HTMLAttributes }) { + return ['umb-rte-block', HTMLAttributes]; + }, + + addCommands() { + return { + setBlock: + (options) => + ({ commands }) => { + const attrs = { [UMB_DATA_CONTENT_UDI]: options.contentUdi }; + return commands.insertContent({ + type: this.name, + attrs, + }); + }, + }; + }, +}); + +const umbRteBlockInline = umbRteBlock.extend({ + name: 'umbRteBlockInline', + group: 'inline', + inline: true, + + parseHTML() { + return [{ tag: 'umb-rte-block-inline' }]; + }, + + renderHTML({ HTMLAttributes }) { + return ['umb-rte-block-inline', HTMLAttributes]; + }, + + addCommands() { + return { + setBlockInline: + (options) => + ({ commands }) => { + const attrs = { [UMB_DATA_CONTENT_UDI]: options.contentUdi }; + return commands.insertContent({ + type: this.name, + attrs, + }); + }, + }; + }, +}); + +export default class UmbTiptapBlockElementApi extends UmbTiptapExtensionApiBase { + getTiptapExtensions() { + return [umbRteBlock, umbRteBlockInline]; + } +} diff --git a/src/packages/block/block-rte/tiptap-extension/manifests.ts b/src/packages/block/block-rte/tiptap-extension/manifests.ts index 5b539cfb80..09d197d539 100644 --- a/src/packages/block/block-rte/tiptap-extension/manifests.ts +++ b/src/packages/block/block-rte/tiptap-extension/manifests.ts @@ -1,12 +1,22 @@ -import type { ManifestTiptapExtensionButtonKind } from '@umbraco-cms/backoffice/tiptap'; +import type { ManifestTiptapExtension } from '@umbraco-cms/backoffice/tiptap'; -export const manifests: ManifestTiptapExtensionButtonKind[] = [ +export const manifests: Array = [ { type: 'tiptapExtension', + alias: 'Umb.Tiptap.Block', + name: 'Block Tiptap Extension', + api: () => import('./block.extension.js'), + meta: { + icon: 'icon-block', + label: 'Block', + }, + }, + { + type: 'tiptapToolbarExtension', kind: 'button', - alias: 'Umb.TiptapExtension.BlockPicker', + alias: 'Umb.Tiptap.Toolbar.BlockPicker', name: 'Block Picker Tiptap Extension Button', - api: () => import('./block-picker.extension.js'), + api: () => import('./block-picker-toolbar.extension.js'), weight: 90, meta: { alias: 'umbblockpicker', diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 777be27462..21b5efde7d 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -96,7 +96,10 @@ export class UmbInputTiptapElement extends UmbFormControlMixin { - this._extensions.forEach((ext) => ext.setEditor(editor)); + this._extensions.forEach((ext) => { + console.log('🚀 ~ this._extensions.forEach ~ ext:', ext); + ext.setEditor(editor); + }); }, onUpdate: ({ editor }) => { this.#markup = editor.getHTML(); diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 457eb7ad8c..9490ad278f 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -1,4 +1,4 @@ -import type { ManifestTiptapExtension } from '../../extensions/tiptap-extension.js'; +import type { ManifestTiptapToolbarExtension } from '../../extensions/tiptap-toolbar-extension.js'; import { css, customElement, html, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; @@ -28,8 +28,8 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { override render() { return html` !!ext.kind || !!ext.element} + type="tiptapToolbarExtension" + .filter=${(ext: ManifestTiptapToolbarExtension) => !!ext.kind || !!ext.element} .elementProps=${{ editor: this.editor }}> `; diff --git a/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts b/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts index d1ecfdfe19..8d605978bd 100644 --- a/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts @@ -1,11 +1,6 @@ -import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import { UmbTiptapExtensionApiBase } from '../types.js'; import { Blockquote } from '@umbraco-cms/backoffice/external/tiptap'; -import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapBlockquoteExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapBlockquoteExtensionApi extends UmbTiptapExtensionApiBase { getTiptapExtensions = () => [Blockquote]; - - override execute(editor?: Editor) { - editor?.chain().focus().toggleBlockquote().run(); - } } diff --git a/src/packages/rte/tiptap/extensions/core/bold.extension.ts b/src/packages/rte/tiptap/extensions/core/bold.extension.ts index e65d167551..824b62c874 100644 --- a/src/packages/rte/tiptap/extensions/core/bold.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/bold.extension.ts @@ -1,11 +1,6 @@ -import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import { UmbTiptapExtensionApiBase } from '../types.js'; import { Bold } from '@umbraco-cms/backoffice/external/tiptap'; -import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapBoldExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapBoldExtensionApi extends UmbTiptapExtensionApiBase { getTiptapExtensions = () => [Bold]; - - override execute(editor?: Editor) { - editor?.chain().focus().toggleBold().run(); - } } diff --git a/src/packages/rte/tiptap/extensions/core/code-block.extension.ts b/src/packages/rte/tiptap/extensions/core/code-block.extension.ts index 7cfa5861b2..2c1310ba3b 100644 --- a/src/packages/rte/tiptap/extensions/core/code-block.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/code-block.extension.ts @@ -1,12 +1,6 @@ -import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import { UmbTiptapExtensionApiBase } from '../types.js'; import { Code, CodeBlock } from '@umbraco-cms/backoffice/external/tiptap'; -import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapCodeBlockExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapCodeBlockExtensionApi extends UmbTiptapExtensionApiBase { getTiptapExtensions = () => [Code, CodeBlock]; - - override execute(editor?: Editor) { - // editor.chain().focus().toggleCode().run(); - editor?.chain().focus().toggleCodeBlock().run(); - } } diff --git a/src/packages/rte/tiptap/extensions/core/embedded-media.extension.ts b/src/packages/rte/tiptap/extensions/core/embedded-media.extension.ts new file mode 100644 index 0000000000..0e64a0153a --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/embedded-media.extension.ts @@ -0,0 +1,6 @@ +import { UmbTiptapExtensionApiBase } from '../types.js'; +import { umbEmbeddedMedia } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapEmbeddedMediaExtensionApi extends UmbTiptapExtensionApiBase { + getTiptapExtensions = () => [umbEmbeddedMedia.configure({ inline: true })]; +} diff --git a/src/packages/rte/tiptap/extensions/core/heading.extension.ts b/src/packages/rte/tiptap/extensions/core/heading.extension.ts new file mode 100644 index 0000000000..255d8832a9 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/heading.extension.ts @@ -0,0 +1,6 @@ +import { UmbTiptapExtensionApiBase } from '../types.js'; +import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapHeading1ExtensionApi extends UmbTiptapExtensionApiBase { + getTiptapExtensions = () => [Heading]; +} diff --git a/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts b/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts index 0219f45673..1d78bc9c96 100644 --- a/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts @@ -1,11 +1,6 @@ -import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import { UmbTiptapExtensionApiBase } from '../types.js'; import { HorizontalRule } from '@umbraco-cms/backoffice/external/tiptap'; -import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapHorizontalRuleExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapHorizontalRuleExtensionApi extends UmbTiptapExtensionApiBase { getTiptapExtensions = () => [HorizontalRule]; - - override execute(editor?: Editor) { - editor?.chain().focus().setHorizontalRule().run(); - } } diff --git a/src/packages/rte/tiptap/extensions/core/italic.extension.ts b/src/packages/rte/tiptap/extensions/core/italic.extension.ts index ff122f81e4..5eccccf8dd 100644 --- a/src/packages/rte/tiptap/extensions/core/italic.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/italic.extension.ts @@ -1,11 +1,6 @@ -import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import { UmbTiptapExtensionApiBase } from '../types.js'; import { Italic } from '@umbraco-cms/backoffice/external/tiptap'; -import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapItalicExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapItalicExtensionApi extends UmbTiptapExtensionApiBase { getTiptapExtensions = () => [Italic]; - - override execute(editor?: Editor) { - editor?.chain().focus().toggleItalic().run(); - } } diff --git a/src/packages/rte/tiptap/extensions/core/link.extension.ts b/src/packages/rte/tiptap/extensions/core/link.extension.ts new file mode 100644 index 0000000000..4800000fda --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/link.extension.ts @@ -0,0 +1,8 @@ +import { UmbTiptapExtensionApiBase } from '../types.js'; +import { UmbLink } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapLinkExtensionApi extends UmbTiptapExtensionApiBase { + getTiptapExtensions() { + return [UmbLink.configure({ openOnClick: false })]; + } +} diff --git a/src/packages/rte/tiptap/extensions/core/list.extension.ts b/src/packages/rte/tiptap/extensions/core/list.extension.ts new file mode 100644 index 0000000000..1391383388 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/list.extension.ts @@ -0,0 +1,6 @@ +import { UmbTiptapExtensionApiBase } from '../types.js'; +import { BulletList, ListItem, OrderedList } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapListExtensionApi extends UmbTiptapExtensionApiBase { + getTiptapExtensions = () => [BulletList, OrderedList, ListItem]; +} diff --git a/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/packages/rte/tiptap/extensions/core/manifests.ts index 3f4b66173e..8131295233 100644 --- a/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/packages/rte/tiptap/extensions/core/manifests.ts @@ -1,6 +1,6 @@ -import type { ManifestTiptapExtension, ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; -export const manifests: Array = [ +export const manifests: Array = [ { type: 'tiptapExtension', kind: 'button', @@ -9,7 +9,6 @@ export const manifests: Array import('./blockquote.extension.js'), weight: 995, meta: { - alias: 'blockquote', icon: 'icon-blockquote', label: 'Blockquote', }, @@ -22,24 +21,10 @@ export const manifests: Array import('./bold.extension.js'), weight: 999, meta: { - alias: 'bold', icon: 'icon-bold', label: 'Bold', }, }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.BulletList', - name: 'Bullet List Tiptap Extension', - api: () => import('./bullet-list.extension.js'), - weight: 993, - meta: { - alias: 'bulletList', - icon: 'icon-bulleted-list', - label: 'Bullet List', - }, - }, { type: 'tiptapExtension', kind: 'button', @@ -48,60 +33,41 @@ export const manifests: Array import('./code-block.extension.js'), weight: 994, meta: { - alias: 'codeBlock', icon: 'icon-code', label: 'Code Block', }, }, { type: 'tiptapExtension', - alias: 'Umb.Tiptap.Figure', - name: 'Figure Tiptap Extension', - api: () => import('./figure.extension.js'), - weight: 955, - meta: { - alias: 'figure', - icon: 'icon-frame', - label: 'Figure', - }, - }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Heading1', - name: 'Heading 1 Tiptap Extension', - api: () => import('./heading1.extension.js'), - weight: 949, + alias: 'Umb.Tiptap.Embed', + name: 'Embed Tiptap Extension', + api: () => import('./embedded-media.extension.js'), + weight: 70, meta: { - alias: 'heading1', - icon: 'icon-heading-1', - label: 'Heading 1', + icon: 'icon-embed', + label: '#general_embed', }, }, { type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Heading2', - name: 'Heading 2 Tiptap Extension', - api: () => import('./heading2.extension.js'), - weight: 948, + alias: 'Umb.Tiptap.Link', + name: 'Link Tiptap Extension', + api: () => import('./link.extension.js'), + weight: 102, meta: { - alias: 'heading2', - icon: 'icon-heading-2', - label: 'Heading 2', + icon: 'icon-link', + label: '#defaultdialogs_urlLinkPicker', }, }, { type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Heading3', - name: 'Heading 3 Tiptap Extension', - api: () => import('./heading3.extension.js'), - weight: 947, + alias: 'Umb.Tiptap.Figure', + name: 'Figure Tiptap Extension', + api: () => import('./figure.extension.js'), + weight: 955, meta: { - alias: 'heading3', - icon: 'icon-heading-3', - label: 'Heading 3', + icon: 'icon-frame', + label: 'Figure', }, }, { @@ -112,7 +78,6 @@ export const manifests: Array import('./horizontal-rule.extension.js'), weight: 991, meta: { - alias: 'horizontalRule', icon: 'icon-horizontal-rule', label: 'Horizontal Rule', }, @@ -123,7 +88,8 @@ export const manifests: Array import('./image.extension.js'), meta: { - alias: 'image', + icon: 'icon-picture', + label: 'Image', }, }, { @@ -134,24 +100,10 @@ export const manifests: Array import('./italic.extension.js'), weight: 998, meta: { - alias: 'italic', icon: 'icon-italic', label: 'Italic', }, }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.OrderedList', - name: 'Ordered List Tiptap Extension', - api: () => import('./ordered-list.extension.js'), - weight: 992, - meta: { - alias: 'orderedList', - icon: 'icon-ordered-list', - label: 'Ordered List', - }, - }, { type: 'tiptapExtension', kind: 'button', @@ -160,7 +112,6 @@ export const manifests: Array import('./strike.extension.js'), weight: 996, meta: { - alias: 'strike', icon: 'icon-strikethrough', label: 'Strike', }, @@ -173,7 +124,6 @@ export const manifests: Array import('./table.extension.js'), weight: 909, meta: { - alias: 'table', icon: 'icon-table', label: 'Table', }, @@ -181,80 +131,43 @@ export const manifests: Array import('./text-align-center.extension.js'), - weight: 918, - meta: { - alias: 'text-align-center', - icon: 'icon-text-align-center', - label: 'Text Align Center', - }, - }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.TextAlignJustify', - name: 'Text Align Justify Tiptap Extension', - api: () => import('./text-align-justify.extension.js'), - weight: 916, - meta: { - alias: 'text-align-justify', - icon: 'icon-text-align-justify', - label: 'Text Align Justify', - }, - }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.TextAlignLeft', - name: 'Text Align Left Tiptap Extension', - api: () => import('./text-align-left.extension.js'), - weight: 919, + alias: 'Umb.Tiptap.Underline', + name: 'Underline Tiptap Extension', + api: () => import('./underline.extension.js'), + weight: 997, meta: { - alias: 'text-align-left', - icon: 'icon-text-align-left', - label: 'Text Align Left', + icon: 'icon-underline', + label: 'Underline', }, }, { type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.TextAlignRight', - name: 'Text Align Right Tiptap Extension', - api: () => import('./text-align-right.extension.js'), - weight: 917, + alias: 'Umb.Tiptap.Heading', + name: 'Heading Tiptap Extension', + api: () => import('./heading.extension.js'), meta: { - alias: 'text-align-right', - icon: 'icon-text-align-right', - label: 'Text Align Right', + icon: 'icon-heading-1', + label: 'Heading', }, }, { type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Underline', - name: 'Underline Tiptap Extension', - api: () => import('./underline.extension.js'), - weight: 997, + alias: 'Umb.Tiptap.List', + name: 'List Tiptap Extension', + api: () => import('./list.extension.js'), meta: { - alias: 'underline', - icon: 'icon-underline', - label: 'Underline', + icon: 'icon-ordered-list', + label: 'Ordered List', }, }, { type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Unlink', - name: 'Unlink Tiptap Extension', - api: () => import('./unlink.extension.js'), - element: () => import('../../components/toolbar/tiptap-toolbar-button-disabled.element.js'), - weight: 101, + alias: 'Umb.Tiptap.TextAlign', + name: 'Text Align Tiptap Extension', + api: () => import('./text-align.extension.js'), meta: { - alias: 'unlink', - icon: 'icon-unlink', - label: 'Unlink', + icon: 'icon-text-align-justify', + label: 'Text Align', }, }, ]; diff --git a/src/packages/rte/tiptap/extensions/core/strike.extension.ts b/src/packages/rte/tiptap/extensions/core/strike.extension.ts index b073c12dd9..88096bacfe 100644 --- a/src/packages/rte/tiptap/extensions/core/strike.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/strike.extension.ts @@ -1,11 +1,6 @@ -import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import { UmbTiptapExtensionApiBase } from '../types.js'; import { Strike } from '@umbraco-cms/backoffice/external/tiptap'; -import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapStrikeExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapStrikeExtensionApi extends UmbTiptapExtensionApiBase { getTiptapExtensions = () => [Strike]; - - override execute(editor?: Editor) { - editor?.chain().focus().toggleStrike().run(); - } } diff --git a/src/packages/rte/tiptap/extensions/core/table.extension.ts b/src/packages/rte/tiptap/extensions/core/table.extension.ts index ae2d71fa73..d8c00b5d39 100644 --- a/src/packages/rte/tiptap/extensions/core/table.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/table.extension.ts @@ -1,11 +1,6 @@ -import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import { UmbTiptapExtensionApiBase } from '../types.js'; import { Table, TableHeader, TableRow, TableCell } from '@umbraco-cms/backoffice/external/tiptap'; -import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapTableExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapTableExtensionApi extends UmbTiptapExtensionApiBase { getTiptapExtensions = () => [Table.configure({ resizable: true }), TableHeader, TableRow, TableCell]; - - override execute(editor?: Editor) { - editor?.commands.insertTable({ rows: 3, cols: 3, withHeaderRow: true }); - } } diff --git a/src/packages/rte/tiptap/extensions/core/text-align.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align.extension.ts new file mode 100644 index 0000000000..3d902dd62a --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/text-align.extension.ts @@ -0,0 +1,10 @@ +import { UmbTiptapExtensionApiBase } from '../types.js'; +import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapTextAlignCenterExtensionApi extends UmbTiptapExtensionApiBase { + getTiptapExtensions = () => [ + TextAlign.configure({ + types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], + }), + ]; +} diff --git a/src/packages/rte/tiptap/extensions/core/underline.extension.ts b/src/packages/rte/tiptap/extensions/core/underline.extension.ts index 4e1bac6d6a..f9663f91c8 100644 --- a/src/packages/rte/tiptap/extensions/core/underline.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/underline.extension.ts @@ -1,11 +1,6 @@ -import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import { UmbTiptapExtensionApiBase } from '../types.js'; import { Underline } from '@umbraco-cms/backoffice/external/tiptap'; -import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapUnderlineExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapUnderlineExtensionApi extends UmbTiptapExtensionApiBase { getTiptapExtensions = () => [Underline]; - - override execute(editor?: Editor) { - editor?.chain().focus().toggleUnderline().run(); - } } diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index 7fc3abe7c2..39f1379c12 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -1,5 +1,10 @@ -import type { ManifestTiptapExtension, ManifestTiptapExtensionButtonKind } from './tiptap-extension.js'; +import type { ManifestTiptapExtension } from './tiptap-extension.js'; import { manifests as core } from './core/manifests.js'; +import { manifests as toolbar } from './toolbar/manifests.js'; +import type { + ManifestTiptapToolbarExtension, + ManifestTiptapToolbarExtensionButtonKind, +} from './tiptap-toolbar-extension.js'; import type { ManifestTypes, UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; const kinds: Array = [ @@ -7,18 +12,18 @@ const kinds: Array = [ type: 'kind', alias: 'Umb.Kind.Button', matchKind: 'button', - matchType: 'tiptapExtension', + matchType: 'tiptapToolbarExtension', manifest: { element: () => import('../components/toolbar/tiptap-toolbar-button.element.js'), }, }, ]; -const umbExtensions: Array = [ +const umbToolbarExtensions: Array = [ { - type: 'tiptapExtension', + type: 'tiptapToolbarExtension', kind: 'button', - alias: 'Umb.Tiptap.CodeEditor', + alias: 'Umb.Tiptap.Toolbar.CodeEditor', name: 'Code Editor Tiptap Extension', api: () => import('./umb/code-editor.extension.js'), weight: 1000, @@ -29,21 +34,9 @@ const umbExtensions: Array import('./umb/media-upload.extension.js'), - weight: 900, - meta: { - alias: 'umbMediaUpload', - icon: 'icon-image-up', - label: 'Media upload', - }, - }, - { - type: 'tiptapExtension', + type: 'tiptapToolbarExtension', kind: 'button', - alias: 'Umb.Tiptap.Link', + alias: 'Umb.Tiptap.Toolbar.Link', name: 'Link Tiptap Extension', api: () => import('./umb/link.extension.js'), weight: 102, @@ -54,9 +47,9 @@ const umbExtensions: Array import('./umb/mediapicker.extension.js'), weight: 80, @@ -67,9 +60,9 @@ const umbExtensions: Array import('./umb/embedded-media.extension.js'), weight: 70, @@ -81,6 +74,24 @@ const umbExtensions: Array = [...core, ...umbExtensions]; +const umbExtensions: Array = [ + { + type: 'tiptapExtension', + alias: 'Umb.Tiptap.MediaUpload', + name: 'Media Upload Tiptap Extension', + api: () => import('./umb/media-upload.extension.js'), + meta: { + icon: 'icon-image-up', + label: 'Media upload', + }, + }, +]; + +const extensions: Array = [ + ...core, + ...toolbar, + ...umbToolbarExtensions, + ...umbExtensions, +]; export const manifests: Array = [...kinds, ...extensions]; diff --git a/src/packages/rte/tiptap/extensions/tiptap-extension.ts b/src/packages/rte/tiptap/extensions/tiptap-extension.ts index 5ecb61da5d..bb3dcd3f88 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-extension.ts @@ -1,31 +1,19 @@ import type { UmbTiptapExtensionApi } from './types.js'; -import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; -import type { ManifestElementAndApi } from '@umbraco-cms/backoffice/extension-api'; +import type { ManifestApi } from '@umbraco-cms/backoffice/extension-api'; export interface ManifestTiptapExtension - extends ManifestElementAndApi { + extends ManifestApi { type: 'tiptapExtension'; meta: MetaType; } export interface MetaTiptapExtension { - alias: string; -} - -export interface ManifestTiptapExtensionButtonKind< - MetaType extends MetaTiptapExtensionButtonKind = MetaTiptapExtensionButtonKind, -> extends ManifestTiptapExtension { - type: 'tiptapExtension'; - kind: 'button'; -} - -export interface MetaTiptapExtensionButtonKind extends MetaTiptapExtension { icon: string; label: string; } declare global { interface UmbExtensionManifestMap { - tiptapExtension: ManifestTiptapExtension | ManifestTiptapExtensionButtonKind; + tiptapExtension: ManifestTiptapExtension; } } diff --git a/src/packages/rte/tiptap/extensions/tiptap-toolbar-extension.ts b/src/packages/rte/tiptap/extensions/tiptap-toolbar-extension.ts new file mode 100644 index 0000000000..a97d658328 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/tiptap-toolbar-extension.ts @@ -0,0 +1,29 @@ +import type { UmbTiptapToolbarElementApi } from './types.js'; +import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import type { ManifestElementAndApi } from '@umbraco-cms/backoffice/extension-api'; + +export interface ManifestTiptapToolbarExtension< + MetaType extends MetaTiptapToolbarExtension = MetaTiptapToolbarExtension, +> extends ManifestElementAndApi { + type: 'tiptapToolbarExtension'; + meta: MetaType; +} + +export interface MetaTiptapToolbarExtension { + alias: string; + icon: string; + label: string; +} + +export interface ManifestTiptapToolbarExtensionButtonKind< + MetaType extends MetaTiptapToolbarExtension = MetaTiptapToolbarExtension, +> extends ManifestTiptapToolbarExtension { + type: 'tiptapToolbarExtension'; + kind: 'button'; +} + +declare global { + interface UmbExtensionManifestMap { + tiptapToolbarExtension: ManifestTiptapToolbarExtension | ManifestTiptapToolbarExtensionButtonKind; + } +} diff --git a/src/packages/rte/tiptap/extensions/toolbar/blockquote.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/blockquote.extension.ts new file mode 100644 index 0000000000..4dc06019d6 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/toolbar/blockquote.extension.ts @@ -0,0 +1,8 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapBlockquoteExtensionApi extends UmbTiptapToolbarElementApiBase { + override execute(editor?: Editor) { + editor?.chain().focus().toggleBlockquote().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/toolbar/bold.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/bold.extension.ts new file mode 100644 index 0000000000..8ee5950edd --- /dev/null +++ b/src/packages/rte/tiptap/extensions/toolbar/bold.extension.ts @@ -0,0 +1,8 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapBoldExtensionApi extends UmbTiptapToolbarElementApiBase { + override execute(editor?: Editor) { + editor?.chain().focus().toggleBold().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/bullet-list.extension.ts similarity index 70% rename from src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts rename to src/packages/rte/tiptap/extensions/toolbar/bullet-list.extension.ts index 8dd956279c..4212b563e0 100644 --- a/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/bullet-list.extension.ts @@ -1,10 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import { BulletList, ListItem } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapBulletListExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions = () => [BulletList, ListItem]; - override execute(editor?: Editor) { editor?.chain().focus().toggleBulletList().run(); } diff --git a/src/packages/rte/tiptap/extensions/toolbar/code-block.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/code-block.extension.ts new file mode 100644 index 0000000000..65273349ae --- /dev/null +++ b/src/packages/rte/tiptap/extensions/toolbar/code-block.extension.ts @@ -0,0 +1,9 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapCodeBlockExtensionApi extends UmbTiptapToolbarElementApiBase { + override execute(editor?: Editor) { + // editor.chain().focus().toggleCode().run(); + editor?.chain().focus().toggleCodeBlock().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/heading1.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/heading1.extension.ts similarity index 80% rename from src/packages/rte/tiptap/extensions/core/heading1.extension.ts rename to src/packages/rte/tiptap/extensions/toolbar/heading1.extension.ts index 7543e321fb..331c0b7136 100644 --- a/src/packages/rte/tiptap/extensions/core/heading1.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/heading1.extension.ts @@ -1,10 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapHeading1ExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions = () => [Heading]; - override isActive(editor?: Editor) { return editor?.isActive('heading', { level: 1 }) === true; } diff --git a/src/packages/rte/tiptap/extensions/core/heading2.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/heading2.extension.ts similarity index 80% rename from src/packages/rte/tiptap/extensions/core/heading2.extension.ts rename to src/packages/rte/tiptap/extensions/toolbar/heading2.extension.ts index 3edcf7b57a..cfe4304ef0 100644 --- a/src/packages/rte/tiptap/extensions/core/heading2.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/heading2.extension.ts @@ -1,10 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapHeading2ExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions = () => [Heading]; - override isActive(editor?: Editor) { return editor?.isActive('heading', { level: 2 }) === true; } diff --git a/src/packages/rte/tiptap/extensions/core/heading3.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/heading3.extension.ts similarity index 80% rename from src/packages/rte/tiptap/extensions/core/heading3.extension.ts rename to src/packages/rte/tiptap/extensions/toolbar/heading3.extension.ts index 9def84dc2c..032c62fbf3 100644 --- a/src/packages/rte/tiptap/extensions/core/heading3.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/heading3.extension.ts @@ -1,10 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapHeading3ExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions = () => [Heading]; - override isActive(editor?: Editor) { return editor?.isActive('heading', { level: 3 }) === true; } diff --git a/src/packages/rte/tiptap/extensions/toolbar/horizontal-rule.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/horizontal-rule.extension.ts new file mode 100644 index 0000000000..1d7ab6456c --- /dev/null +++ b/src/packages/rte/tiptap/extensions/toolbar/horizontal-rule.extension.ts @@ -0,0 +1,8 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapHorizontalRuleExtensionApi extends UmbTiptapToolbarElementApiBase { + override execute(editor?: Editor) { + editor?.chain().focus().setHorizontalRule().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/toolbar/italic.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/italic.extension.ts new file mode 100644 index 0000000000..6b4e7465c8 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/toolbar/italic.extension.ts @@ -0,0 +1,8 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapItalicExtensionApi extends UmbTiptapToolbarElementApiBase { + override execute(editor?: Editor) { + editor?.chain().focus().toggleItalic().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/toolbar/manifests.ts b/src/packages/rte/tiptap/extensions/toolbar/manifests.ts new file mode 100644 index 0000000000..cc39270567 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/toolbar/manifests.ts @@ -0,0 +1,242 @@ +import type { + ManifestTiptapToolbarExtension, + ManifestTiptapToolbarExtensionButtonKind, +} from '../tiptap-toolbar-extension.js'; + +export const manifests: Array = [ + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Blockquote', + name: 'Blockquote Tiptap Extension', + api: () => import('./blockquote.extension.js'), + weight: 995, + meta: { + alias: 'blockquote', + icon: 'icon-blockquote', + label: 'Blockquote', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Bold', + name: 'Bold Tiptap Extension', + api: () => import('./bold.extension.js'), + weight: 999, + meta: { + alias: 'bold', + icon: 'icon-bold', + label: 'Bold', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.CodeBlock', + name: 'Code Block Tiptap Extension', + api: () => import('./code-block.extension.js'), + weight: 994, + meta: { + alias: 'codeBlock', + icon: 'icon-code', + label: 'Code Block', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.BulletList', + name: 'Bullet List Tiptap Extension', + api: () => import('./bullet-list.extension.js'), + weight: 993, + meta: { + alias: 'bulletList', + icon: 'icon-bulleted-list', + label: 'Bullet List', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.OrderedList', + name: 'Ordered List Tiptap Extension', + api: () => import('./ordered-list.extension.js'), + weight: 992, + meta: { + alias: 'orderedList', + icon: 'icon-ordered-list', + label: 'Ordered List', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Strike', + name: 'Strike Tiptap Extension', + api: () => import('./strike.extension.js'), + weight: 996, + meta: { + alias: 'strike', + icon: 'icon-strikethrough', + label: 'Strike', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Table', + name: 'Table Tiptap Extension', + api: () => import('./table.extension.js'), + weight: 909, + meta: { + alias: 'table', + icon: 'icon-table', + label: 'Table', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Heading1', + name: 'Heading 1 Tiptap Extension', + api: () => import('./heading1.extension.js'), + weight: 949, + meta: { + alias: 'heading1', + icon: 'icon-heading-1', + label: 'Heading 1', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Heading2', + name: 'Heading 2 Tiptap Extension', + api: () => import('./heading2.extension.js'), + weight: 948, + meta: { + alias: 'heading2', + icon: 'icon-heading-2', + label: 'Heading 2', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Heading3', + name: 'Heading 3 Tiptap Extension', + api: () => import('./heading3.extension.js'), + weight: 947, + meta: { + alias: 'heading3', + icon: 'icon-heading-3', + label: 'Heading 3', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.HorizontalRule', + name: 'Horizontal Rule Tiptap Extension', + api: () => import('./horizontal-rule.extension.js'), + weight: 991, + meta: { + alias: 'horizontalRule', + icon: 'icon-horizontal-rule', + label: 'Horizontal Rule', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Italic', + name: 'Italic Tiptap Extension', + api: () => import('./italic.extension.js'), + weight: 998, + meta: { + alias: 'italic', + icon: 'icon-italic', + label: 'Italic', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.TextAlignCenter', + name: 'Text Align Center Tiptap Extension', + api: () => import('./text-align-center.extension.js'), + weight: 918, + meta: { + alias: 'text-align-center', + icon: 'icon-text-align-center', + label: 'Text Align Center', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.TextAlignJustify', + name: 'Text Align Justify Tiptap Extension', + api: () => import('./text-align-justify.extension.js'), + weight: 916, + meta: { + alias: 'text-align-justify', + icon: 'icon-text-align-justify', + label: 'Text Align Justify', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.TextAlignLeft', + name: 'Text Align Left Tiptap Extension', + api: () => import('./text-align-left.extension.js'), + weight: 919, + meta: { + alias: 'text-align-left', + icon: 'icon-text-align-left', + label: 'Text Align Left', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.TextAlignRight', + name: 'Text Align Right Tiptap Extension', + api: () => import('./text-align-right.extension.js'), + weight: 917, + meta: { + alias: 'text-align-right', + icon: 'icon-text-align-right', + label: 'Text Align Right', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Underline', + name: 'Underline Tiptap Extension', + api: () => import('./underline.extension.js'), + weight: 997, + meta: { + alias: 'underline', + icon: 'icon-underline', + label: 'Underline', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Unlink', + name: 'Unlink Tiptap Extension', + api: () => import('./unlink.extension.js'), + element: () => import('../../components/toolbar/tiptap-toolbar-button-disabled.element.js'), + weight: 101, + meta: { + alias: 'unlink', + icon: 'icon-unlink', + label: 'Unlink', + }, + }, +]; diff --git a/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/ordered-list.extension.ts similarity index 100% rename from src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts rename to src/packages/rte/tiptap/extensions/toolbar/ordered-list.extension.ts diff --git a/src/packages/rte/tiptap/extensions/toolbar/strike.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/strike.extension.ts new file mode 100644 index 0000000000..2f4f284633 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/toolbar/strike.extension.ts @@ -0,0 +1,8 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapStrikeExtensionApi extends UmbTiptapToolbarElementApiBase { + override execute(editor?: Editor) { + editor?.chain().focus().toggleStrike().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/toolbar/table.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/table.extension.ts new file mode 100644 index 0000000000..ee997c7280 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/toolbar/table.extension.ts @@ -0,0 +1,8 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapTableExtensionApi extends UmbTiptapToolbarElementApiBase { + override execute(editor?: Editor) { + editor?.commands.insertTable({ rows: 3, cols: 3, withHeaderRow: true }); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/text-align-center.extension.ts similarity index 65% rename from src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts rename to src/packages/rte/tiptap/extensions/toolbar/text-align-center.extension.ts index fa9c90855c..50eb9ccaf3 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/text-align-center.extension.ts @@ -1,14 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapTextAlignCenterExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions = () => [ - TextAlign.configure({ - types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], - }), - ]; - override isActive(editor?: Editor) { return editor?.isActive({ textAlign: 'center' }) === true; } diff --git a/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/text-align-justify.extension.ts similarity index 65% rename from src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts rename to src/packages/rte/tiptap/extensions/toolbar/text-align-justify.extension.ts index 03e197654f..c7e71ca6a7 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/text-align-justify.extension.ts @@ -1,14 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapTextAlignJustifyExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions = () => [ - TextAlign.configure({ - types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], - }), - ]; - override isActive(editor?: Editor) { return editor?.isActive({ textAlign: 'justify' }) === true; } diff --git a/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/text-align-left.extension.ts similarity index 65% rename from src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts rename to src/packages/rte/tiptap/extensions/toolbar/text-align-left.extension.ts index 2f35da46d2..4f3b0f87d6 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/text-align-left.extension.ts @@ -1,14 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapTextAlignLeftExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions = () => [ - TextAlign.configure({ - types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], - }), - ]; - override isActive(editor?: Editor) { return editor?.isActive({ textAlign: 'left' }) === true; } diff --git a/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/text-align-right.extension.ts similarity index 65% rename from src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts rename to src/packages/rte/tiptap/extensions/toolbar/text-align-right.extension.ts index 62de9a54f9..0cf0313208 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/text-align-right.extension.ts @@ -1,14 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapTextAlignRightExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions = () => [ - TextAlign.configure({ - types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], - }), - ]; - override isActive(editor?: Editor) { return editor?.isActive({ textAlign: 'right' }) === true; } diff --git a/src/packages/rte/tiptap/extensions/toolbar/underline.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/underline.extension.ts new file mode 100644 index 0000000000..82436dc55a --- /dev/null +++ b/src/packages/rte/tiptap/extensions/toolbar/underline.extension.ts @@ -0,0 +1,8 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapUnderlineExtensionApi extends UmbTiptapToolbarElementApiBase { + override execute(editor?: Editor) { + editor?.chain().focus().toggleUnderline().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/core/unlink.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/unlink.extension.ts similarity index 90% rename from src/packages/rte/tiptap/extensions/core/unlink.extension.ts rename to src/packages/rte/tiptap/extensions/toolbar/unlink.extension.ts index 5645c154bc..fcf5f60c78 100644 --- a/src/packages/rte/tiptap/extensions/core/unlink.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/unlink.extension.ts @@ -2,10 +2,6 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapUnlinkExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions() { - return []; - } - override isActive = (editor?: Editor) => editor?.isActive('umbLink') ?? false; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/types.ts b/src/packages/rte/tiptap/extensions/types.ts index 919bd3092b..d297efd984 100644 --- a/src/packages/rte/tiptap/extensions/types.ts +++ b/src/packages/rte/tiptap/extensions/types.ts @@ -1,10 +1,16 @@ import type { ManifestTiptapExtension } from './tiptap-extension.js'; +import type { ManifestTiptapToolbarExtension } from './tiptap-toolbar-extension.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { Editor, Extension, Mark, Node } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; export interface UmbTiptapExtensionApi extends UmbApi { + /** + * The manifest for the extension. + */ + manifest?: ManifestTiptapExtension; + /** * Sets the editor instance to the extension. */ @@ -20,7 +26,7 @@ export abstract class UmbTiptapExtensionApiBase extends UmbControllerBase implem /** * The manifest for the extension. */ - protected manifest?: ManifestTiptapExtension; + manifest?: ManifestTiptapExtension; /** * The editor instance. @@ -49,7 +55,12 @@ export interface UmbTiptapExtensionArgs { configuration?: UmbPropertyEditorConfigCollection; } -export interface UmbTiptapToolbarElementApi extends UmbTiptapExtensionApi { +export interface UmbTiptapToolbarElementApi extends UmbApi { + /** + * The manifest for the extension. + */ + manifest?: ManifestTiptapToolbarExtension; + /** * Executes the toolbar element action. */ @@ -61,10 +72,12 @@ export interface UmbTiptapToolbarElementApi extends UmbTiptapExtensionApi { isActive(editor: Editor): boolean; } -export abstract class UmbTiptapToolbarElementApiBase - extends UmbTiptapExtensionApiBase - implements UmbTiptapToolbarElementApi -{ +export abstract class UmbTiptapToolbarElementApiBase extends UmbControllerBase implements UmbTiptapToolbarElementApi { + /** + * The manifest for the extension. + */ + manifest?: ManifestTiptapToolbarExtension; + /** * A method to execute the toolbar element action. */ @@ -72,7 +85,9 @@ export abstract class UmbTiptapToolbarElementApiBase /** * Informs the toolbar element if it is active or not. It uses the manifest meta alias to check if the toolbar element is active. - * @see {ManifestTiptapExtension} + * @see {ManifestTiptapToolbarExtension} + * @param {Editor} editor The editor instance. + * @returns {boolean} Returns true if the toolbar element is active. */ public isActive(editor: Editor) { return editor && this.manifest?.meta.alias ? editor?.isActive(this.manifest.meta.alias) : false; diff --git a/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts b/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts index 32722af243..2c3c8c69c8 100644 --- a/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts @@ -4,8 +4,6 @@ import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; export default class UmbTiptapCodeEditorExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions = () => []; - override async execute(editor?: Editor) { console.log('umb-code-editor.execute', editor); if (!editor) return; diff --git a/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts b/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts index 9d0a1f71c8..b439a18101 100644 --- a/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts @@ -5,8 +5,6 @@ import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapEmbedExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions = () => [umbEmbeddedMedia.configure({ inline: true })]; - override isActive = (editor: Editor) => editor.isActive(umbEmbeddedMedia.name) === true; override async execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/umb/link.extension.ts b/src/packages/rte/tiptap/extensions/umb/link.extension.ts index e7ef26bf85..d15602f0c7 100644 --- a/src/packages/rte/tiptap/extensions/umb/link.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/link.extension.ts @@ -6,10 +6,6 @@ import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbLinkPickerLink } from '@umbraco-cms/backoffice/multi-url-picker'; export default class UmbTiptapLinkExtensionApi extends UmbTiptapToolbarElementApiBase { - getTiptapExtensions() { - return [UmbLink.configure({ openOnClick: false })]; - } - override async execute(editor?: Editor) { const attrs = editor?.getAttributes(UmbLink.name) ?? {}; const link = this.#getLinkData(attrs); From a1015f17625eb62454f0650d5170da13cf12541b Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 13:33:50 +0200 Subject: [PATCH 171/241] feat: export types --- src/packages/rte/tiptap/extensions/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/packages/rte/tiptap/extensions/index.ts b/src/packages/rte/tiptap/extensions/index.ts index 20b2f7a29a..8f09498433 100644 --- a/src/packages/rte/tiptap/extensions/index.ts +++ b/src/packages/rte/tiptap/extensions/index.ts @@ -1,2 +1,3 @@ export type * from './tiptap-extension.js'; +export type * from './tiptap-toolbar-extension.js'; export * from './types.js'; From 7f811c8acff0905d0c448c625d80ec1a9553d002 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 13:33:57 +0200 Subject: [PATCH 172/241] feat: add block types --- .../block-picker-toolbar.extension.ts | 34 -------------- .../tiptap-extension/block.extension.ts | 44 ++++++++++++++++++- .../block-rte/tiptap-extension/manifests.ts | 2 +- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/src/packages/block/block-rte/tiptap-extension/block-picker-toolbar.extension.ts b/src/packages/block/block-rte/tiptap-extension/block-picker-toolbar.extension.ts index 52a16ef213..241eebbb9c 100644 --- a/src/packages/block/block-rte/tiptap-extension/block-picker-toolbar.extension.ts +++ b/src/packages/block/block-rte/tiptap-extension/block-picker-toolbar.extension.ts @@ -1,12 +1,9 @@ import { UMB_BLOCK_RTE_MANAGER_CONTEXT } from '../context/block-rte-manager.context-token.js'; import { UMB_BLOCK_RTE_ENTRIES_CONTEXT } from '../context/block-rte-entries.context-token.js'; -import type { UmbBlockDataType } from '../../block/types.js'; -import { UMB_DATA_CONTENT_UDI, type UmbBlockRteLayoutModel } from '../types.js'; import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type'; import { UmbTiptapToolbarElementApiBase } from '@umbraco-cms/backoffice/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { distinctUntilChanged } from '@umbraco-cms/backoffice/external/rxjs'; export default class UmbTiptapBlockPickerToolbarExtension extends UmbTiptapToolbarElementApiBase { #blocks?: Array; @@ -23,15 +20,6 @@ export default class UmbTiptapBlockPickerToolbarExtension extends UmbTiptapToolb }, 'blockType', ); - this.observe( - context.contents.pipe( - distinctUntilChanged((prev, curr) => prev.map((y) => y.udi).join() === curr.map((y) => y.udi).join()), - ), - (contents) => { - this.#updateBlocks(contents, context.getLayouts()); - }, - 'contents', - ); }); this.consumeContext(UMB_BLOCK_RTE_ENTRIES_CONTEXT, (context) => { this.#entriesContext = context; @@ -65,26 +53,4 @@ export default class UmbTiptapBlockPickerToolbarExtension extends UmbTiptapToolb window.history.pushState({}, '', createPath); } } - - #updateBlocks(blocks: UmbBlockDataType[], layouts: Array) { - const editor = this._editor; - if (!editor) return; - - const existingBlocks = Array.from(editor.view.dom.querySelectorAll('umb-rte-block, umb-rte-block-inline')).map( - (x) => x.getAttribute(UMB_DATA_CONTENT_UDI), - ); - const newBlocks = blocks.filter((x) => !existingBlocks.find((contentUdi) => contentUdi === x.udi)); - - newBlocks.forEach((block) => { - // Find layout for block - const layout = layouts.find((x) => x.contentUdi === block.udi); - const inline = layout?.displayInline ?? false; - - if (inline) { - editor.commands.setBlockInline({ contentUdi: block.udi }); - } else { - editor.commands.setBlock({ contentUdi: block.udi }); - } - }); - } } diff --git a/src/packages/block/block-rte/tiptap-extension/block.extension.ts b/src/packages/block/block-rte/tiptap-extension/block.extension.ts index c447be0c83..f60235b15e 100644 --- a/src/packages/block/block-rte/tiptap-extension/block.extension.ts +++ b/src/packages/block/block-rte/tiptap-extension/block.extension.ts @@ -1,6 +1,10 @@ -import { UMB_DATA_CONTENT_UDI } from '../types.js'; +import { UMB_DATA_CONTENT_UDI, type UmbBlockRteLayoutModel } from '../types.js'; +import { UMB_BLOCK_RTE_MANAGER_CONTEXT } from '../context/index.js'; import { UmbTiptapExtensionApiBase } from '@umbraco-cms/backoffice/tiptap'; import { Node } from '@umbraco-cms/backoffice/external/tiptap'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { distinctUntilChanged } from '@umbraco-cms/backoffice/external/rxjs'; +import type { UmbBlockDataType } from '@umbraco-cms/backoffice/block'; declare module '@tiptap/core' { interface Commands { @@ -82,7 +86,45 @@ const umbRteBlockInline = umbRteBlock.extend({ }); export default class UmbTiptapBlockElementApi extends UmbTiptapExtensionApiBase { + constructor(host: UmbControllerHost) { + super(host); + + this.consumeContext(UMB_BLOCK_RTE_MANAGER_CONTEXT, (context) => { + this.observe( + context.contents.pipe( + distinctUntilChanged((prev, curr) => prev.map((y) => y.udi).join() === curr.map((y) => y.udi).join()), + ), + (contents) => { + this.#updateBlocks(contents, context.getLayouts()); + }, + 'contents', + ); + }); + } + getTiptapExtensions() { return [umbRteBlock, umbRteBlockInline]; } + + #updateBlocks(blocks: UmbBlockDataType[], layouts: Array) { + const editor = this._editor; + if (!editor) return; + + const existingBlocks = Array.from(editor.view.dom.querySelectorAll('umb-rte-block, umb-rte-block-inline')).map( + (x) => x.getAttribute(UMB_DATA_CONTENT_UDI), + ); + const newBlocks = blocks.filter((x) => !existingBlocks.find((contentUdi) => contentUdi === x.udi)); + + newBlocks.forEach((block) => { + // Find layout for block + const layout = layouts.find((x) => x.contentUdi === block.udi); + const inline = layout?.displayInline ?? false; + + if (inline) { + editor.commands.setBlockInline({ contentUdi: block.udi }); + } else { + editor.commands.setBlock({ contentUdi: block.udi }); + } + }); + } } diff --git a/src/packages/block/block-rte/tiptap-extension/manifests.ts b/src/packages/block/block-rte/tiptap-extension/manifests.ts index 09d197d539..b62c0f0e5c 100644 --- a/src/packages/block/block-rte/tiptap-extension/manifests.ts +++ b/src/packages/block/block-rte/tiptap-extension/manifests.ts @@ -1,4 +1,4 @@ -import type { ManifestTiptapExtension } from '@umbraco-cms/backoffice/tiptap'; +import type { ManifestTiptapExtension, ManifestTiptapToolbarExtensionButtonKind } from '@umbraco-cms/backoffice/tiptap'; export const manifests: Array = [ { From 99d467137488136d968492c10f40ac0c24831af6 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Sep 2024 14:22:56 +0200 Subject: [PATCH 173/241] extensions config --- .../rte/tiptap/property-editors/manifests.ts | 11 + ...tiptap-extensions-configuration.element.ts | 190 ++++++++++++++++++ .../property-editors/tiptap/manifests.ts | 7 + 3 files changed, 208 insertions(+) create mode 100644 src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts diff --git a/src/packages/rte/tiptap/property-editors/manifests.ts b/src/packages/rte/tiptap/property-editors/manifests.ts index 71f28f1a7c..2d8c3c7d66 100644 --- a/src/packages/rte/tiptap/property-editors/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/manifests.ts @@ -15,4 +15,15 @@ export const manifests: Array = [ group: 'common', }, }, + { + type: 'propertyEditorUi', + alias: 'Umb.PropertyEditorUi.Tiptap.ExtensionsConfiguration', + name: 'Tiptap Extensions Property Editor UI', + js: () => import('./property-editor-ui-tiptap-extensions-configuration.element.js'), + meta: { + label: 'Tiptap Extensions Configuration', + icon: 'icon-autofill', + group: 'common', + }, + }, ]; diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts new file mode 100644 index 0000000000..16d24d3a63 --- /dev/null +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts @@ -0,0 +1,190 @@ +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; +import { customElement, css, html, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { umbExtensionsRegistry, type UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; +import { + UmbPropertyValueChangeEvent, + type UmbPropertyEditorConfigCollection, +} from '@umbraco-cms/backoffice/property-editor'; + +type ExtensionConfig = { + alias: string; + label: string; + icon?: string; + category: string; +}; + +type ExtensionCategoryItem = { + alias: string; + label: string; + icon?: string; + selected: boolean; +}; + +type ExtensionCategory = { + category: string; + extensions: ExtensionCategoryItem[]; +}; + +@customElement('umb-property-editor-ui-tiptap-extensions-configuration') +export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement + extends UmbLitElement + implements UmbPropertyEditorUiElement +{ + @property({ attribute: false }) + set value(value: string[]) { + if (!value) value = []; + this.#value = value; + } + get value(): string[] { + return this.#value; + } + + #value: string[] = []; + + @property({ attribute: false }) + config?: UmbPropertyEditorConfigCollection; + + @state() + private _extensionCategories: ExtensionCategory[] = []; + + @state() + private _extensionConfigs: ExtensionConfig[] = []; + + protected override async firstUpdated(_changedProperties: PropertyValueMap) { + super.firstUpdated(_changedProperties); + + this.observe(umbExtensionsRegistry.byType('tiptapExtension'), (extensions) => { + this._extensionConfigs = extensions.map((ext) => { + return { + alias: ext.alias, + label: ext.meta.label, + icon: ext.meta.icon, + category: '', + }; + }); + this.#setupExtensionCategories(); + }); + } + + #setupExtensionCategories() { + const withSelectedProperty = this._extensionConfigs.map((extensionConfig) => { + return { + ...extensionConfig, + selected: this.value.includes(extensionConfig.alias), + }; + }); + + const grouped = withSelectedProperty.reduce((acc: any, item) => { + const group = item.category || 'Uncategorized'; // Assign to "Uncategorized" if no group + if (!acc[group]) { + acc[group] = []; + } + acc[group].push(item); + return acc; + }, {}); + this._extensionCategories = Object.keys(grouped).map((group) => ({ + category: group.charAt(0).toUpperCase() + group.slice(1).replace(/-/g, ' '), + extensions: grouped[group], + })); + } + + #onExtensionClick(item: ExtensionCategoryItem) { + item.selected = !item.selected; + + if (item.selected) { + this.#value = [...this.value, item.alias]; + } else { + this.#value = this.value.filter((alias) => alias !== item.alias); + } + + this.requestUpdate('_extensionCategories'); + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + } + + override render() { + return html` +
    + ${repeat( + this._extensionCategories, + (category) => html` +
    +

    ${category.category}

    + ${repeat( + category.extensions, + (item) => + html`
    + this.#onExtensionClick(item)} + > + ${item.label} +
    `, + )} +
    + `, + )} +
    +
    + `; + } + + static override readonly styles = [ + UmbTextStyles, + css` + uui-icon { + width: unset; + height: unset; + display: flex; + vertical-align: unset; + } + uui-button.selected { + --uui-button-border-color: var(--uui-color-selected); + --uui-button-border-width: 2px; + } + .extensions { + display: flex; + flex-wrap: wrap; + gap: 16px; + margin-top: 16px; + } + .extension-item { + display: grid; + grid-template-columns: 36px 1fr; + grid-template-rows: 1fr; + align-items: center; + gap: 9px; + } + .category { + flex: 1; + background-color: var(--uui-color-surface-alt); + padding: 12px; + border-radius: 6px; + display: flex; + flex-direction: column; + gap: 6px; + border: 1px solid var(--uui-color-border); + } + .category-name { + grid-column: 1 / -1; + margin: 0; + font-weight: bold; + display: flex; + } + `, + ]; +} + +export default UmbPropertyEditorUiTiptapExtensionsConfigurationElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-property-editor-ui-tiptap-extensions-configuration': UmbPropertyEditorUiTiptapExtensionsConfigurationElement; + } +} diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 5de70b8af0..3569959f82 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -13,6 +13,13 @@ export const manifests: Array = [ group: 'richContent', settings: { properties: [ + { + alias: 'extensions', + label: 'Extensions', + description: 'Extensions to enable', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.Tiptap.ExtensionsConfiguration', + weight: 5, + }, { alias: 'toolbar', label: 'Toolbar', From 5a51aef36d2dc1e246bd10c811437440c42fdf40 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:22:04 +0200 Subject: [PATCH 174/241] add toolbar config --- ...ui-tiptap-toolbar-configuration.element.ts | 361 +++++++++++------- 1 file changed, 222 insertions(+), 139 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index c70e4c0518..b94e768b59 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -1,39 +1,24 @@ -import type UmbTiptapToolbarGroupsConfigurationElement from './input-tiptap-toolbar-layout.element.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; -import { customElement, css, html, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; +import { + customElement, + css, + html, + property, + state, + repeat, + nothing, + type PropertyValueMap, +} from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { umbExtensionsRegistry, type UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; -import { - UmbPropertyValueChangeEvent, - type UmbPropertyEditorConfigCollection, -} from '@umbraco-cms/backoffice/property-editor'; import './input-tiptap-toolbar-layout.element.js'; +import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; -// If an extension does not have a position, it is considered hidden in the toolbar -type TestServerValue = Array<{ - alias: string; - position?: [number, number, number]; -}>; - -type ExtensionConfig = { +type Extension = { alias: string; label: string; - icon?: string; - category: string; -}; - -type ExtensionCategoryItem = { - alias: string; - label: string; - icon?: string; - selected: boolean; -}; - -type ExtensionCategory = { - category: string; - extensions: ExtensionCategoryItem[]; + icon: string; }; @customElement('umb-property-editor-ui-tiptap-toolbar-configuration') @@ -42,30 +27,25 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement implements UmbPropertyEditorUiElement { @property({ attribute: false }) - set value(value: TestServerValue) { - if (!value) value = []; - this.#value = value; + set value(value: string[][][]) { + // TODO: Make sure that value has at least one row and one group + this.#value = value.map((rows) => rows.map((groups) => [...groups])); } - get value(): TestServerValue { + + get value(): string[][][] { return this.#value; } - #value: TestServerValue = []; - - @property({ attribute: false }) - config?: UmbPropertyEditorConfigCollection; + #value: string[][][] = [[[]]]; @state() - private _extensionCategories: ExtensionCategory[] = []; - - @state() - private _extensionConfigs: ExtensionConfig[] = []; + _extensions: Extension[] = []; protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); this.observe(umbExtensionsRegistry.byType('tiptapExtension'), (extensions) => { - this._extensionConfigs = extensions.map((ext) => { + this._extensions = extensions.map((ext) => { return { alias: ext.alias, label: ext.meta.label, @@ -73,137 +53,240 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement category: '', }; }); - this.#setupExtensionCategories(); }); } - #setupExtensionCategories() { - const withSelectedProperty = this._extensionConfigs.map((v) => { - return { - ...v, - selected: this.value?.some((item) => item.alias === v.alias), - }; - }); + #onDragStart = (event: DragEvent, alias: string, fromPos?: [number, number, number]) => { + event.dataTransfer!.effectAllowed = 'move'; + event.dataTransfer!.setData( + 'application/json', + JSON.stringify({ + alias, + fromPos, + }), + ); + }; - const grouped = withSelectedProperty.reduce((acc: any, item) => { - const group = item.category || 'miscellaneous'; // Assign to "miscellaneous" if no group - if (!acc[group]) { - acc[group] = []; - } - acc[group].push(item); - return acc; - }, {}); - this._extensionCategories = Object.keys(grouped).map((group) => ({ - category: group.charAt(0).toUpperCase() + group.slice(1).replace(/-/g, ' '), - extensions: grouped[group], - })); - } + #onDragOver = (event: DragEvent) => { + event.preventDefault(); + event.dataTransfer!.dropEffect = 'move'; + }; - #onExtensionSelect(item: ExtensionCategoryItem) { - item.selected = !item.selected; + #onDragEnd = (event: DragEvent) => { + event.preventDefault(); + if (event.dataTransfer?.dropEffect === 'none') { + const { fromPos } = JSON.parse(event.dataTransfer!.getData('application/json')); + if (!fromPos) return; - if (item.selected) { - this.value = [ - ...this.value, - { - alias: item.alias, - }, - ]; - } else { - this.value = this.value.filter((v) => v.alias !== item.alias); + this.#removeItem(fromPos); + } + }; + + #onDrop = (event: DragEvent, toPos: [number, number, number]) => { + event.preventDefault(); + + const { alias, fromPos } = JSON.parse(event.dataTransfer!.getData('application/json')); + + if (fromPos) { + this.#moveItem(fromPos, toPos); + } else if (alias) { + this.#insertItem(alias, toPos); } + }; + + #moveItem = (from: [number, number, number], to: [number, number, number]) => { + const [rowIndex, groupIndex, itemIndex] = from; + + // Get the item to move from the 'from' position + const itemToMove = this.#value[rowIndex][groupIndex][itemIndex]; + + // Remove the item from the original position + this.#value[rowIndex][groupIndex].splice(itemIndex, 1); + + this.#insertItem(itemToMove, to); + }; + + #insertItem = (alias: string, toPos: [number, number, number]) => { + const [rowIndex, groupIndex, itemIndex] = toPos; + // Insert the item into the new position + this.#value[rowIndex][groupIndex].splice(itemIndex, 0, alias); + + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + }; + + #removeItem(from: [number, number, number]) { + const [rowIndex, groupIndex, itemIndex] = from; + this.#value[rowIndex][groupIndex].splice(itemIndex, 1); - this.requestUpdate('_extensionCategories'); this.dispatchEvent(new UmbPropertyValueChangeEvent()); } - #onChange(event: CustomEvent) { - this.value = (event.target as UmbTiptapToolbarGroupsConfigurationElement).value; + #addGroup = (rowIndex: number, groupIndex: number) => { + this.#value[rowIndex].splice(groupIndex, 0, []); + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + }; - // update the selected state of the extensions - // TODO this should be done in a more efficient way - this._extensionCategories.forEach((category) => { - category.extensions.forEach((item) => { - item.selected = this.value.some((v) => v.alias === item.alias); - }); - }); + #removeGroup = (rowIndex: number, groupIndex: number) => { + if (rowIndex === 0 && groupIndex === 0) { + // Prevent removing the last group + this.#value[rowIndex][groupIndex] = []; + } else { + this.#value[rowIndex].splice(groupIndex, 1); + } + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + }; + #addRow = (rowIndex: number) => { + this.#value.splice(rowIndex, 0, [[]]); this.dispatchEvent(new UmbPropertyValueChangeEvent()); + }; + + #removeRow = (rowIndex: number) => { + if (rowIndex === 0) { + // Prevent removing the last row + this.#value[rowIndex] = [[]]; + } else { + this.#value.splice(rowIndex, 1); + } + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + }; + + #renderItem(alias: string, rowIndex: number, groupIndex: number, itemIndex: number) { + const extension = this._extensions.find((ext) => ext.alias === alias); + if (!extension) return nothing; + return html`
    this.#onDragStart(e, alias, [rowIndex, groupIndex, itemIndex])}> + +
    `; } - override render() { + #renderGroup(group: string[], rowIndex: number, groupIndex: number) { + return html` +
    this.#onDrop(e, [rowIndex, groupIndex, group.length])}> + ${group.map((alias, itemIndex) => this.#renderItem(alias, rowIndex, groupIndex, itemIndex))} + this.#removeGroup(rowIndex, groupIndex)}> + + +
    + `; + } + + #renderRow(row: string[][], rowIndex: number) { return html` - -
    - ${repeat( - this._extensionCategories, - (category) => html` -
    -

    ${category.category}

    - ${repeat( - category.extensions, - (item) => - html`
    - this.#onExtensionSelect(item)} - > - ${item.label} -
    `, - )} -
    - `, - )} -
    +
    + ${repeat(row, (group, groupIndex) => this.#renderGroup(group, rowIndex, groupIndex))} + this.#addGroup(rowIndex, row.length)}>+ + this.#removeRow(rowIndex)}> + +
    `; } - static override readonly styles = [ + override render() { + return html` +

    + WIP Feature Rows, groups, and item order have no effect yet.
    + However, adding and removing items from the toolbar is functional. Additionally, hiding items from the toolbar + while retaining their functionality by excluding them from the toolbar layout is also functional. +

    + ${repeat(this.#value, (row, rowIndex) => this.#renderRow(row, rowIndex))} + this.#addRow(this.#value.length)}>+ + ${this.#renderExtensions()} + `; + } + + #renderExtensions() { + // TODO: Can we avoid using a flat here? or is it okay for performance? + return html`
    + ${repeat( + this._extensions.filter((ext) => !this.#value.flat(2).includes(ext.alias)), + (extension) => + html`
    this.#onDragStart(e, extension.alias)}> + +
    `, + )} +
    `; + } + + static override styles = [ UmbTextStyles, css` - uui-icon { - width: unset; - height: unset; + :host { display: flex; - vertical-align: unset; - } - uui-button.selected { - --uui-button-border-color: var(--uui-color-selected); - --uui-button-border-width: 2px; + flex-direction: column; + gap: 6px; } .extensions { display: flex; flex-wrap: wrap; - gap: 16px; - margin-top: 16px; + gap: 3px; + border-radius: var(--uui-border-radius); + background-color: var(--uui-color-surface-alt); + padding: 6px; + min-height: 30px; + min-width: 30px; } - .extension-item { - display: grid; - grid-template-columns: 36px 1fr; - grid-template-rows: 1fr; - align-items: center; - gap: 9px; + .row { + position: relative; + display: flex; + gap: 12px; } - .category { - flex: 1; - background-color: var(--uui-color-surface-alt); - padding: 12px; - border-radius: 6px; + .group { + position: relative; display: flex; - flex-direction: column; - gap: 6px; + gap: 3px; + border-radius: var(--uui-border-radius); + background-color: var(--uui-color-surface-alt); + padding: 6px; + min-height: 30px; + min-width: 30px; + } + .item { + padding: var(--uui-size-space-2); border: 1px solid var(--uui-color-border); + border-radius: var(--uui-border-radius); + background-color: var(--uui-color-surface); + cursor: move; + display: flex; + align-items: baseline; } - .category-name { - grid-column: 1 / -1; - margin: 0; - font-weight: bold; + + .remove-row-button, + .remove-group-button { + display: none; + } + .remove-group-button { + position: absolute; + top: -26px; + left: 50%; + transform: translateX(-50%); + z-index: 1; + } + + .row:hover .remove-row-button:not(.hidden), + .group:hover .remove-group-button:not(.hidden) { display: flex; } `, From 4e1766aa116aaecdb7f15098933f6b3c0f9fa64b Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:33:32 +0200 Subject: [PATCH 175/241] fix removing item --- ...ui-tiptap-toolbar-configuration.element.ts | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index b94e768b59..a2a55d22b8 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -41,6 +41,11 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement @state() _extensions: Extension[] = []; + #currentDragItem?: { + alias: string; + fromPos?: [number, number, number]; + }; + protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); @@ -58,13 +63,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement #onDragStart = (event: DragEvent, alias: string, fromPos?: [number, number, number]) => { event.dataTransfer!.effectAllowed = 'move'; - event.dataTransfer!.setData( - 'application/json', - JSON.stringify({ - alias, - fromPos, - }), - ); + this.#currentDragItem = { alias, fromPos }; }; #onDragOver = (event: DragEvent) => { @@ -75,21 +74,29 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement #onDragEnd = (event: DragEvent) => { event.preventDefault(); if (event.dataTransfer?.dropEffect === 'none') { - const { fromPos } = JSON.parse(event.dataTransfer!.getData('application/json')); + const { fromPos } = this.#currentDragItem ?? {}; if (!fromPos) return; this.#removeItem(fromPos); } }; - #onDrop = (event: DragEvent, toPos: [number, number, number]) => { + #onDrop = (event: DragEvent, toPos?: [number, number, number]) => { event.preventDefault(); + const { alias, fromPos } = this.#currentDragItem ?? {}; - const { alias, fromPos } = JSON.parse(event.dataTransfer!.getData('application/json')); - - if (fromPos) { + // Remove item if no destination position is provided + if (fromPos && !toPos) { + this.#removeItem(fromPos); + return; + } + // Move item if both source and destination positions are available + if (fromPos && toPos) { this.#moveItem(fromPos, toPos); - } else if (alias) { + return; + } + // Insert item if an alias and a destination position are provided + if (alias && toPos) { this.#insertItem(alias, toPos); } }; @@ -215,7 +222,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement #renderExtensions() { // TODO: Can we avoid using a flat here? or is it okay for performance? - return html`
    + return html`
    ${repeat( this._extensions.filter((ext) => !this.#value.flat(2).includes(ext.alias)), (extension) => From 8a8da05f79d39af8c368e6ae51ec525084ee9507 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:37:26 +0200 Subject: [PATCH 176/241] update WIP message --- ...erty-editor-ui-tiptap-toolbar-configuration.element.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index a2a55d22b8..61eb550192 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -210,9 +210,11 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement override render() { return html`

    - WIP Feature Rows, groups, and item order have no effect yet.
    - However, adding and removing items from the toolbar is functional. Additionally, hiding items from the toolbar - while retaining their functionality by excluding them from the toolbar layout is also functional. + WIP Feature + Rows, groups, and item order currently have no effect. +
    + However, items added to the toolbar will be saved and displayed in the editor according to their weight in the + manifest.

    ${repeat(this.#value, (row, rowIndex) => this.#renderRow(row, rowIndex))} this.#addRow(this.#value.length)}>+ From da776bb8411b65d8f8202931ec3dfc23b4b1f8d9 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:38:08 +0200 Subject: [PATCH 177/241] remove unused input --- .../input-tiptap-toolbar-layout.element.ts | 338 ------------------ ...ui-tiptap-toolbar-configuration.element.ts | 1 - 2 files changed, 339 deletions(-) delete mode 100644 src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts diff --git a/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts b/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts deleted file mode 100644 index b7a404fd83..0000000000 --- a/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts +++ /dev/null @@ -1,338 +0,0 @@ -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { customElement, css, html, property, repeat, nothing, state } from '@umbraco-cms/backoffice/external/lit'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; - -type Extension = { - alias: string; - label: string; - icon?: string; -}; - -type TestServerValue = Array<{ - alias: string; - position?: [number, number, number]; -}>; - -@customElement('umb-input-tiptap-toolbar-layout') -export class UmbTiptapToolbarGroupsConfigurationElement extends UmbLitElement { - @property({ attribute: false }) - set value(value: TestServerValue) { - if (this.#originalFormat === value) return; - // TODO: also check if the added values have positions, if not, there's no need to update the structured data. - this.#originalFormat = value; - this._structuredData = this.#toStructuredData(value); - } - - get value(): TestServerValue { - return this.#originalFormat; - } - - @property({ attribute: false }) - extensionConfigs: Extension[] = []; - - @state() - _structuredData: string[][][] = [[[]]]; - - #originalFormat: TestServerValue = []; - - #currentDragAlias?: string; - - #onDragStart = (event: DragEvent, alias: string) => { - this.#currentDragAlias = alias; - event.dataTransfer!.effectAllowed = 'move'; - }; - - #onDragOver = (event: DragEvent) => { - event.preventDefault(); - event.dataTransfer!.dropEffect = 'move'; - }; - - #onDragEnd = (event: DragEvent) => { - event.preventDefault(); - if (event.dataTransfer?.dropEffect === 'none') { - const fromPos = this.#originalFormat.find((item) => item.alias === this.#currentDragAlias)?.position; - if (!fromPos) return; - - this.#moveItemToHiddenExtensions(fromPos); - } - }; - - #onDrop = (event: DragEvent, toPos: [number, number, number]) => { - event.preventDefault(); - const fromPos = this.#originalFormat.find((item) => item.alias === this.#currentDragAlias)?.position; - - if (fromPos) { - this.#moveItem(fromPos, toPos); - } else if (this.#currentDragAlias) { - this.#insertItem(this.#currentDragAlias, toPos); - } - }; - - #moveItem = (from: [number, number, number], to: [number, number, number]) => { - const [rowIndex, groupIndex, itemIndex] = from; - - // Get the item to move from the 'from' position - const itemToMove = this._structuredData[rowIndex][groupIndex][itemIndex]; - - // Remove the item from the original position - this._structuredData[rowIndex][groupIndex].splice(itemIndex, 1); - - this.#insertItem(itemToMove, to); - }; - - #insertItem = (alias: string, toPos: [number, number, number]) => { - const [rowIndex, groupIndex, itemIndex] = toPos; - // Insert the item into the new position - this._structuredData[rowIndex][groupIndex].splice(itemIndex, 0, alias); - this.#updateOriginalFormat(); - - this.requestUpdate('_structuredData'); - this.dispatchEvent(new UmbChangeEvent()); - }; - - #moveItemToHiddenExtensions(from: [number, number, number]) { - const [rowIndex, groupIndex, itemIndex] = from; - this._structuredData[rowIndex][groupIndex].splice(itemIndex, 1); - - this.#updateOriginalFormat(); - - this.requestUpdate('_structuredData'); - this.dispatchEvent(new UmbChangeEvent()); - } - - #addGroup = (rowIndex: number, groupIndex: number) => { - this._structuredData[rowIndex].splice(groupIndex, 0, []); - this.requestUpdate('_structuredData'); - }; - - #removeGroup = (rowIndex: number, groupIndex: number) => { - if (rowIndex === 0 && groupIndex === 0) { - // Prevent removing the last group - this._structuredData[rowIndex][groupIndex] = []; - } else { - this._structuredData[rowIndex].splice(groupIndex, 1); - } - this.requestUpdate('_structuredData'); - this.#updateOriginalFormat(); - }; - - #addRow = (rowIndex: number) => { - this._structuredData.splice(rowIndex, 0, [[]]); - this.requestUpdate('_structuredData'); - }; - - #removeRow = (rowIndex: number) => { - if (rowIndex === 0) { - // Prevent removing the last row - this._structuredData[rowIndex] = [[]]; - } else { - this._structuredData.splice(rowIndex, 1); - } - this.requestUpdate('_structuredData'); - this.#updateOriginalFormat(); - }; - - #updateOriginalFormat() { - this.#originalFormat = this.#toOriginalFormat(this._structuredData); - this.dispatchEvent(new UmbChangeEvent()); - } - - #renderItem(alias: string) { - const extension = this.extensionConfigs.find((ext) => ext.alias === alias); - if (!extension) return nothing; - return html`
    this.#onDragStart(e, alias)}> - -
    `; - } - - #renderGroup(group: string[], rowIndex: number, groupIndex: number) { - return html` -
    this.#onDrop(e, [rowIndex, groupIndex, group.length])}> - ${group.map((alias) => this.#renderItem(alias))} - this.#removeGroup(rowIndex, groupIndex)}> - - -
    - `; - } - - #renderRow(row: string[][], rowIndex: number) { - return html` -
    - ${repeat(row, (group, groupIndex) => this.#renderGroup(group, rowIndex, groupIndex))} - this.#addGroup(rowIndex, row.length)}>+ - this.#removeRow(rowIndex)}> - - -
    - `; - } - - override render() { - return html` -

    - WIP Feature Rows, groups, and item order have no effect yet.
    - However, adding and removing items from the toolbar is functional. Additionally, hiding items from the toolbar - while retaining their functionality by excluding them from the toolbar layout is also functional. -

    - ${repeat(this._structuredData, (row, rowIndex) => this.#renderRow(row, rowIndex))} - this.#addRow(this._structuredData.length)}>+ - ${this.#renderHiddenExtensions()} - `; - } - - #renderHiddenExtensions() { - const hiddenExtensions = this.#originalFormat?.filter((item) => !item.position); - - if (!hiddenExtensions?.length) return nothing; - - return html` -

    - Extensions hidden from the toolbar
    Drag and drop buttons into the toolbar to add them -

    - -
    ${hiddenExtensions.map((item) => this.#renderItem(item.alias))}
    - `; - } - - #toStructuredData(data: TestServerValue) { - if (!data?.length) return [[[]]]; - - const structuredData: string[][][] = [[[]]]; - data.forEach(({ alias, position }) => { - if (!position) return; - - const [rowIndex, groupIndex, aliasIndex] = position; - - while (structuredData.length <= rowIndex) { - structuredData.push([]); - } - - const currentRow = structuredData[rowIndex]; - - while (currentRow.length <= groupIndex) { - currentRow.push([]); - } - - const currentGroup = currentRow[groupIndex]; - - currentGroup[aliasIndex] = alias; - }); - - return structuredData; - } - - #toOriginalFormat = (structuredData: string[][][]) => { - const originalData: TestServerValue = []; - - structuredData.forEach((row, rowIndex) => { - row.forEach((group, groupIndex) => { - group.forEach((alias, aliasIndex) => { - if (alias) { - originalData.push({ - alias, - position: [rowIndex, groupIndex, aliasIndex], - }); - } - }); - }); - }); - - // Add the hidden extensions so they are not lost - this.#originalFormat.forEach((item) => { - if (!originalData.some((i) => i.alias === item.alias)) { - originalData.push({ - alias: item.alias, - }); - } - }); - - return originalData; - }; - - static override styles = [ - UmbTextStyles, - css` - :host { - display: flex; - flex-direction: column; - gap: 6px; - } - .hidden-extensions { - display: flex; - gap: 6px; - } - .hidden-extensions-header { - margin-bottom: 3px; - } - .row { - position: relative; - display: flex; - gap: 12px; - } - .group { - position: relative; - display: flex; - gap: 3px; - border-radius: var(--uui-border-radius); - background-color: var(--uui-color-surface-alt); - padding: 6px; - min-height: 30px; - min-width: 30px; - } - .item { - padding: var(--uui-size-space-2); - border: 1px solid var(--uui-color-border); - border-radius: var(--uui-border-radius); - background-color: var(--uui-color-surface); - cursor: move; - display: flex; - align-items: baseline; - } - - .remove-row-button, - .remove-group-button { - display: none; - } - .remove-group-button { - position: absolute; - top: -26px; - left: 50%; - transform: translateX(-50%); - z-index: 1; - } - - .row:hover .remove-row-button:not(.hidden), - .group:hover .remove-group-button:not(.hidden) { - display: flex; - } - `, - ]; -} - -export default UmbTiptapToolbarGroupsConfigurationElement; - -declare global { - interface HTMLElementTagNameMap { - 'umb-input-tiptap-toolbar-layout': UmbTiptapToolbarGroupsConfigurationElement; - } -} diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 61eb550192..6c198df93d 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -12,7 +12,6 @@ import { import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { umbExtensionsRegistry, type UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; -import './input-tiptap-toolbar-layout.element.js'; import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; type Extension = { From 0c2d39adebfac532078c9d07661af5095cd2cddc Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:52:53 +0200 Subject: [PATCH 178/241] fix toolbar config --- ...rty-editor-ui-tiptap-toolbar-configuration.element.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 6c198df93d..966c818db6 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -48,7 +48,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); - this.observe(umbExtensionsRegistry.byType('tiptapExtension'), (extensions) => { + this.observe(umbExtensionsRegistry.byType('tiptapToolbarExtension'), (extensions) => { this._extensions = extensions.map((ext) => { return { alias: ext.alias, @@ -226,14 +226,15 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement return html`
    ${repeat( this._extensions.filter((ext) => !this.#value.flat(2).includes(ext.alias)), - (extension) => - html`
    html` +
    this.#onDragStart(e, extension.alias)}> -
    `, +
    + `, )}
    `; } From d0c06b29233afe138d9c59cbf4e29103aea20eef Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:53:04 +0200 Subject: [PATCH 179/241] add groups to extensions --- .../rte/tiptap/extensions/core/manifests.ts | 15 +++++++++++++++ .../rte/tiptap/extensions/tiptap-extension.ts | 1 + ...-ui-tiptap-extensions-configuration.element.ts | 6 +++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/packages/rte/tiptap/extensions/core/manifests.ts index 8131295233..ded8464142 100644 --- a/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/packages/rte/tiptap/extensions/core/manifests.ts @@ -11,6 +11,7 @@ export const manifests: Array = [ meta: { icon: 'icon-blockquote', label: 'Blockquote', + group: 'Content Structure', }, }, { @@ -23,6 +24,7 @@ export const manifests: Array = [ meta: { icon: 'icon-bold', label: 'Bold', + group: 'Text Formatting', }, }, { @@ -35,6 +37,7 @@ export const manifests: Array = [ meta: { icon: 'icon-code', label: 'Code Block', + group: 'Content Structure', }, }, { @@ -46,6 +49,7 @@ export const manifests: Array = [ meta: { icon: 'icon-embed', label: '#general_embed', + group: 'Media and Embeds', }, }, { @@ -57,6 +61,7 @@ export const manifests: Array = [ meta: { icon: 'icon-link', label: '#defaultdialogs_urlLinkPicker', + group: 'Interactive Elements', }, }, { @@ -68,6 +73,7 @@ export const manifests: Array = [ meta: { icon: 'icon-frame', label: 'Figure', + group: 'Media and Embeds', }, }, { @@ -80,6 +86,7 @@ export const manifests: Array = [ meta: { icon: 'icon-horizontal-rule', label: 'Horizontal Rule', + group: 'Content Structure', }, }, { @@ -90,6 +97,7 @@ export const manifests: Array = [ meta: { icon: 'icon-picture', label: 'Image', + group: 'Media and Embeds', }, }, { @@ -102,6 +110,7 @@ export const manifests: Array = [ meta: { icon: 'icon-italic', label: 'Italic', + group: 'Text Formatting', }, }, { @@ -114,6 +123,7 @@ export const manifests: Array = [ meta: { icon: 'icon-strikethrough', label: 'Strike', + group: 'Text Formatting', }, }, { @@ -126,6 +136,7 @@ export const manifests: Array = [ meta: { icon: 'icon-table', label: 'Table', + group: 'Interactive Elements', }, }, { @@ -138,6 +149,7 @@ export const manifests: Array = [ meta: { icon: 'icon-underline', label: 'Underline', + group: 'Text Formatting', }, }, { @@ -148,6 +160,7 @@ export const manifests: Array = [ meta: { icon: 'icon-heading-1', label: 'Heading', + group: 'Text Formatting', }, }, { @@ -158,6 +171,7 @@ export const manifests: Array = [ meta: { icon: 'icon-ordered-list', label: 'Ordered List', + group: 'Content Structure', }, }, { @@ -168,6 +182,7 @@ export const manifests: Array = [ meta: { icon: 'icon-text-align-justify', label: 'Text Align', + group: 'Content Structure', }, }, ]; diff --git a/src/packages/rte/tiptap/extensions/tiptap-extension.ts b/src/packages/rte/tiptap/extensions/tiptap-extension.ts index bb3dcd3f88..619792c6bb 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-extension.ts @@ -10,6 +10,7 @@ export interface ManifestTiptapExtension { - const group = item.category || 'Uncategorized'; // Assign to "Uncategorized" if no group + const group = item.group || 'Uncategorized'; // Assign to "Uncategorized" if no group if (!acc[group]) { acc[group] = []; } From 24e66c67bc525b567bf68c93b2863df088f86420 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:54:07 +0200 Subject: [PATCH 180/241] chore: fix import path --- .../components/toolbar/tiptap-toolbar-button.element.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts index c193554a81..35212303be 100644 --- a/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts +++ b/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts @@ -1,4 +1,4 @@ -import type { ManifestTiptapExtensionButtonKind } from '../../extensions/tiptap-extension.js'; +import type { ManifestTiptapToolbarExtensionButtonKind } from '../../extensions/index.js'; import type { UmbTiptapToolbarElementApi } from '../../extensions/types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import { customElement, html, ifDefined, state, when } from '@umbraco-cms/backoffice/external/lit'; @@ -10,7 +10,7 @@ const elementName = 'umb-tiptap-toolbar-button'; export class UmbTiptapToolbarButtonElement extends UmbLitElement { public api?: UmbTiptapToolbarElementApi; public editor?: Editor; - public manifest?: ManifestTiptapExtensionButtonKind; + public manifest?: ManifestTiptapToolbarExtensionButtonKind; @state() protected isActive = false; From b7944b17c99bc428575daeee7d0ee89111839cea Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:54:47 +0200 Subject: [PATCH 181/241] description --- src/packages/rte/tiptap/property-editors/tiptap/manifests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 3569959f82..1adc57a016 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -23,7 +23,7 @@ export const manifests: Array = [ { alias: 'toolbar', label: 'Toolbar', - description: 'Pick the toolbar options that should be available when editing', + description: 'Pick the toolbar items that should be available when editing', propertyEditorUiAlias: 'Umb.PropertyEditorUi.Tiptap.ToolbarConfiguration', weight: 10, }, From 010f47a4e34e49ad55fc3ca845861b95d8618458 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Sep 2024 16:16:39 +0200 Subject: [PATCH 182/241] styling --- ...erty-editor-ui-tiptap-toolbar-configuration.element.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 966c818db6..5b2067ef1a 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -32,7 +32,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement } get value(): string[][][] { - return this.#value; + return this.#value.map((rows) => rows.map((groups) => [...groups])); } #value: string[][][] = [[[]]]; @@ -133,7 +133,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement }; #removeGroup = (rowIndex: number, groupIndex: number) => { - if (rowIndex === 0 && groupIndex === 0) { + if (groupIndex === 0) { // Prevent removing the last group this.#value[rowIndex][groupIndex] = []; } else { @@ -181,7 +181,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement look="primary" color="danger" compact - class="remove-group-button ${rowIndex === 0 && groupIndex === 0 && group.length === 0 ? 'hidden' : undefined}" + class="remove-group-button ${groupIndex === 0 && group.length === 0 ? 'hidden' : undefined}" @click=${() => this.#removeGroup(rowIndex, groupIndex)}> @@ -198,7 +198,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement look="primary" color="danger" compact - class="remove-row-button ${rowIndex === 0 && row[rowIndex].length === 0 ? 'hidden' : undefined}" + class="remove-row-button ${rowIndex === 0 && row.length === 1 && row[0].length === 0 ? 'hidden' : undefined}" @click=${() => this.#removeRow(rowIndex)}> From 82109d5731610ba05b963be5b9d7dd26f5f47f5f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 16:19:27 +0200 Subject: [PATCH 183/241] chore(sonarcloud): fix issues --- .../tiptap/extensions/umb/link.extension.ts | 84 +++++++++++++++---- 1 file changed, 67 insertions(+), 17 deletions(-) diff --git a/src/packages/rte/tiptap/extensions/umb/link.extension.ts b/src/packages/rte/tiptap/extensions/umb/link.extension.ts index d15602f0c7..13b907f40e 100644 --- a/src/packages/rte/tiptap/extensions/umb/link.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/link.extension.ts @@ -49,39 +49,89 @@ export default class UmbTiptapLinkExtensionApi extends UmbTiptapToolbarElementAp let { queryString, url } = link; // If an anchor exists, check that it is appropriately prefixed - if (!queryString?.startsWith('?') && !queryString?.startsWith('#')) { - queryString = (queryString?.startsWith('=') ? '#' : '?') + queryString; - } + queryString = this.#queryStringFromUrl(queryString); // The href might be an external url, so check the value for an anchor/querystring; // `href` has the anchor re-appended later, hence the reset here to avoid duplicating the anchor if (!queryString) { - const urlParts = url?.split(/([#?])/); - if (urlParts?.length === 3) { - url = urlParts[0]; - queryString = urlParts[1] + urlParts[2]; - } + const extractedInfo = this.#extractUrlAndQueryString(url, queryString); + url = extractedInfo.url; + queryString = extractedInfo.queryString; } // If we have a unique id, it must be a `/{localLink:guid}` if (unique) { url = `/{localLink:${unique}}`; + } else { + // If it's an email address and not `//user@domain.com` and protocol (e.g. mailto:, sip:) is not specified; + // then we'll assume it should be a "mailto" link. + url = this.#transformURLToMailto(url); + + url = this.#ensureHttpProtocol(url); } - // If it's an email address and not `//user@domain.com` and protocol (e.g. mailto:, sip:) is not specified; - // then we'll assume it should be a "mailto" link. - if (url?.includes('@') && !url.includes('//') && !url.includes(':')) { - url = `mailto:${url}`; + const anchor = this.#getAnchorFromQueryString(queryString); + + if (anchor) url += anchor; + + if (!url) return null; + + return { + type: type ?? 'external', + href: url, + 'data-anchor': anchor, + target, + title: name ?? url, + }; + } + + #extractUrlAndQueryString(url: string | null | undefined, queryString: string | null) { + const urlParts = url?.split(/([#?])/); + if (urlParts?.length === 3) { + url = urlParts[0]; + queryString = urlParts[1] + urlParts[2]; } + return { url, queryString }; + } - // If the URL is prefixed "www.", then prepend "http://" protocol scheme. - if (url && /^\s*www\./i.test(url)) { + /** + * If the URL is prefixed "www.", then prepend "http://" protocol scheme. + */ + #ensureHttpProtocol(url: string | null | undefined) { + if (!url) return null; + if (/^\s*www\./i.test(url)) { url = `http://${url}`; } + return url; + } + + /** + * If the URL is an email address, then prepend "mailto:" protocol scheme. + */ + #transformURLToMailto(url: string | null | undefined) { + if (!url) return null; + if (url?.includes('@') && !url.includes('//') && !url.includes(':')) { + url = `mailto:${url}`; + } + return url; + } - const anchor = queryString?.startsWith('#') || queryString?.startsWith('?') ? queryString : null; - const href = url + (anchor ?? ''); + /** + * If the URL contains an anchor, then return the anchor. + */ + #getAnchorFromQueryString(queryString: string | null) { + if (!queryString) return null; + return queryString.startsWith('#') || queryString.startsWith('?') ? queryString : null; + } - return href ? { type: type ?? 'external', href, 'data-anchor': anchor, target, title: name ?? url } : null; + /** + * If the query string does not start with "?" or "#", then prepend it. + */ + #queryStringFromUrl(queryString: string | null | undefined) { + if (!queryString) return null; + if (!queryString.startsWith('?') && !queryString.startsWith('#')) { + queryString = (queryString.startsWith('=') ? '#' : '?') + queryString; + } + return queryString; } } From 7958b0cce82d2b6ee576fbfcceb7943eac3064a9 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Sep 2024 16:31:57 +0200 Subject: [PATCH 184/241] cleanup and filter extensions --- .../input-tiptap/input-tiptap.element.ts | 23 +++++-------------- .../input-tiptap/tiptap-fixed-menu.element.ts | 6 ----- ...ui-tiptap-toolbar-configuration.element.ts | 2 ++ 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 19f8e726fe..29bd882061 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -56,30 +56,22 @@ export class UmbInputTiptapElement extends UmbFormControlMixin = []; - protected override async firstUpdated() { - // TODO: we need some types here - this._toolbarConfig = (this.configuration?.getValueByAlias('toolbar') as any) ?? []; await Promise.all([await this.#loadExtensions(), await this.#loadEditor()]); } async #loadExtensions() { await new Promise((resolve) => { this.observe(umbExtensionsRegistry.byType('tiptapExtension'), async (manifests) => { - manifests = manifests.filter((ext) => { - return !!this._toolbarConfig.find((x) => x.alias === ext.alias); - }); - + const enabledExtensions = this.configuration?.getValueByAlias('extensions') ?? []; for (const manifest of manifests) { if (manifest.api) { const extension = await loadManifestApi(manifest.api); if (extension) { - this._extensions.push(new extension(this)); + // Check if the extension is enabled + if (enabledExtensions.includes(manifest.alias)) { + this._extensions.push(new extension(this)); + } } } } @@ -125,10 +117,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin html``, () => html` - + `, )}
    diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 42874b0dba..9490ad278f 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -12,12 +12,6 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { @property({ type: Boolean, reflect: true }) readonly = false; - @property({ attribute: false }) - toolbarConfig: Array<{ - alias: string; - position?: [number, number, number]; - }> = []; - @property({ attribute: false }) set editor(value) { const oldValue = this.#editor; diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 5b2067ef1a..e56361410f 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -28,10 +28,12 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement @property({ attribute: false }) set value(value: string[][][]) { // TODO: Make sure that value has at least one row and one group + // TODO: This can be optimized with cashing; this.#value = value.map((rows) => rows.map((groups) => [...groups])); } get value(): string[][][] { + // TODO: This can be optimized with cashing; return this.#value.map((rows) => rows.map((groups) => [...groups])); } From a953c126da0fdafc5cd7d495b833b222819a0705 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 16:48:21 +0200 Subject: [PATCH 185/241] chore: remove console.log --- .../tiptap/components/input-tiptap/input-tiptap.element.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 21b5efde7d..777be27462 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -96,10 +96,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin { - this._extensions.forEach((ext) => { - console.log('🚀 ~ this._extensions.forEach ~ ext:', ext); - ext.setEditor(editor); - }); + this._extensions.forEach((ext) => ext.setEditor(editor)); }, onUpdate: ({ editor }) => { this.#markup = editor.getHTML(); From 639ecbdd060218dae4ce5ed5c335da842d61501e Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Sep 2024 16:52:11 +0200 Subject: [PATCH 186/241] swap order and default to all extensions selected --- ...tiptap-extensions-configuration.element.ts | 21 ++++++++++++++----- ...ui-tiptap-toolbar-configuration.element.ts | 8 +++++-- .../property-editors/tiptap/manifests.ts | 14 ++++++------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts index b513b99415..40649ae3d3 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts @@ -33,15 +33,14 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement implements UmbPropertyEditorUiElement { @property({ attribute: false }) - set value(value: string[]) { - if (!value) value = []; + set value(value: string[] | undefined) { this.#value = value; } - get value(): string[] { + get value(): string[] | undefined { return this.#value; } - #value: string[] = []; + #value?: string[] = []; @property({ attribute: false }) config?: UmbPropertyEditorConfigCollection; @@ -64,15 +63,23 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement group: ext.meta.group, }; }); + + if (!this.value) { + // The default value is all extensions enabled + this.#value = this._extensionConfigs.map((ext) => ext.alias); + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + } + this.#setupExtensionCategories(); }); } #setupExtensionCategories() { + const useDefault = !this.value; // The default value is all extensions enabled const withSelectedProperty = this._extensionConfigs.map((extensionConfig) => { return { ...extensionConfig, - selected: this.value.includes(extensionConfig.alias), + selected: useDefault ? true : this.value!.includes(extensionConfig.alias), }; }); @@ -93,6 +100,10 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement #onExtensionClick(item: ExtensionCategoryItem) { item.selected = !item.selected; + if (!this.value) { + this.value = []; + } + if (item.selected) { this.#value = [...this.value, item.alias]; } else { diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index e56361410f..fdc4b089a8 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -26,8 +26,12 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement implements UmbPropertyEditorUiElement { @property({ attribute: false }) - set value(value: string[][][]) { - // TODO: Make sure that value has at least one row and one group + set value(value: string[][][] | undefined) { + if (!value) { + this.#value = [[[]]]; + return; + } + // TODO: This can be optimized with cashing; this.#value = value.map((rows) => rows.map((groups) => [...groups])); } diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 1adc57a016..906c3b67b7 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -13,18 +13,18 @@ export const manifests: Array = [ group: 'richContent', settings: { properties: [ - { - alias: 'extensions', - label: 'Extensions', - description: 'Extensions to enable', - propertyEditorUiAlias: 'Umb.PropertyEditorUi.Tiptap.ExtensionsConfiguration', - weight: 5, - }, { alias: 'toolbar', label: 'Toolbar', description: 'Pick the toolbar items that should be available when editing', propertyEditorUiAlias: 'Umb.PropertyEditorUi.Tiptap.ToolbarConfiguration', + weight: 5, + }, + { + alias: 'extensions', + label: 'Extensions', + description: 'Extensions to enable', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.Tiptap.ExtensionsConfiguration', weight: 10, }, { From 64b80c2489b2c42a4b0a3517c5051f0793b4a193 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Sep 2024 16:57:25 +0200 Subject: [PATCH 187/241] filter toolbar --- .../components/input-tiptap/input-tiptap.element.ts | 10 +++++++++- .../input-tiptap/tiptap-fixed-menu.element.ts | 6 +++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 29bd882061..39736b1305 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -56,6 +56,9 @@ export class UmbInputTiptapElement extends UmbFormControlMixin('toolbar') ?? [[[]]]; + const extensions = this._extensions .map((ext) => ext.getTiptapExtensions({ configuration: this.configuration })) .flat(); @@ -117,7 +122,10 @@ export class UmbInputTiptapElement extends UmbFormControlMixin html``, () => html` - + `, )}
    diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 9490ad278f..26c0aa2030 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -25,11 +25,15 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { } #editor?: Editor; + @property({ attribute: false }) + toolbar: string[][][] = [[[]]]; + override render() { return html` !!ext.kind || !!ext.element} + .filter=${(ext: ManifestTiptapToolbarExtension) => + this.toolbar.flat(2).includes(ext.alias) && (!!ext.kind || !!ext.element)} .elementProps=${{ editor: this.editor }}> `; From 7212f1e1f956a978a07fbec9a48ef9708a0014e1 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:02:36 +0200 Subject: [PATCH 188/241] styling --- ...rty-editor-ui-tiptap-toolbar-configuration.element.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index fdc4b089a8..e378f3b954 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -275,8 +275,8 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement border-radius: var(--uui-border-radius); background-color: var(--uui-color-surface-alt); padding: 6px; - min-height: 30px; - min-width: 30px; + min-height: 32px; + min-width: 32px; } .item { padding: var(--uui-size-space-2); @@ -285,7 +285,10 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement background-color: var(--uui-color-surface); cursor: move; display: flex; - align-items: baseline; + box-sizing: border-box; + width: 32px; + height: 32px; + justify-content: center; } .remove-row-button, From 827b748c77c3a63f6ca58b7c94c2434e00e4a623 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:07:04 +0200 Subject: [PATCH 189/241] feat: apply crop parameters to images --- .../rte/tiptap/extensions/umb/mediapicker.extension.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts b/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts index 6b81230c59..abc90d5d2a 100644 --- a/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts @@ -7,8 +7,9 @@ import { import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { getGuidFromUdi, imageSize } from '@umbraco-cms/backoffice/utils'; +import { getGuidFromUdi, getProcessedImageUrl, imageSize } from '@umbraco-cms/backoffice/utils'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; +import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api'; export default class UmbTiptapMediaPickerExtensionApi extends UmbTiptapToolbarElementApiBase { #modalManager?: typeof UMB_MODAL_MANAGER_CONTEXT.TYPE; @@ -103,10 +104,11 @@ export default class UmbTiptapMediaPickerExtensionApi extends UmbTiptapToolbarEl if (!media?.url) return; const { width, height } = await imageSize(media.url, { maxWidth: this.maxWidth }); + const src = await getProcessedImageUrl(media.url, { width, height, mode: ImageCropModeModel.MAX }); const img = { alt: media.altText, - src: media.url ? media.url : 'nothing.jpg', + src, 'data-udi': `umb://media/${mediaUnique.replace(/-/g, '')}`, width: width.toString(), height: height.toString(), From 8fab7013e9305145046dac2a649a61525e9be430 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:30:24 +0200 Subject: [PATCH 190/241] add defaults and title --- .../extensions/tiptap-toolbar-extension.ts | 1 + .../rte/tiptap/extensions/toolbar/manifests.ts | 14 ++++++++++++++ ...or-ui-tiptap-toolbar-configuration.element.ts | 16 ++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/src/packages/rte/tiptap/extensions/tiptap-toolbar-extension.ts b/src/packages/rte/tiptap/extensions/tiptap-toolbar-extension.ts index a97d658328..fde0edb310 100644 --- a/src/packages/rte/tiptap/extensions/tiptap-toolbar-extension.ts +++ b/src/packages/rte/tiptap/extensions/tiptap-toolbar-extension.ts @@ -13,6 +13,7 @@ export interface MetaTiptapToolbarExtension { alias: string; icon: string; label: string; + isDefault?: boolean; } export interface ManifestTiptapToolbarExtensionButtonKind< diff --git a/src/packages/rte/tiptap/extensions/toolbar/manifests.ts b/src/packages/rte/tiptap/extensions/toolbar/manifests.ts index cc39270567..0780c05820 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/manifests.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/manifests.ts @@ -15,6 +15,7 @@ export const manifests: Array rows.map((groups) => [...groups])); } + #useDefault = false; + #value: string[][][] = [[[]]]; @state() @@ -56,6 +59,9 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement this.observe(umbExtensionsRegistry.byType('tiptapToolbarExtension'), (extensions) => { this._extensions = extensions.map((ext) => { + if (this.#useDefault && ext.meta.isDefault) { + this.#value[0][0].push(ext.alias); + } return { alias: ext.alias, label: ext.meta.label, @@ -63,6 +69,10 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement category: '', }; }); + + if (this.#useDefault) { + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + } }); } @@ -167,6 +177,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement const extension = this._extensions.find((ext) => ext.alias === alias); if (!extension) return nothing; return html`
    !this.#value.flat(2).includes(ext.alias)), (extension) => html`
    Date: Fri, 27 Sep 2024 17:57:29 +0200 Subject: [PATCH 191/241] add link and media picker as default --- src/packages/rte/tiptap/extensions/manifests.ts | 2 ++ src/packages/rte/tiptap/extensions/toolbar/manifests.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index 39f1379c12..e147fc85c6 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -44,6 +44,7 @@ const umbToolbarExtensions: Array Date: Fri, 27 Sep 2024 17:57:34 +0200 Subject: [PATCH 192/241] add missing groups --- src/packages/block/block-rte/tiptap-extension/manifests.ts | 1 + src/packages/rte/tiptap/extensions/manifests.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/packages/block/block-rte/tiptap-extension/manifests.ts b/src/packages/block/block-rte/tiptap-extension/manifests.ts index b62c0f0e5c..9500a28c9b 100644 --- a/src/packages/block/block-rte/tiptap-extension/manifests.ts +++ b/src/packages/block/block-rte/tiptap-extension/manifests.ts @@ -9,6 +9,7 @@ export const manifests: Array = [ meta: { icon: 'icon-image-up', label: 'Media upload', + group: 'Media and Embeds', }, }, ]; From 81feb3ea220b2223c3f6e455000afd9b5c5db97c Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 22:11:56 +0200 Subject: [PATCH 193/241] feat: adds the placeholder plugin with a message to get users started --- package-lock.json | 14 +++++++++ package.json | 1 + src/assets/lang/da-dk.ts | 2 ++ src/assets/lang/en.ts | 2 ++ src/external/tiptap/index.ts | 1 + .../input-tiptap/input-tiptap.element.ts | 29 ++++++++++++++++++- 6 files changed, 48 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 2d9d650673..037c333a6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "@tiptap/extension-list-item": "^2.7.4", "@tiptap/extension-ordered-list": "^2.7.4", "@tiptap/extension-paragraph": "^2.7.4", + "@tiptap/extension-placeholder": "^2.7.4", "@tiptap/extension-strike": "^2.7.4", "@tiptap/extension-table": "^2.7.4", "@tiptap/extension-table-cell": "^2.7.4", @@ -6723,6 +6724,19 @@ "@tiptap/core": "^2.7.0" } }, + "node_modules/@tiptap/extension-placeholder": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.7.4.tgz", + "integrity": "sha512-7MOA4z8M7tUu8G9eiMvnitLcrhZJb4Hak3VCWgU2Cl9SXPizgKuF5VHd5ESOaEhNRk5pktFDDvCX9PHD7ZayGg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0", + "@tiptap/pm": "^2.7.0" + } + }, "node_modules/@tiptap/extension-strike": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.7.4.tgz", diff --git a/package.json b/package.json index 65331dcba2..d3b621c879 100644 --- a/package.json +++ b/package.json @@ -211,6 +211,7 @@ "@tiptap/extension-list-item": "^2.7.4", "@tiptap/extension-ordered-list": "^2.7.4", "@tiptap/extension-paragraph": "^2.7.4", + "@tiptap/extension-placeholder": "^2.7.4", "@tiptap/extension-strike": "^2.7.4", "@tiptap/extension-table": "^2.7.4", "@tiptap/extension-table-cell": "^2.7.4", diff --git a/src/assets/lang/da-dk.ts b/src/assets/lang/da-dk.ts index f35a6596ed..3a8de51a46 100644 --- a/src/assets/lang/da-dk.ts +++ b/src/assets/lang/da-dk.ts @@ -643,6 +643,8 @@ export default { a11yCreateItem: 'Opret element', a11yEdit: 'Rediger', a11yName: 'Navn', + rteParagraph: 'Udfold din kreativitet...', + rteHeading: 'Hvad skal overskriften være?', }, editcontenttype: { createListView: 'Opret brugerdefineret listevisning', diff --git a/src/assets/lang/en.ts b/src/assets/lang/en.ts index 495129c5cc..e588340496 100644 --- a/src/assets/lang/en.ts +++ b/src/assets/lang/en.ts @@ -664,6 +664,8 @@ export default { a11yCreateItem: 'Create item', a11yEdit: 'Edit', a11yName: 'Name', + rteParagraph: 'Write something amazing...', + rteHeading: "What's the title?", }, editcontenttype: { createListView: 'Create custom list view', diff --git a/src/external/tiptap/index.ts b/src/external/tiptap/index.ts index e17d84cb3a..587e432476 100644 --- a/src/external/tiptap/index.ts +++ b/src/external/tiptap/index.ts @@ -6,6 +6,7 @@ export { Gapcursor } from '@tiptap/extension-gapcursor'; export { HardBreak } from '@tiptap/extension-hard-break'; export { History } from '@tiptap/extension-history'; export { Paragraph } from '@tiptap/extension-paragraph'; +export { Placeholder } from '@tiptap/extension-placeholder'; export { Text } from '@tiptap/extension-text'; // OPTIONAL EXTENSIONS diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index c203377cf2..b2837b223c 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -10,6 +10,7 @@ import { HardBreak, History, Paragraph, + Placeholder, Text, } from '@umbraco-cms/backoffice/external/tiptap'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; @@ -24,7 +25,24 @@ const elementName = 'umb-input-tiptap'; @customElement(elementName) export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement) { - readonly #requiredExtensions = [Document, Dropcursor, Gapcursor, HardBreak, History, Paragraph, Text]; + readonly #requiredExtensions = [ + Document, + Dropcursor, + Gapcursor, + HardBreak, + History, + Paragraph, + Placeholder.configure({ + placeholder: ({ node }) => { + if (node.type.name === 'heading') { + return this.localize.term('placeholders_rteHeading'); + } + + return this.localize.term('placeholders_rteParagraph'); + }, + }), + Text, + ]; @state() private _extensions: Array = []; @@ -150,6 +168,15 @@ export class UmbInputTiptapElement extends UmbFormControlMixin Date: Fri, 27 Sep 2024 22:43:15 +0200 Subject: [PATCH 194/241] feat: adds 'redo' and 'undo' buttons to tiptap --- .../tiptap/extensions/toolbar/manifests.ts | 28 +++++++++++++++++++ .../extensions/toolbar/redo.extension.ts | 12 ++++++++ .../extensions/toolbar/undo.extension.ts | 12 ++++++++ 3 files changed, 52 insertions(+) create mode 100644 src/packages/rte/tiptap/extensions/toolbar/redo.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/toolbar/undo.extension.ts diff --git a/src/packages/rte/tiptap/extensions/toolbar/manifests.ts b/src/packages/rte/tiptap/extensions/toolbar/manifests.ts index 898b4003b7..6ac9973d8d 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/manifests.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/manifests.ts @@ -74,6 +74,20 @@ export const manifests: Array import('./redo.extension.js'), + element: () => import('../../components/toolbar/tiptap-toolbar-button-disabled.element.js'), + weight: 994, + meta: { + alias: 'redo', + icon: 'icon-redo', + label: 'Redo', + }, + }, { type: 'tiptapToolbarExtension', kind: 'button', @@ -239,6 +253,20 @@ export const manifests: Array import('./undo.extension.js'), + element: () => import('../../components/toolbar/tiptap-toolbar-button-disabled.element.js'), + weight: 994, + meta: { + alias: 'undo', + icon: 'icon-undo', + label: 'Undo', + }, + }, { type: 'tiptapToolbarExtension', kind: 'button', diff --git a/src/packages/rte/tiptap/extensions/toolbar/redo.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/redo.extension.ts new file mode 100644 index 0000000000..879fc90cec --- /dev/null +++ b/src/packages/rte/tiptap/extensions/toolbar/redo.extension.ts @@ -0,0 +1,12 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapRedoExtensionApi extends UmbTiptapToolbarElementApiBase { + override isActive(editor: Editor): boolean { + return editor.can().redo(); + } + + override execute(editor?: Editor) { + editor?.chain().focus().redo().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/toolbar/undo.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/undo.extension.ts new file mode 100644 index 0000000000..a206efaa4e --- /dev/null +++ b/src/packages/rte/tiptap/extensions/toolbar/undo.extension.ts @@ -0,0 +1,12 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapUndoExtensionApi extends UmbTiptapToolbarElementApiBase { + override isActive(editor: Editor): boolean { + return editor.can().undo(); + } + + override execute(editor?: Editor) { + editor?.chain().focus().undo().run(); + } +} From d01b23f1a176fe44e5797301c989bbc3fcb93e64 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Sep 2024 23:07:32 +0200 Subject: [PATCH 195/241] feat: adds configuration to toolbar buttons --- .../input-tiptap/input-tiptap.element.ts | 1 + .../input-tiptap/tiptap-fixed-menu.element.ts | 19 +++++++------------ src/packages/rte/tiptap/extensions/types.ts | 7 ++++++- .../extensions/umb/mediapicker.extension.ts | 13 +++---------- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index b2837b223c..0e9539f206 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -140,6 +140,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin `, )} diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 26c0aa2030..2ed9b7774e 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -2,6 +2,7 @@ import type { ManifestTiptapToolbarExtension } from '../../extensions/tiptap-too import { css, customElement, html, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; +import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import '../toolbar/tiptap-toolbar-dropdown-base.element.js'; @@ -13,17 +14,10 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { readonly = false; @property({ attribute: false }) - set editor(value) { - const oldValue = this.#editor; - if (value === oldValue) { - return; - } - this.#editor = value; - } - get editor() { - return this.#editor; - } - #editor?: Editor; + editor?: Editor; + + @property({ attribute: false }) + configuration?: UmbPropertyEditorConfigCollection; @property({ attribute: false }) toolbar: string[][][] = [[[]]]; @@ -34,7 +28,8 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { type="tiptapToolbarExtension" .filter=${(ext: ManifestTiptapToolbarExtension) => this.toolbar.flat(2).includes(ext.alias) && (!!ext.kind || !!ext.element)} - .elementProps=${{ editor: this.editor }}> + .elementProps=${{ editor: this.editor, configuration: this.configuration }} + .apiProps=${{ configuration: this.configuration }}> `; } diff --git a/src/packages/rte/tiptap/extensions/types.ts b/src/packages/rte/tiptap/extensions/types.ts index d297efd984..ec2bcc1f8c 100644 --- a/src/packages/rte/tiptap/extensions/types.ts +++ b/src/packages/rte/tiptap/extensions/types.ts @@ -55,7 +55,7 @@ export interface UmbTiptapExtensionArgs { configuration?: UmbPropertyEditorConfigCollection; } -export interface UmbTiptapToolbarElementApi extends UmbApi { +export interface UmbTiptapToolbarElementApi extends UmbApi, UmbTiptapExtensionArgs { /** * The manifest for the extension. */ @@ -78,6 +78,11 @@ export abstract class UmbTiptapToolbarElementApiBase extends UmbControllerBase i */ manifest?: ManifestTiptapToolbarExtension; + /** + * The data type configuration for the property editor that the editor is used for. + */ + configuration?: UmbPropertyEditorConfigCollection; + /** * A method to execute the toolbar element action. */ diff --git a/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts b/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts index abc90d5d2a..cbca94f7ff 100644 --- a/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts @@ -1,4 +1,4 @@ -import { UmbTiptapToolbarElementApiBase, type UmbTiptapExtensionArgs } from '../types.js'; +import { UmbTiptapToolbarElementApiBase } from '../types.js'; import { UMB_MEDIA_CAPTION_ALT_TEXT_MODAL, UMB_MEDIA_PICKER_MODAL, @@ -8,18 +8,16 @@ import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { getGuidFromUdi, getProcessedImageUrl, imageSize } from '@umbraco-cms/backoffice/utils'; -import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api'; -export default class UmbTiptapMediaPickerExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapMediaPickerToolbarExtensionApi extends UmbTiptapToolbarElementApiBase { #modalManager?: typeof UMB_MODAL_MANAGER_CONTEXT.TYPE; - #configuration?: UmbPropertyEditorConfigCollection; /** * @returns {number} The maximum width of uploaded images */ get maxWidth(): number { - const maxImageSize = parseInt(this.#configuration?.getValueByAlias('maxImageSize') ?? '', 10); + const maxImageSize = parseInt(this.configuration?.getValueByAlias('maxImageSize') ?? '', 10); return isNaN(maxImageSize) ? 500 : maxImageSize; } @@ -31,11 +29,6 @@ export default class UmbTiptapMediaPickerExtensionApi extends UmbTiptapToolbarEl }); } - getTiptapExtensions(args: UmbTiptapExtensionArgs) { - this.#configuration = args?.configuration; - return []; - } - override isActive(editor?: Editor) { return editor?.isActive('image') === true || editor?.isActive('figure') === true; } From 094ac485a5e32791b56cbb49759ee875d4356312 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 08:35:59 +0200 Subject: [PATCH 196/241] feat: use focus color when selecting images --- .../components/block-rte-entry/block-rte-entry.element.ts | 2 +- .../tiptap/components/input-tiptap/input-tiptap.element.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts b/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts index 435711cb3e..dfe3621241 100644 --- a/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts +++ b/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts @@ -205,7 +205,7 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert :host(.ProseMirror-selectednode) { umb-ref-rte-block { cursor: not-allowed; - outline: 3px solid #b4d7ff; + outline: 3px solid var(--uui-color-focus); } } uui-action-bar { diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 0e9539f206..7400057c30 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -229,13 +229,13 @@ export class UmbInputTiptapElement extends UmbFormControlMixin Date: Mon, 30 Sep 2024 08:58:34 +0200 Subject: [PATCH 197/241] feat: move Umbraco.RichText to the root of the rte package --- .../property-editors/tiny-mce => }/Umbraco.RichText.ts | 2 +- src/packages/rte/manifests.ts | 7 ++++++- .../rte/tiny-mce/property-editors/tiny-mce/manifests.ts | 3 +-- 3 files changed, 8 insertions(+), 4 deletions(-) rename src/packages/rte/{tiny-mce/property-editors/tiny-mce => }/Umbraco.RichText.ts (93%) diff --git a/src/packages/rte/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts b/src/packages/rte/Umbraco.RichText.ts similarity index 93% rename from src/packages/rte/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts rename to src/packages/rte/Umbraco.RichText.ts index 3dbe86e0a0..36fb6b3c94 100644 --- a/src/packages/rte/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts +++ b/src/packages/rte/Umbraco.RichText.ts @@ -5,7 +5,7 @@ export const manifest: ManifestPropertyEditorSchema = { name: 'Rich Text', alias: 'Umbraco.RichText', meta: { - defaultPropertyEditorUiAlias: 'Umb.PropertyEditorUi.TinyMCE', + defaultPropertyEditorUiAlias: 'Umb.PropertyEditorUi.Tiptap', settings: { properties: [ { diff --git a/src/packages/rte/manifests.ts b/src/packages/rte/manifests.ts index 81d70617d7..acef781d4b 100644 --- a/src/packages/rte/manifests.ts +++ b/src/packages/rte/manifests.ts @@ -1,6 +1,11 @@ // eslint-disable-next-line local-rules/no-relative-import-to-import-map-module import { manifests as tiptapManifests } from './tiptap/manifests.js'; import { manifests as tinyMceManifests } from './tiny-mce/manifests.js'; +import { manifest as schemaManifest } from './Umbraco.RichText.js'; import type { ManifestTypes, UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; -export const manifests: Array = [...tinyMceManifests, ...tiptapManifests]; +export const manifests: Array = [ + ...tinyMceManifests, + ...tiptapManifests, + schemaManifest, +]; diff --git a/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts b/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts index 4e657fa2bc..cb68e5300c 100644 --- a/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts +++ b/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts @@ -1,4 +1,3 @@ -import { manifest as schemaManifest } from './Umbraco.RichText.js'; import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/property-editor'; export const UMB_BLOCK_RTE_PROPERTY_EDITOR_SCHEMA_ALIAS = 'Umbraco.RichText'; @@ -134,4 +133,4 @@ const manifest: ManifestPropertyEditorUi = { }, }; -export const manifests: Array = [manifest, schemaManifest]; +export const manifests: Array = [manifest]; From 09b02335cfc6fde3cd0fa8daa2fd8f7d8fa42417 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 08:58:52 +0200 Subject: [PATCH 198/241] merge main into v15/feature/tiptap --- ...ty-editor-ui-tiny-mce-dimensions-configuration.stories.ts | 2 +- .../property-editor-ui-tiny-mce-maximagesize.stories.ts | 2 +- ...y-editor-ui-tiny-mce-stylesheets-configuration.stories.ts | 2 +- ...perty-editor-ui-tiny-mce-toolbar-configuration.stories.ts | 2 +- src/packages/rte/tiptap/property-editors/manifests.ts | 4 ++-- ...erty-editor-ui-tiptap-extensions-configuration.element.ts | 3 ++- ...roperty-editor-ui-tiptap-toolbar-configuration.element.ts | 5 ++--- src/packages/rte/tiptap/property-editors/tiptap/manifests.ts | 4 ++-- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts b/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts index 70b2d39ad3..51322338cd 100644 --- a/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts +++ b/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts @@ -1,6 +1,6 @@ +import { umbDataTypeMockDb } from '../../../../../mocks/data/data-type/data-type.db.js'; import type { Meta } from '@storybook/web-components'; import './property-editor-ui-tiny-mce-dimensions-configuration.element.js'; -import { umbDataTypeMockDb } from '../../../../mocks/data/data-type/data-type.db.js'; import { html } from '@umbraco-cms/backoffice/external/lit'; import type { UmbDataTypeDetailModel } from '@umbraco-cms/backoffice/data-type'; diff --git a/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts b/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts index 714136611c..652aa6769e 100644 --- a/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts +++ b/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts @@ -1,4 +1,4 @@ -import { umbDataTypeMockDb } from '../../../../mocks/data/data-type/data-type.db.js'; +import { umbDataTypeMockDb } from '../../../../../mocks/data/data-type/data-type.db.js'; import type { Meta } from '@storybook/web-components'; import { html } from '@umbraco-cms/backoffice/external/lit'; import './property-editor-ui-tiny-mce-maximagesize.element.js'; diff --git a/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts b/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts index 1341c768a7..0c6292c87b 100644 --- a/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts +++ b/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts @@ -1,4 +1,4 @@ -import { umbDataTypeMockDb } from '../../../../mocks/data/data-type/data-type.db.js'; +import { umbDataTypeMockDb } from '../../../../../mocks/data/data-type/data-type.db.js'; import type { Meta } from '@storybook/web-components'; import { html } from '@umbraco-cms/backoffice/external/lit'; diff --git a/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts b/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts index 29f8543717..aa598bf356 100644 --- a/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts +++ b/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts @@ -1,4 +1,4 @@ -import { umbDataTypeMockDb } from '../../../../mocks/data/data-type/data-type.db.js'; +import { umbDataTypeMockDb } from '../../../../../mocks/data/data-type/data-type.db.js'; import type { Meta } from '@storybook/web-components'; import { html } from '@umbraco-cms/backoffice/external/lit'; diff --git a/src/packages/rte/tiptap/property-editors/manifests.ts b/src/packages/rte/tiptap/property-editors/manifests.ts index 2d8c3c7d66..9f9f7706f4 100644 --- a/src/packages/rte/tiptap/property-editors/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/manifests.ts @@ -1,8 +1,8 @@ // eslint-disable-next-line local-rules/no-relative-import-to-import-map-module import { manifests as tiptapManifests } from './tiptap/manifests.js'; -import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; +import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/property-editor'; -export const manifests: Array = [ +export const manifests: Array = [ ...tiptapManifests, { type: 'propertyEditorUi', diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts index 40649ae3d3..5805ceee12 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts @@ -2,10 +2,11 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import { customElement, css, html, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { umbExtensionsRegistry, type UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { UmbPropertyValueChangeEvent, type UmbPropertyEditorConfigCollection, + type UmbPropertyEditorUiElement, } from '@umbraco-cms/backoffice/property-editor'; type ExtensionConfig = { diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 39fd9f2136..136b2f73c6 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -10,9 +10,8 @@ import { type PropertyValueMap, } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { umbExtensionsRegistry, type UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; - -import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbPropertyValueChangeEvent, type UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/property-editor'; type Extension = { alias: string; diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 906c3b67b7..e3c51f2e65 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -1,6 +1,6 @@ -import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; +import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/property-editor'; -export const manifests: Array = [ +export const manifests: Array = [ { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.Tiptap', From c85b6648d99268e929fe2b8b4d81fd33b83c9141 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 09:05:38 +0200 Subject: [PATCH 199/241] test: add rudimentary test for tiptap property editor ui --- .../tiptap/property-editor-ui-tiptap.test.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.test.ts diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.test.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.test.ts new file mode 100644 index 0000000000..214fa7d487 --- /dev/null +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.test.ts @@ -0,0 +1,21 @@ +import { UmbPropertyEditorUiTiptapElement } from './property-editor-ui-tiptap.element.js'; +import { expect, fixture, html } from '@open-wc/testing'; +import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; + +describe('UmbPropertyEditorUITiptapElement', () => { + let element: UmbPropertyEditorUiTiptapElement; + + beforeEach(async () => { + element = await fixture(html` `); + }); + + it('is defined with its own instance', () => { + expect(element).to.be.instanceOf(UmbPropertyEditorUiTiptapElement); + }); + + if ((window as UmbTestRunnerWindow).__UMBRACO_TEST_RUN_A11Y_TEST) { + it('passes the a11y audit', async () => { + await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); + }); + } +}); From 6cff752006e906e338743250c74a081c9ff360f4 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 09:13:20 +0200 Subject: [PATCH 200/241] docs: add storybook story for tiptap --- .../property-editor-ui-tiptap.stories.ts | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.stories.ts diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.stories.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.stories.ts new file mode 100644 index 0000000000..ed597afb7a --- /dev/null +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.stories.ts @@ -0,0 +1,90 @@ +import type { UmbPropertyEditorUiTiptapElement } from './property-editor-ui-tiptap.element.js'; +import type { Meta, StoryObj } from '@storybook/web-components'; +import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; + +import './property-editor-ui-tiptap.element.js'; + +const config = new UmbPropertyEditorConfigCollection([ + { + alias: 'hideLabel', + value: true, + }, + { alias: 'dimensions', value: { height: 500 } }, + { alias: 'maxImageSize', value: 500 }, + { alias: 'ignoreUserStartNodes', value: false }, + { + alias: 'toolbar', + value: [ + [ + [ + ['Umb.Tiptap.Toolbar.Bold', 'Umb.Tiptap.Toolbar.Italic', 'Umb.Tiptap.Toolbar.Underline'], + [ + 'Umb.Tiptap.Toolbar.TextAlignLeft', + 'Umb.Tiptap.Toolbar.TextAlignCenter', + 'Umb.Tiptap.Toolbar.TextAlignRight', + ], + ['Umb.Tiptap.Toolbar.Heading1', 'Umb.Tiptap.Toolbar.Heading2', 'Umb.Tiptap.Toolbar.Heading3'], + ['Umb.Tiptap.Toolbar.Unlink', 'Umb.Tiptap.Toolbar.Link'], + ['Umb.Tiptap.Toolbar.Embed', 'Umb.Tiptap.Toolbar.MediaPicker', 'Umb.Tiptap.Toolbar.BlockPicker'], + ['Umb.Tiptap.Toolbar.Redo', 'Umb.Tiptap.Toolbar.Undo'], + ], + ], + ], + }, + { + alias: 'extensions', + value: [ + 'Umb.Tiptap.Bold', + 'Umb.Tiptap.Italic', + 'Umb.Tiptap.Underline', + 'Umb.Tiptap.Strike', + 'Umb.Tiptap.Blockquote', + 'Umb.Tiptap.CodeBlock', + 'Umb.Tiptap.HorizontalRule', + 'Umb.Tiptap.Figure', + 'Umb.Tiptap.Table', + 'Umb.Tiptap.Link', + 'Umb.Tiptap.Embed', + 'Umb.Tiptap.Image', + 'Umb.Tiptap.Heading', + 'Umb.Tiptap.List', + 'Umb.Tiptap.TextAlign', + 'Umb.Tiptap.MediaUpload', + 'Umb.Tiptap.Block', + ], + }, +]); + +const meta: Meta = { + title: 'Property Editor UIs/Tiptap', + component: 'umb-property-editor-ui-tiptap', + id: 'umb-property-editor-ui-tiptap', + args: { + config: undefined, + value: { + blocks: { + layout: {}, + contentData: [], + settingsData: [], + }, + markup: ` +

    Tiptap

    +

    I am a default value for the Tiptap text editor story.

    +

    + Umbraco documentation +

    + `, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const DefaultConfig: Story = { + args: { + config, + }, +}; From 088c9cbb758d4ffecaa26133a55c31b17807109d Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 09:24:11 +0200 Subject: [PATCH 201/241] feat: rename the property editor ui containing tinymce from "Rich Text Editor" to "Rich Text Editor [TinyMCE]" --- .../rte/tiny-mce/property-editors/tiny-mce/manifests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts b/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts index cb68e5300c..561fbceb4e 100644 --- a/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts +++ b/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts @@ -9,7 +9,7 @@ const manifest: ManifestPropertyEditorUi = { name: 'Rich Text Editor Property Editor UI', element: () => import('./property-editor-ui-tiny-mce.element.js'), meta: { - label: 'Rich Text Editor', + label: 'Rich Text Editor [TinyMCE]', propertyEditorSchemaAlias: UMB_BLOCK_RTE_PROPERTY_EDITOR_SCHEMA_ALIAS, icon: 'icon-browser-window', group: 'richContent', From 0d23e040826f95fc8f0b202a31441daaad6820b1 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 09:49:13 +0200 Subject: [PATCH 202/241] feat: migrate to `Umbraco.RichText` and `data-content-key` --- .../block-rte/tiptap-extension/block.extension.ts | 10 +++++----- src/packages/block/block-rte/types.ts | 8 ++++++++ src/packages/rte/components/rte-base.element.ts | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/packages/block/block-rte/tiptap-extension/block.extension.ts b/src/packages/block/block-rte/tiptap-extension/block.extension.ts index f60235b15e..2d00aa0486 100644 --- a/src/packages/block/block-rte/tiptap-extension/block.extension.ts +++ b/src/packages/block/block-rte/tiptap-extension/block.extension.ts @@ -1,4 +1,4 @@ -import { UMB_DATA_CONTENT_UDI, type UmbBlockRteLayoutModel } from '../types.js'; +import { UMB_DATA_CONTENT_KEY, type UmbBlockRteLayoutModel } from '../types.js'; import { UMB_BLOCK_RTE_MANAGER_CONTEXT } from '../context/index.js'; import { UmbTiptapExtensionApiBase } from '@umbraco-cms/backoffice/tiptap'; import { Node } from '@umbraco-cms/backoffice/external/tiptap'; @@ -28,7 +28,7 @@ const umbRteBlock = Node.create({ addAttributes() { return { - [UMB_DATA_CONTENT_UDI]: { + [UMB_DATA_CONTENT_KEY]: { isRequired: true, }, }; @@ -47,7 +47,7 @@ const umbRteBlock = Node.create({ setBlock: (options) => ({ commands }) => { - const attrs = { [UMB_DATA_CONTENT_UDI]: options.contentUdi }; + const attrs = { [UMB_DATA_CONTENT_KEY]: options.contentUdi }; return commands.insertContent({ type: this.name, attrs, @@ -75,7 +75,7 @@ const umbRteBlockInline = umbRteBlock.extend({ setBlockInline: (options) => ({ commands }) => { - const attrs = { [UMB_DATA_CONTENT_UDI]: options.contentUdi }; + const attrs = { [UMB_DATA_CONTENT_KEY]: options.contentUdi }; return commands.insertContent({ type: this.name, attrs, @@ -111,7 +111,7 @@ export default class UmbTiptapBlockElementApi extends UmbTiptapExtensionApiBase if (!editor) return; const existingBlocks = Array.from(editor.view.dom.querySelectorAll('umb-rte-block, umb-rte-block-inline')).map( - (x) => x.getAttribute(UMB_DATA_CONTENT_UDI), + (x) => x.getAttribute(UMB_DATA_CONTENT_KEY), ); const newBlocks = blocks.filter((x) => !existingBlocks.find((contentUdi) => contentUdi === x.udi)); diff --git a/src/packages/block/block-rte/types.ts b/src/packages/block/block-rte/types.ts index dca59515c5..1d0449f656 100644 --- a/src/packages/block/block-rte/types.ts +++ b/src/packages/block/block-rte/types.ts @@ -3,7 +3,15 @@ import type { UmbBlockLayoutBaseModel, UmbBlockValueType } from '@umbraco-cms/ba export const UMB_BLOCK_RTE_TYPE = 'block-rte-type'; export const UMB_BLOCK_RTE = 'block-rte'; +/** + * The attribute where the block content key is stored. + * @deprecated Use {@link UMB_DATA_CONTENT_KEY} instead + */ export const UMB_DATA_CONTENT_UDI = 'data-content-udi'; +/** + * The attribute where the block content key is stored. + */ +export const UMB_DATA_CONTENT_KEY = 'data-content-key'; export interface UmbBlockRteTypeModel extends UmbBlockTypeBaseModel { displayInline: boolean; diff --git a/src/packages/rte/components/rte-base.element.ts b/src/packages/rte/components/rte-base.element.ts index 0c1b6357ad..3577ebb6a4 100644 --- a/src/packages/rte/components/rte-base.element.ts +++ b/src/packages/rte/components/rte-base.element.ts @@ -18,7 +18,7 @@ export interface UmbRichTextEditorValueType { blocks: UmbBlockValueType; } -export const UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS = 'Umbraco.TinyMCE'; +export const UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS = 'Umbraco.RichText'; export abstract class UmbRteBaseElement extends UmbLitElement implements UmbPropertyEditorUiElement { public set config(config: UmbPropertyEditorConfigCollection | undefined) { From db050c9107f12f1e6b6e972752731abafe5afc1a Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Mon, 30 Sep 2024 10:00:06 +0200 Subject: [PATCH 203/241] make z-index relative to the editor --- .../rte/tiptap/components/input-tiptap/input-tiptap.element.ts | 3 ++- .../components/input-tiptap/tiptap-fixed-menu.element.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 7400057c30..d8267e4024 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -152,6 +152,8 @@ export class UmbInputTiptapElement extends UmbFormControlMixin Date: Mon, 30 Sep 2024 10:13:39 +0100 Subject: [PATCH 204/241] Prevents the backoffice router from listening to links inside Tiptap --- src/external/tiptap/extensions/tiptap-umb-link.extension.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/external/tiptap/extensions/tiptap-umb-link.extension.ts b/src/external/tiptap/extensions/tiptap-umb-link.extension.ts index 1576004644..9ba9433993 100644 --- a/src/external/tiptap/extensions/tiptap-umb-link.extension.ts +++ b/src/external/tiptap/extensions/tiptap-umb-link.extension.ts @@ -17,6 +17,7 @@ export const UmbLink = Link.extend({ ...this.parent?.(), HTMLAttributes: { target: '', + 'data-router-slot': 'disabled', }, }; }, From 58da807cd21e19f51f487b7b7b5d357075519365 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 12:24:19 +0200 Subject: [PATCH 205/241] feat: rename removeBlockUdi to removeBlockKey --- src/packages/block/block/context/block-manager.context.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/block/block/context/block-manager.context.ts b/src/packages/block/block/context/block-manager.context.ts index 2e0222f8d5..d355bb27f2 100644 --- a/src/packages/block/block/context/block-manager.context.ts +++ b/src/packages/block/block/context/block-manager.context.ts @@ -307,7 +307,7 @@ export abstract class UmbBlockManagerContext< } } - protected removeBlockUdi(contentUdi: string) { - this.#contents.removeOne(contentUdi); + protected removeBlockKey(contentKey: string) { + this.#contents.removeOne(contentKey); } } From 38caff31646ccca4b7171e2a792b18f36c8e24d7 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 12:24:26 +0200 Subject: [PATCH 206/241] feat: rename removeBlockUdi to removeBlockKey --- .../block/block-rte/context/block-rte-manager.context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/block/block-rte/context/block-rte-manager.context.ts b/src/packages/block/block-rte/context/block-rte-manager.context.ts index a09b598419..876dfb06d8 100644 --- a/src/packages/block/block-rte/context/block-rte-manager.context.ts +++ b/src/packages/block/block-rte/context/block-rte-manager.context.ts @@ -43,6 +43,6 @@ export class UmbBlockRteManagerContext< * @internal */ public deleteLayoutElement(contentKey: string) { - this.removeBlockUdi(contentKey); + this.removeBlockKey(contentKey); } } From 4e681cb38a315c6c73e19a90c1c053760af04fbd Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 12:24:45 +0200 Subject: [PATCH 207/241] feat: insert rte blocks --- src/packages/rte/components/rte-base.element.ts | 3 +-- .../tiptap/property-editor-ui-tiptap.element.ts | 15 ++++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/packages/rte/components/rte-base.element.ts b/src/packages/rte/components/rte-base.element.ts index 71ec6584d6..9e142e203e 100644 --- a/src/packages/rte/components/rte-base.element.ts +++ b/src/packages/rte/components/rte-base.element.ts @@ -127,7 +127,6 @@ export abstract class UmbRteBaseElement extends UmbLitElement implements UmbProp this.#managerContext.exposes, ]).pipe(debounceTime(20)), ([layouts, contents, settings, exposes]) => { - console.log('new blocks', layouts, contents, exposes); this._value = { ...this._value, blocks: { @@ -137,7 +136,7 @@ export abstract class UmbRteBaseElement extends UmbLitElement implements UmbProp expose: exposes, }, }; - //context.setValue(this._value); + this._fireChangeEvent(); }, 'motherObserver', diff --git a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index 54d0590c55..8d56fc1943 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts @@ -13,12 +13,6 @@ const elementName = 'umb-property-editor-ui-tiptap'; export class UmbPropertyEditorUiTiptapElement extends UmbRteBaseElement { #onChange(event: CustomEvent & { target: UmbInputTiptapElement }) { const value = event.target.value; - this._latestMarkup = value; - - this._value = { - ...this._value, - markup: this._latestMarkup, - }; // Remove unused Blocks of Blocks Layout. Leaving only the Blocks that are present in Markup. const usedContentKeys: string[] = []; @@ -28,7 +22,7 @@ export class UmbPropertyEditorUiTiptapElement extends UmbRteBaseElement { /(?:)?<\/umb-rte-block(?:-inline)?>/gi, ); let blockElement: RegExpExecArray | null; - while ((blockElement = regex.exec(this._latestMarkup)) !== null) { + while ((blockElement = regex.exec(value)) !== null) { if (blockElement.groups?.key) { usedContentKeys.push(blockElement.groups.key); } @@ -36,6 +30,13 @@ export class UmbPropertyEditorUiTiptapElement extends UmbRteBaseElement { this._filterUnusedBlocks(usedContentKeys); + this._latestMarkup = value; + + this._value = { + ...this._value, + markup: this._latestMarkup, + }; + this._fireChangeEvent(); } From 29bbf8eb2abf9752498fc526933976bbfd66c16c Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Mon, 30 Sep 2024 12:40:03 +0200 Subject: [PATCH 208/241] feat: add subscript and superscript and generate icons --- package-lock.json | 26 +++++++++++++++++ package.json | 2 ++ src/external/tiptap/index.ts | 2 ++ .../core/icon-registry/icon-dictionary.json | 8 ++++++ src/packages/core/icon-registry/icons.ts | 10 ++++++- .../core/icon-registry/icons/icon-science.ts | 2 +- .../icon-registry/icons/icon-subscript.ts | 16 +++++++++++ .../icon-registry/icons/icon-superscript.ts | 16 +++++++++++ .../core/icon-registry/icons/icon-unlink.ts | 2 +- .../rte/tiptap/extensions/core/manifests.ts | 28 ++++++++++++++++++- .../extensions/core/subscript.extension.ts | 6 ++++ .../extensions/core/superscript.extension.ts | 6 ++++ .../tiptap/extensions/toolbar/manifests.ts | 28 +++++++++++++++++++ .../extensions/toolbar/subscript.extension.ts | 8 ++++++ .../toolbar/superscript.extension.ts | 8 ++++++ 15 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 src/packages/core/icon-registry/icons/icon-subscript.ts create mode 100644 src/packages/core/icon-registry/icons/icon-superscript.ts create mode 100644 src/packages/rte/tiptap/extensions/core/subscript.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/core/superscript.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/toolbar/subscript.extension.ts create mode 100644 src/packages/rte/tiptap/extensions/toolbar/superscript.extension.ts diff --git a/package-lock.json b/package-lock.json index 037c333a6b..1e261c3d8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,8 @@ "@tiptap/extension-paragraph": "^2.7.4", "@tiptap/extension-placeholder": "^2.7.4", "@tiptap/extension-strike": "^2.7.4", + "@tiptap/extension-subscript": "^2.7.4", + "@tiptap/extension-superscript": "^2.7.4", "@tiptap/extension-table": "^2.7.4", "@tiptap/extension-table-cell": "^2.7.4", "@tiptap/extension-table-header": "^2.7.4", @@ -6749,6 +6751,30 @@ "@tiptap/core": "^2.7.0" } }, + "node_modules/@tiptap/extension-subscript": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-subscript/-/extension-subscript-2.7.4.tgz", + "integrity": "sha512-EZLwt/u1PQcIVuXRA+Lq8zVuzLxajNiJi5C2XqwvyLhhNGySvYqWCy2Nr80dTiwOe+yZVr9gwVQOvOE53EHW2A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, + "node_modules/@tiptap/extension-superscript": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/@tiptap/extension-superscript/-/extension-superscript-2.7.4.tgz", + "integrity": "sha512-AFYvbVCkOsix+2QVTl036LJeMpNNJT/XOCnxcCaWUeVwNKxrLxlGLzwrNqCC7hW6eYd73/Ht4+mzGIAGM78PPA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, "node_modules/@tiptap/extension-table": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.7.4.tgz", diff --git a/package.json b/package.json index d3b621c879..cecbbf61d6 100644 --- a/package.json +++ b/package.json @@ -213,6 +213,8 @@ "@tiptap/extension-paragraph": "^2.7.4", "@tiptap/extension-placeholder": "^2.7.4", "@tiptap/extension-strike": "^2.7.4", + "@tiptap/extension-subscript": "^2.7.4", + "@tiptap/extension-superscript": "^2.7.4", "@tiptap/extension-table": "^2.7.4", "@tiptap/extension-table-cell": "^2.7.4", "@tiptap/extension-table-header": "^2.7.4", diff --git a/src/external/tiptap/index.ts b/src/external/tiptap/index.ts index 587e432476..d7daf5502a 100644 --- a/src/external/tiptap/index.ts +++ b/src/external/tiptap/index.ts @@ -23,6 +23,8 @@ export { Link } from '@tiptap/extension-link'; export { ListItem } from '@tiptap/extension-list-item'; export { OrderedList } from '@tiptap/extension-ordered-list'; export { Strike } from '@tiptap/extension-strike'; +export { Subscript } from '@tiptap/extension-subscript'; +export { Superscript } from '@tiptap/extension-superscript'; export { Table } from '@tiptap/extension-table'; export { TableCell } from '@tiptap/extension-table-cell'; export { TableHeader } from '@tiptap/extension-table-header'; diff --git a/src/packages/core/icon-registry/icon-dictionary.json b/src/packages/core/icon-registry/icon-dictionary.json index f1cffa4000..4a097fd118 100644 --- a/src/packages/core/icon-registry/icon-dictionary.json +++ b/src/packages/core/icon-registry/icon-dictionary.json @@ -1966,6 +1966,14 @@ "name": "icon-strikethrough", "file": "strikethrough.svg" }, + { + "name": "icon-subscript", + "file": "subscript.svg" + }, + { + "name": "icon-superscript", + "file": "superscript.svg" + }, { "name": "icon-sunny", "file": "sun.svg" diff --git a/src/packages/core/icon-registry/icons.ts b/src/packages/core/icon-registry/icons.ts index a011051d45..9849a23c35 100644 --- a/src/packages/core/icon-registry/icons.ts +++ b/src/packages/core/icon-registry/icons.ts @@ -1712,7 +1712,7 @@ name: "icon-shipping", path: () => import("./icons/icon-shipping.js"), },{ name: "icon-shoe", - +legacy: true, path: () => import("./icons/icon-shoe.js"), },{ name: "icon-shopping-basket-alt-2", @@ -1851,6 +1851,14 @@ name: "icon-strikethrough", path: () => import("./icons/icon-strikethrough.js"), },{ +name: "icon-subscript", + +path: () => import("./icons/icon-subscript.js"), +},{ +name: "icon-superscript", + +path: () => import("./icons/icon-superscript.js"), +},{ name: "icon-sunny", path: () => import("./icons/icon-sunny.js"), diff --git a/src/packages/core/icon-registry/icons/icon-science.ts b/src/packages/core/icon-registry/icons/icon-science.ts index b45b7ed1d5..5c6f524a0b 100644 --- a/src/packages/core/icon-registry/icons/icon-science.ts +++ b/src/packages/core/icon-registry/icons/icon-science.ts @@ -1,4 +1,4 @@ -export default ` +export default ` + + + + + +`; \ No newline at end of file diff --git a/src/packages/core/icon-registry/icons/icon-superscript.ts b/src/packages/core/icon-registry/icons/icon-superscript.ts new file mode 100644 index 0000000000..e2b7852d44 --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-superscript.ts @@ -0,0 +1,16 @@ +export default ` + + + + + +`; \ No newline at end of file diff --git a/src/packages/core/icon-registry/icons/icon-unlink.ts b/src/packages/core/icon-registry/icons/icon-unlink.ts index d770affee6..7a308ee40d 100644 --- a/src/packages/core/icon-registry/icons/icon-unlink.ts +++ b/src/packages/core/icon-registry/icons/icon-unlink.ts @@ -1,4 +1,4 @@ -export default ` +export default ` = [ alias: 'Umb.Tiptap.Strike', name: 'Strike Tiptap Extension', api: () => import('./strike.extension.js'), - weight: 996, + weight: 998, meta: { icon: 'icon-strikethrough', label: 'Strike', group: 'Text Formatting', }, }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Subscript', + name: 'Subscript Tiptap Extension', + api: () => import('./subscript.extension.js'), + weight: 1010, + meta: { + icon: 'icon-subscript', + label: 'Subscript', + group: 'Text Formatting', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Superscript', + name: 'Superscript Tiptap Extension', + api: () => import('./superscript.extension.js'), + weight: 1011, + meta: { + icon: 'icon-superscript', + label: 'Superscript', + group: 'Text Formatting', + }, + }, { type: 'tiptapExtension', kind: 'button', diff --git a/src/packages/rte/tiptap/extensions/core/subscript.extension.ts b/src/packages/rte/tiptap/extensions/core/subscript.extension.ts new file mode 100644 index 0000000000..57bfb91472 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/subscript.extension.ts @@ -0,0 +1,6 @@ +import { UmbTiptapExtensionApiBase } from '../types.js'; +import { Subscript } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapBoldExtensionApi extends UmbTiptapExtensionApiBase { + getTiptapExtensions = () => [Subscript]; +} diff --git a/src/packages/rte/tiptap/extensions/core/superscript.extension.ts b/src/packages/rte/tiptap/extensions/core/superscript.extension.ts new file mode 100644 index 0000000000..453efb13e5 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/core/superscript.extension.ts @@ -0,0 +1,6 @@ +import { UmbTiptapExtensionApiBase } from '../types.js'; +import { Superscript } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapBoldExtensionApi extends UmbTiptapExtensionApiBase { + getTiptapExtensions = () => [Superscript]; +} diff --git a/src/packages/rte/tiptap/extensions/toolbar/manifests.ts b/src/packages/rte/tiptap/extensions/toolbar/manifests.ts index 6ac9973d8d..d666f66bd8 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/manifests.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/manifests.ts @@ -102,6 +102,34 @@ export const manifests: Array import('./subscript.extension.js'), + weight: 1010, + meta: { + alias: 'subscript', + icon: 'icon-subscript', + label: 'Subscript', + isDefault: true, + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Superscript', + name: 'Superscript Tiptap Extension', + api: () => import('./superscript.extension.js'), + weight: 1011, + meta: { + alias: 'superscript', + icon: 'icon-superscript', + label: 'Superscript', + isDefault: true, + }, + }, { type: 'tiptapToolbarExtension', kind: 'button', diff --git a/src/packages/rte/tiptap/extensions/toolbar/subscript.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/subscript.extension.ts new file mode 100644 index 0000000000..6753abec31 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/toolbar/subscript.extension.ts @@ -0,0 +1,8 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapBoldExtensionApi extends UmbTiptapToolbarElementApiBase { + override execute(editor?: Editor) { + editor?.chain().focus().toggleSubscript().run(); + } +} diff --git a/src/packages/rte/tiptap/extensions/toolbar/superscript.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/superscript.extension.ts new file mode 100644 index 0000000000..18bc7a7505 --- /dev/null +++ b/src/packages/rte/tiptap/extensions/toolbar/superscript.extension.ts @@ -0,0 +1,8 @@ +import { UmbTiptapToolbarElementApiBase } from '../types.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export default class UmbTiptapBoldExtensionApi extends UmbTiptapToolbarElementApiBase { + override execute(editor?: Editor) { + editor?.chain().focus().toggleSuperscript().run(); + } +} From 33e240ebcc69245755201cdb8cdbec647b0e7306 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 12:05:33 +0100 Subject: [PATCH 209/241] Renders Tiptap toolbar, with rows and groups --- .../input-tiptap/tiptap-fixed-menu.element.ts | 82 +++++++++++++++---- 1 file changed, 65 insertions(+), 17 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 156a19d1b4..021ee6d607 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -1,5 +1,6 @@ -import type { ManifestTiptapToolbarExtension } from '../../extensions/tiptap-toolbar-extension.js'; -import { css, customElement, html, property } from '@umbraco-cms/backoffice/external/lit'; +import { css, customElement, html, map, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbExtensionsElementAndApiInitializer } from '@umbraco-cms/backoffice/extension-api'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; @@ -10,6 +11,12 @@ const elementName = 'umb-tiptap-fixed-menu'; @customElement(elementName) export class UmbTiptapFixedMenuElement extends UmbLitElement { + #attached = false; + #extensionsController?: UmbExtensionsElementAndApiInitializer; + + @state() + private _lookup?: Map; + @property({ type: Boolean, reflect: true }) readonly = false; @@ -20,21 +27,58 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { configuration?: UmbPropertyEditorConfigCollection; @property({ attribute: false }) - toolbar: string[][][] = [[[]]]; + toolbar: Array>> = [[[]]]; + + override connectedCallback(): void { + super.connectedCallback(); + this.#attached = true; + this.#observeExtensions(); + } + override disconnectedCallback(): void { + this.#attached = false; + this.#extensionsController?.destroy(); + this.#extensionsController = undefined; + super.disconnectedCallback(); + } + + #observeExtensions(): void { + if (!this.#attached) return; + this.#extensionsController?.destroy(); + + this.#extensionsController = new UmbExtensionsElementAndApiInitializer( + this, + umbExtensionsRegistry, + 'tiptapToolbarExtension', + [], + (manifest) => this.toolbar.flat(2).includes(manifest.alias), + (extensionControllers) => { + this._lookup = new Map(extensionControllers.map((ext) => [ext.alias, ext.component])); + }, + ); + + this.#extensionsController.apiProperties = { configuration: this.configuration }; + this.#extensionsController.elementProperties = { editor: this.editor, configuration: this.configuration }; + } override render() { return html` - - this.toolbar.flat(2).includes(ext.alias) && (!!ext.kind || !!ext.element)} - .elementProps=${{ editor: this.editor, configuration: this.configuration }} - .apiProps=${{ configuration: this.configuration }}> - + ${map( + this.toolbar, + (row) => html` +
    + ${map(row, (group) => html`
    ${map(group, (alias) => this._lookup?.get(alias))}
    `)} +
    + `, + )} `; } static override readonly styles = css` + :host([readonly]) { + pointer-events: none; + background-color: var(--uui-color-surface-alt); + } + :host { border-radius: var(--uui-border-radius); border: 1px solid var(--uui-color-border); @@ -42,21 +86,25 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { border-bottom-right-radius: 0; background-color: var(--uui-color-surface); color: var(--color-text); - display: grid; - grid-template-columns: repeat(auto-fill, minmax(24px, 1fr)); - gap: var(--uui-size-space-1); + display: flex; + flex-direction: column; position: sticky; top: -25px; left: 0px; right: 0px; padding: var(--uui-size-space-3); - align-items: center; z-index: 9999999; } - :host([readonly]) { - pointer-events: none; - background-color: var(--uui-color-surface-alt); + .row { + display: flex; + flex-direction: row; + gap: var(--uui-size-space-3); + } + + .group { + display: flex; + flex-direction: row; } `; } From f4345eda09d16f0ab023d8b6d082d4a3c3b9ed68 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 12:18:07 +0100 Subject: [PATCH 210/241] Moved the Tiptap toolbar value type to its own `UmbTiptapToolbarValue` type --- .../input-tiptap/input-tiptap.element.ts | 6 ++--- .../input-tiptap/tiptap-fixed-menu.element.ts | 3 ++- src/packages/rte/tiptap/extensions/types.ts | 2 ++ ...ui-tiptap-toolbar-configuration.element.ts | 23 +++++++------------ 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index d8267e4024..95ecea0805 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -1,4 +1,4 @@ -import type { UmbTiptapExtensionApi } from '../../extensions/types.js'; +import type { UmbTiptapExtensionApi, UmbTiptapToolbarValue } from '../../extensions/types.js'; import { css, customElement, html, property, state, when } from '@umbraco-cms/backoffice/external/lit'; import { loadManifestApi } from '@umbraco-cms/backoffice/extension-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; @@ -75,7 +75,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin('toolbar') ?? [[[]]]; + this._toolbar = this.configuration?.getValueByAlias('toolbar') ?? [[[]]]; const extensions = this._extensions .map((ext) => ext.getTiptapExtensions({ configuration: this.configuration })) diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 021ee6d607..8466830bc9 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -1,3 +1,4 @@ +import type { UmbTiptapToolbarValue } from '../../extensions/types.js'; import { css, customElement, html, map, property, state } from '@umbraco-cms/backoffice/external/lit'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { UmbExtensionsElementAndApiInitializer } from '@umbraco-cms/backoffice/extension-api'; @@ -27,7 +28,7 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { configuration?: UmbPropertyEditorConfigCollection; @property({ attribute: false }) - toolbar: Array>> = [[[]]]; + toolbar: UmbTiptapToolbarValue = [[[]]]; override connectedCallback(): void { super.connectedCallback(); diff --git a/src/packages/rte/tiptap/extensions/types.ts b/src/packages/rte/tiptap/extensions/types.ts index ec2bcc1f8c..927d22cf63 100644 --- a/src/packages/rte/tiptap/extensions/types.ts +++ b/src/packages/rte/tiptap/extensions/types.ts @@ -98,3 +98,5 @@ export abstract class UmbTiptapToolbarElementApiBase extends UmbControllerBase i return editor && this.manifest?.meta.alias ? editor?.isActive(this.manifest.meta.alias) : false; } } + +export type UmbTiptapToolbarValue = Array>>; diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 136b2f73c6..dc18221309 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -1,17 +1,10 @@ -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { - customElement, - css, - html, - property, - state, - repeat, - nothing, - type PropertyValueMap, -} from '@umbraco-cms/backoffice/external/lit'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import type { UmbTiptapToolbarValue } from '../extensions/types.js'; +import { customElement, css, html, property, state, repeat, nothing } from '@umbraco-cms/backoffice/external/lit'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbPropertyValueChangeEvent, type UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/property-editor'; +import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; type Extension = { alias: string; @@ -25,7 +18,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement implements UmbPropertyEditorUiElement { @property({ attribute: false }) - set value(value: string[][][] | undefined) { + set value(value: UmbTiptapToolbarValue | undefined) { if (!value) { this.#useDefault = true; this.#value = [[[]]]; @@ -36,14 +29,14 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement this.#value = value.map((rows) => rows.map((groups) => [...groups])); } - get value(): string[][][] { + get value(): UmbTiptapToolbarValue { // TODO: This can be optimized with cashing; return this.#value.map((rows) => rows.map((groups) => [...groups])); } #useDefault = false; - #value: string[][][] = [[[]]]; + #value: UmbTiptapToolbarValue = [[[]]]; @state() _extensions: Extension[] = []; From 6dbbe0bac2345c147576f81eb1a0aa54323e61f8 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 13:57:46 +0200 Subject: [PATCH 211/241] merge origin/main into v15/feature/tiptap --- .../block/block/context/block-entries.context.ts | 2 +- src/packages/rte/components/rte-base.element.ts | 15 ++++++++------- .../plugins/tiny-mce-block-picker.plugin.ts | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/packages/block/block/context/block-entries.context.ts b/src/packages/block/block/context/block-entries.context.ts index 0a6d0da85d..5e7c44ae15 100644 --- a/src/packages/block/block/context/block-entries.context.ts +++ b/src/packages/block/block/context/block-entries.context.ts @@ -95,7 +95,7 @@ export abstract class UmbBlockEntriesContext< layoutEntry: BlockLayoutType, content: UmbBlockDataModel, settings: UmbBlockDataModel | undefined, - originData: UmbBlockWorkspaceOriginData, + originData: BlockOriginData, ): Promise; //edit? //editSettings diff --git a/src/packages/rte/components/rte-base.element.ts b/src/packages/rte/components/rte-base.element.ts index 9e142e203e..e775b39d3a 100644 --- a/src/packages/rte/components/rte-base.element.ts +++ b/src/packages/rte/components/rte-base.element.ts @@ -27,13 +27,6 @@ export abstract class UmbRteBaseElement extends UmbLitElement implements UmbProp this.#managerContext.setEditorConfiguration(config); } - /** - * Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content. - * @default false - */ - @property({ type: Boolean, reflect: true }) - readonly = false; - @property({ attribute: false, type: Object, @@ -45,6 +38,7 @@ export abstract class UmbRteBaseElement extends UmbLitElement implements UmbProp const buildUpValue: Partial = value ? { ...value } : {}; buildUpValue.markup ??= ''; buildUpValue.blocks ??= { layout: {}, contentData: [], settingsData: [], expose: [] }; + buildUpValue.blocks.layout ??= {}; buildUpValue.blocks.contentData ??= []; buildUpValue.blocks.settingsData ??= []; buildUpValue.blocks.expose ??= []; @@ -64,6 +58,13 @@ export abstract class UmbRteBaseElement extends UmbLitElement implements UmbProp return this._value; } + /** + * Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content. + * @default false + */ + @property({ type: Boolean, reflect: true }) + readonly = false; + @state() protected _config?: UmbPropertyEditorConfigCollection; diff --git a/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts b/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts index d188364b6f..03194589ba 100644 --- a/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts +++ b/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts @@ -114,7 +114,7 @@ export default class UmbTinyMceMultiUrlPickerPlugin extends UmbTinyMcePluginBase const blockEl = `<${blockTag} ${UMB_BLOCK_RTE_DATA_CONTENT_KEY}="${block.key}">`; - editor.insertContent(blockEl); + editor.selection.setContent(blockEl); }); } } From 6483425570b726030ae150a18a8cea01b34735d5 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 10:21:57 +0100 Subject: [PATCH 212/241] Localized the extension label --- ...tiptap-extensions-configuration.element.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts index 5805ceee12..a454a02829 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts @@ -125,24 +125,24 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement

    ${category.category}

    ${repeat( category.extensions, - (item) => - html`
    + (item) => html` +
    this.#onExtensionClick(item)} - > - ${item.label} -
    `, + @click=${() => this.#onExtensionClick(item)}> + + + ${this.localize.string(item.label)} +
    + `, )}
    `, )} -
    `; } From f8ef74de646acf59376fd5c99cbec6b22e8c0716 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 10:23:07 +0100 Subject: [PATCH 213/241] Refactored the extension grouping to use `Object.groupBy` Also added sorting, to show the extensions in alphabetical order per group --- ...tiptap-extensions-configuration.element.ts | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts index a454a02829..394dde72a0 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts @@ -56,14 +56,16 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement super.firstUpdated(_changedProperties); this.observe(umbExtensionsRegistry.byType('tiptapExtension'), (extensions) => { - this._extensionConfigs = extensions.map((ext) => { - return { - alias: ext.alias, - label: ext.meta.label, - icon: ext.meta.icon, - group: ext.meta.group, - }; - }); + this._extensionConfigs = extensions + .sort((a, b) => a.alias.localeCompare(b.alias)) + .map((ext) => { + return { + alias: ext.alias, + label: ext.meta.label, + icon: ext.meta.icon, + group: ext.meta.group, + }; + }); if (!this.value) { // The default value is all extensions enabled @@ -84,18 +86,16 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement }; }); - const grouped = withSelectedProperty.reduce((acc: any, item) => { - const group = item.group || 'Uncategorized'; // Assign to "Uncategorized" if no group - if (!acc[group]) { - acc[group] = []; - } - acc[group].push(item); - return acc; - }, {}); - this._extensionCategories = Object.keys(grouped).map((group) => ({ - category: group.charAt(0).toUpperCase() + group.slice(1).replace(/-/g, ' '), - extensions: grouped[group], - })); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + const grouped = Object.groupBy(withSelectedProperty, (item: ExtensionConfig) => item.group || 'Uncategorized'); + + this._extensionCategories = Object.keys(grouped) + .sort((a, b) => a.localeCompare(b)) + .map((key) => ({ + category: key, + extensions: grouped[key], + })); } #onExtensionClick(item: ExtensionCategoryItem) { From 1a2f827924882836ec10cc5e0d0e69d716fdabe9 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 10:23:44 +0100 Subject: [PATCH 214/241] Amended max height/width config labels --- .../rte/tiptap/property-editors/tiptap/manifests.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index e3c51f2e65..af3774c108 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -29,15 +29,15 @@ export const manifests: Array = [ }, { alias: 'maxWidth', - label: 'MaxWidth', - description: 'Editor max width', + label: 'Maximum width', + description: 'Editor maximum width', propertyEditorUiAlias: 'Umb.PropertyEditorUi.Integer', weight: 20, }, { alias: 'maxHeight', - label: 'MaxHeight', - description: 'Editor max height', + label: 'Maximum height', + description: 'Editor maximum height', propertyEditorUiAlias: 'Umb.PropertyEditorUi.Integer', weight: 30, }, From e083121668e9e3e8c906ad3f7d1a96c148cdbc1b Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 10:41:12 +0100 Subject: [PATCH 215/241] Added localizations for Tiptap extension group names --- src/assets/lang/en.ts | 6 ++++ .../rte/tiptap/extensions/core/manifests.ts | 30 +++++++++---------- .../rte/tiptap/extensions/manifests.ts | 2 +- src/packages/rte/tiptap/plugins/manifests.ts | 4 +-- ...tiptap-extensions-configuration.element.ts | 2 +- 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/assets/lang/en.ts b/src/assets/lang/en.ts index e588340496..2a35ea59ae 100644 --- a/src/assets/lang/en.ts +++ b/src/assets/lang/en.ts @@ -2628,4 +2628,10 @@ export default { wordWrapConfigLabel: 'Word wrap', wordWrapConfigDescription: 'Enable word wrapping in the code editor.', }, + tiptap: { + extGroup_formatting: 'Text formatting', + extGroup_interactive: 'Interactive elements', + extGroup_media: 'Embeds and media', + extGroup_structure: 'Content structure', + } } as UmbLocalizationDictionary; diff --git a/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/packages/rte/tiptap/extensions/core/manifests.ts index 487db61305..45c0784597 100644 --- a/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/packages/rte/tiptap/extensions/core/manifests.ts @@ -11,7 +11,7 @@ export const manifests: Array = [ meta: { icon: 'icon-blockquote', label: 'Blockquote', - group: 'Content Structure', + group: '#tiptap_extGroup_structure', }, }, { @@ -24,7 +24,7 @@ export const manifests: Array = [ meta: { icon: 'icon-bold', label: 'Bold', - group: 'Text Formatting', + group: '#tiptap_extGroup_formatting', }, }, { @@ -37,7 +37,7 @@ export const manifests: Array = [ meta: { icon: 'icon-code', label: 'Code Block', - group: 'Content Structure', + group: '#tiptap_extGroup_structure', }, }, { @@ -49,7 +49,7 @@ export const manifests: Array = [ meta: { icon: 'icon-embed', label: '#general_embed', - group: 'Media and Embeds', + group: '#tiptap_extGroup_media', }, }, { @@ -61,7 +61,7 @@ export const manifests: Array = [ meta: { icon: 'icon-link', label: '#defaultdialogs_urlLinkPicker', - group: 'Interactive Elements', + group: '#tiptap_extGroup_interactive', }, }, { @@ -73,7 +73,7 @@ export const manifests: Array = [ meta: { icon: 'icon-frame', label: 'Figure', - group: 'Media and Embeds', + group: '#tiptap_extGroup_media', }, }, { @@ -86,7 +86,7 @@ export const manifests: Array = [ meta: { icon: 'icon-horizontal-rule', label: 'Horizontal Rule', - group: 'Content Structure', + group: '#tiptap_extGroup_structure', }, }, { @@ -97,7 +97,7 @@ export const manifests: Array = [ meta: { icon: 'icon-picture', label: 'Image', - group: 'Media and Embeds', + group: '#tiptap_extGroup_media', }, }, { @@ -110,7 +110,7 @@ export const manifests: Array = [ meta: { icon: 'icon-italic', label: 'Italic', - group: 'Text Formatting', + group: '#tiptap_extGroup_formatting', }, }, { @@ -123,7 +123,7 @@ export const manifests: Array = [ meta: { icon: 'icon-strikethrough', label: 'Strike', - group: 'Text Formatting', + group: '#tiptap_extGroup_formatting', }, }, { @@ -162,7 +162,7 @@ export const manifests: Array = [ meta: { icon: 'icon-table', label: 'Table', - group: 'Interactive Elements', + group: '#tiptap_extGroup_interactive', }, }, { @@ -175,7 +175,7 @@ export const manifests: Array = [ meta: { icon: 'icon-underline', label: 'Underline', - group: 'Text Formatting', + group: '#tiptap_extGroup_formatting', }, }, { @@ -186,7 +186,7 @@ export const manifests: Array = [ meta: { icon: 'icon-heading-1', label: 'Heading', - group: 'Text Formatting', + group: '#tiptap_extGroup_formatting', }, }, { @@ -197,7 +197,7 @@ export const manifests: Array = [ meta: { icon: 'icon-ordered-list', label: 'Ordered List', - group: 'Content Structure', + group: '#tiptap_extGroup_structure', }, }, { @@ -208,7 +208,7 @@ export const manifests: Array = [ meta: { icon: 'icon-text-align-justify', label: 'Text Align', - group: 'Content Structure', + group: '#tiptap_extGroup_structure', }, }, ]; diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index d24cdfe8b7..02fdd6dcb8 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -85,7 +85,7 @@ const umbExtensions: Array = [ meta: { icon: 'icon-image-up', label: 'Media upload', - group: 'Media and Embeds', + group: '#tiptap_extGroup_media', }, }, ]; diff --git a/src/packages/rte/tiptap/plugins/manifests.ts b/src/packages/rte/tiptap/plugins/manifests.ts index 463d795cfc..aafd570b89 100644 --- a/src/packages/rte/tiptap/plugins/manifests.ts +++ b/src/packages/rte/tiptap/plugins/manifests.ts @@ -8,9 +8,9 @@ export const manifests: Array import('./block.extension.js'), meta: { - icon: 'icon-block', + icon: 'icon-plugin', label: 'Block', - group: 'Interactive Elements', + group: '#tiptap_extGroup_interactive', }, }, { diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts index 394dde72a0..aa9398da88 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts @@ -122,7 +122,7 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement this._extensionCategories, (category) => html`
    -

    ${category.category}

    +

    ${this.localize.string(category.category)}

    ${repeat( category.extensions, (item) => html` From dcce03aa1f4c1fb27937503d5b0c8bc6dfd336e8 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 10:42:00 +0100 Subject: [PATCH 216/241] Tiptap extension config, changed `` to be a button then the labels are clickable. --- ...ty-editor-ui-tiptap-extensions-configuration.element.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts index aa9398da88..cea56a64f2 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts @@ -136,7 +136,12 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement @click=${() => this.#onExtensionClick(item)}> - ${this.localize.string(item.label)} + this.#onExtensionClick(item)}>
    `, )} From 1e4e558f1b3459e048c276e0c695dda48392461d Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 10:42:20 +0100 Subject: [PATCH 217/241] Removed the WIP notice --- ...perty-editor-ui-tiptap-toolbar-configuration.element.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 136b2f73c6..342e733fc9 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -224,13 +224,6 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement override render() { return html` -

    - WIP Feature - Rows, groups, and item order currently have no effect. -
    - However, items added to the toolbar will be saved and displayed in the editor according to their weight in the - manifest. -

    ${repeat(this.#value, (row, rowIndex) => this.#renderRow(row, rowIndex))} this.#addRow(this.#value.length)}>+ ${this.#renderExtensions()} From f3838788a8610aee3db946476d58e7a95904a71c Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 10:47:26 +0100 Subject: [PATCH 218/241] Renamed "Category" to "Group" in the code --- ...tiptap-extensions-configuration.element.ts | 41 ++++++++------- ...ui-tiptap-toolbar-configuration.element.ts | 51 ++++++++++--------- 2 files changed, 50 insertions(+), 42 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts index cea56a64f2..aaa8fc1278 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts @@ -16,16 +16,16 @@ type ExtensionConfig = { group: string; }; -type ExtensionCategoryItem = { +type ExtensionGroupItem = { alias: string; label: string; icon?: string; selected: boolean; }; -type ExtensionCategory = { - category: string; - extensions: ExtensionCategoryItem[]; +type ExtensionGroup = { + group: string; + extensions: ExtensionGroupItem[]; }; @customElement('umb-property-editor-ui-tiptap-extensions-configuration') @@ -47,7 +47,7 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement config?: UmbPropertyEditorConfigCollection; @state() - private _extensionCategories: ExtensionCategory[] = []; + private _extensionCategories: ExtensionGroup[] = []; @state() private _extensionConfigs: ExtensionConfig[] = []; @@ -93,12 +93,12 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement this._extensionCategories = Object.keys(grouped) .sort((a, b) => a.localeCompare(b)) .map((key) => ({ - category: key, + group: key, extensions: grouped[key], })); } - #onExtensionClick(item: ExtensionCategoryItem) { + #onExtensionClick(item: ExtensionGroupItem) { item.selected = !item.selected; if (!this.value) { @@ -120,11 +120,11 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement
    ${repeat( this._extensionCategories, - (category) => html` -
    -

    ${this.localize.string(category.category)}

    + (group) => html` +
    +

    ${this.localize.string(group.group)}

    ${repeat( - category.extensions, + group.extensions, (item) => html`
    ext.alias === alias); if (!extension) return nothing; - return html`
    this.#onDragStart(e, alias, [rowIndex, groupIndex, itemIndex])}> - -
    `; + return html` +
    this.#onDragStart(e, alias, [rowIndex, groupIndex, itemIndex])}> + +
    + `; } #renderGroup(group: string[], rowIndex: number, groupIndex: number) { @@ -232,21 +233,23 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement #renderExtensions() { // TODO: Can we avoid using a flat here? or is it okay for performance? - return html`
    - ${repeat( - this._extensions.filter((ext) => !this.#value.flat(2).includes(ext.alias)), - (extension) => html` -
    this.#onDragStart(e, extension.alias)}> - -
    - `, - )} -
    `; + return html` +
    + ${repeat( + this._extensions.filter((ext) => !this.#value.flat(2).includes(ext.alias)), + (extension) => html` +
    this.#onDragStart(e, extension.alias)}> + +
    + `, + )} +
    + `; } static override styles = [ From deef2ab87995c574e724a976582208ef3e0f1262 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:32:31 +0200 Subject: [PATCH 219/241] feat: allow data-content-key --- .../components/input-tiny-mce/input-tiny-mce.defaults.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts index a26ab76fc0..45705a7ec5 100644 --- a/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts +++ b/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts @@ -13,7 +13,7 @@ export const defaultFallbackConfig: RawEditorOptions = { '+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-s[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*],figure,figcaption,cite,video[*],audio[*],picture[*],source[*],canvas[*]', invalid_elements: 'font', extended_valid_elements: - '@[id|class|style],+umb-rte-block[!data-content-udi],+umb-rte-block-inline[!data-content-udi],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align],span[id|class|style|lang],figure,figcaption', + '@[id|class|style],+umb-rte-block[!data-content-key],+umb-rte-block-inline[!data-content-key],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align],span[id|class|style|lang],figure,figcaption', custom_elements: 'umb-rte-block,~umb-rte-block-inline', toolbar: [ 'styles', From 7caf006cf11e77889359e72d8be32517482f5492 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:45:46 +0200 Subject: [PATCH 220/241] fix: revert back to single value updates in multiple observers to avoid issues where a value set in one array would "remove" the other values, because this ends up triggering the property value change --- .../rte/components/rte-base.element.ts | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/components/rte-base.element.ts b/src/packages/rte/components/rte-base.element.ts index e775b39d3a..beb9d06164 100644 --- a/src/packages/rte/components/rte-base.element.ts +++ b/src/packages/rte/components/rte-base.element.ts @@ -120,7 +120,34 @@ export abstract class UmbRteBaseElement extends UmbLitElement implements UmbProp 'observePropertyAlias', ); - this.observe( + this.observe(this.#entriesContext.layoutEntries, (layouts) => { + // Update manager: + this.#managerContext.setLayouts(layouts); + }); + + // Observe the value of the property and update the editor value. + this.observe(this.#managerContext.layouts, (layouts) => { + this._value = { + ...this._value, + blocks: { ...this._value.blocks, layout: { [UMB_BLOCK_RTE_PROPERTY_EDITOR_SCHEMA_ALIAS]: layouts } }, + }; + this._fireChangeEvent(); + }); + this.observe(this.#managerContext.contents, (contents) => { + this._value = { ...this._value, blocks: { ...this._value.blocks, contentData: contents } }; + this._fireChangeEvent(); + }); + this.observe(this.#managerContext.settings, (settings) => { + this._value = { ...this._value, blocks: { ...this._value.blocks, settingsData: settings } }; + this._fireChangeEvent(); + }); + this.observe(this.#managerContext.exposes, (exposes) => { + this._value = { ...this._value, blocks: { ...this._value.blocks, expose: exposes } }; + this._fireChangeEvent(); + }); + + // The above could potentially be replaced with a single observeMultiple call, but it is not done for now to avoid potential issues with the order of the updates. + /*this.observe( observeMultiple([ this.#managerContext.layouts, this.#managerContext.contents, @@ -141,7 +168,7 @@ export abstract class UmbRteBaseElement extends UmbLitElement implements UmbProp this._fireChangeEvent(); }, 'motherObserver', - ); + );*/ }); this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, (context) => { this.#managerContext.setVariantId(context.getVariantId()); @@ -154,6 +181,10 @@ export abstract class UmbRteBaseElement extends UmbLitElement implements UmbProp } protected _filterUnusedBlocks(usedContentKeys: (string | null)[]) { + const unusedBlockContents = this.#managerContext.getContents().filter((x) => usedContentKeys.indexOf(x.key) === -1); + unusedBlockContents.forEach((blockContent) => { + this.#managerContext.removeOneContent(blockContent.key); + }); const unusedBlocks = this.#managerContext.getLayouts().filter((x) => usedContentKeys.indexOf(x.contentKey) === -1); unusedBlocks.forEach((blockLayout) => { this.#managerContext.removeOneLayout(blockLayout.contentKey); From 345220eea1d04b12ca588d04f30489ebcf1ca358 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:45:57 +0200 Subject: [PATCH 221/241] feat: add getContents() method --- src/packages/block/block/context/block-manager.context.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/packages/block/block/context/block-manager.context.ts b/src/packages/block/block/context/block-manager.context.ts index d355bb27f2..e823da7bfd 100644 --- a/src/packages/block/block/context/block-manager.context.ts +++ b/src/packages/block/block/context/block-manager.context.ts @@ -91,6 +91,9 @@ export abstract class UmbBlockManagerContext< setContents(contents: Array) { this.#contents.setValue(contents); } + getContents() { + return this.#contents.value; + } setSettings(settings: Array) { this.#settings.setValue(settings); } From ef36dffb3e43885079a56764a3c6ea6bf9c05049 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:48:22 +0200 Subject: [PATCH 222/241] chore: sort imports --- src/packages/rte/components/rte-base.element.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/packages/rte/components/rte-base.element.ts b/src/packages/rte/components/rte-base.element.ts index beb9d06164..c7e01a7dd5 100644 --- a/src/packages/rte/components/rte-base.element.ts +++ b/src/packages/rte/components/rte-base.element.ts @@ -12,8 +12,6 @@ import { type UmbBlockRteTypeModel, } from '@umbraco-cms/backoffice/block-rte'; import { UMB_PROPERTY_CONTEXT, UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property'; -import { observeMultiple } from '@umbraco-cms/backoffice/observable-api'; -import { debounceTime } from '@umbraco-cms/backoffice/external/rxjs'; export abstract class UmbRteBaseElement extends UmbLitElement implements UmbPropertyEditorUiElement { public set config(config: UmbPropertyEditorConfigCollection | undefined) { From 2054b815b74c1cabf78805ca97ef43b71511cf0a Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:01:11 +0200 Subject: [PATCH 223/241] responsive grid setup for toolbar button --- .../input-tiptap/tiptap-fixed-menu.element.ts | 46 +++++++++++-------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 8466830bc9..fd1e1e6099 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -62,16 +62,19 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { } override render() { - return html` - ${map( - this.toolbar, - (row) => html` -
    - ${map(row, (group) => html`
    ${map(group, (alias) => this._lookup?.get(alias))}
    `)} -
    - `, - )} - `; + return html`${map(this.toolbar, (row, rowIndex) => + map( + row, + (group, groupIndex) => + html`${map(group, (alias, aliasIndex) => { + const newRow = rowIndex !== 0 && groupIndex === 0 && aliasIndex === 0; + return html`
    + ${this._lookup?.get(alias)} +
    `; + })} +
    `, + ), + )} `; } static override readonly styles = css` @@ -87,8 +90,9 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { border-bottom-right-radius: 0; background-color: var(--uui-color-surface); color: var(--color-text); - display: flex; - flex-direction: column; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(10px, 1fr)); + grid-auto-flow: row; position: sticky; top: -25px; left: 0px; @@ -97,15 +101,19 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { z-index: 9999999; } - .row { - display: flex; - flex-direction: row; - gap: var(--uui-size-space-3); + .item { + grid-column: span 3; } - .group { - display: flex; - flex-direction: row; + .separator { + background-color: var(--uui-color-border); + width: 1px; + place-self: center; + height: 22px; + } + .separator:last-child, + .separator:has(+ [data-new-row]) { + display: none; } `; } From 093b6b6728ee456ae94994e26c6233ef845b4d83 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:02:24 +0200 Subject: [PATCH 224/241] feat: set tinymce to dirty when just inserting a block --- .../rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts b/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts index 03194589ba..4cdabebefb 100644 --- a/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts +++ b/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts @@ -115,6 +115,7 @@ export default class UmbTinyMceMultiUrlPickerPlugin extends UmbTinyMcePluginBase const blockEl = `<${blockTag} ${UMB_BLOCK_RTE_DATA_CONTENT_KEY}="${block.key}">`; editor.selection.setContent(blockEl); + editor.setDirty(true); }); } } From 9b72d2afaf0d973e63b496d7ffcc7ce5a2077173 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 13:44:20 +0100 Subject: [PATCH 225/241] Tiptap: replaced max height/width options with combined dimensions --- .../input-tiptap/input-tiptap.element.ts | 7 +++---- .../tiptap/property-editors/tiptap/manifests.ts | 15 ++++----------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 95ecea0805..f82d551198 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -105,10 +105,9 @@ export class UmbInputTiptapElement extends UmbFormControlMixin('maxWidth'); - const maxHeight = this.configuration?.getValueByAlias('maxHeight'); - if (maxWidth) this.setAttribute('style', `max-width: ${maxWidth}px;`); - if (maxHeight) element.setAttribute('style', `max-height: ${maxHeight}px;`); + const dimensions = this.configuration?.getValueByAlias<{ width?: number; height?: number }>('dimensions'); + if (dimensions?.width) this.setAttribute('style', `max-width: ${dimensions.width}px;`); + if (dimensions?.height) element.setAttribute('style', `max-height: ${dimensions.height}px;`); this._toolbar = this.configuration?.getValueByAlias('toolbar') ?? [[[]]]; diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index e3c51f2e65..0dfc51e663 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -28,19 +28,12 @@ export const manifests: Array = [ weight: 10, }, { - alias: 'maxWidth', - label: 'MaxWidth', - description: 'Editor max width', - propertyEditorUiAlias: 'Umb.PropertyEditorUi.Integer', + alias: 'dimensions', + label: 'Dimensions', + description: 'Set the maximum width and height of the editor', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.TinyMCE.DimensionsConfiguration', weight: 20, }, - { - alias: 'maxHeight', - label: 'MaxHeight', - description: 'Editor max height', - propertyEditorUiAlias: 'Umb.PropertyEditorUi.Integer', - weight: 30, - }, { alias: 'maxImageSize', label: 'Maximum size for inserted images', From 8881cbc3beb2519ccd61deb6cf0be6b7113ff408 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 13:38:01 +0100 Subject: [PATCH 226/241] Tiptap Link: Configured overlay size --- .../rte/tiptap/extensions/umb/link.extension.ts | 5 ++++- .../rte/tiptap/property-editors/tiptap/manifests.ts | 11 ++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/extensions/umb/link.extension.ts b/src/packages/rte/tiptap/extensions/umb/link.extension.ts index 13b907f40e..bbf928d420 100644 --- a/src/packages/rte/tiptap/extensions/umb/link.extension.ts +++ b/src/packages/rte/tiptap/extensions/umb/link.extension.ts @@ -4,6 +4,7 @@ import { UMB_LINK_PICKER_MODAL } from '@umbraco-cms/backoffice/multi-url-picker' import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbLinkPickerLink } from '@umbraco-cms/backoffice/multi-url-picker'; +import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui'; export default class UmbTiptapLinkExtensionApi extends UmbTiptapToolbarElementApiBase { override async execute(editor?: Editor) { @@ -12,8 +13,10 @@ export default class UmbTiptapLinkExtensionApi extends UmbTiptapToolbarElementAp const data = { config: {}, index: null }; const value = { link }; + const overlaySize = this.configuration?.getValueByAlias('overlaySize') ?? 'small'; + const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalHandler = modalManager.open(this, UMB_LINK_PICKER_MODAL, { data, value }); + const modalHandler = modalManager.open(this, UMB_LINK_PICKER_MODAL, { data, value, modal: { size: overlaySize } }); if (!modalHandler) return; diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 0dfc51e663..acdc401c54 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -42,8 +42,17 @@ export const manifests: Array = [ weight: 40, config: [{ alias: 'min', value: 0 }], }, + { + alias: 'overlaySize', + label: 'Overlay Size', + description: 'Select the width of the overlay (link picker)', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.OverlaySize', + weight: 50, + }, + ], + defaultData: [ + { alias: 'overlaySize', value: 'medium' }, ], - defaultData: [], }, }, }, From d7a6a6f9f0da3d8ebdcbc08cddff070310c32b76 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 12:46:43 +0100 Subject: [PATCH 227/241] Fixes Tiptap Table CSS (rogue nesting, assume bad merge conflict) --- .../rte/tiptap/components/input-tiptap/input-tiptap.element.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index f82d551198..cb1d68b538 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -263,6 +263,8 @@ export class UmbInputTiptapElement extends UmbFormControlMixin Date: Mon, 30 Sep 2024 15:07:57 +0200 Subject: [PATCH 228/241] bugfix: fix toolbar wobble and remove WIP tag --- .../components/input-tiptap/tiptap-fixed-menu.element.ts | 2 +- ...perty-editor-ui-tiptap-toolbar-configuration.element.ts | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index fd1e1e6099..37c837f0ce 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -91,7 +91,7 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { background-color: var(--uui-color-surface); color: var(--color-text); display: grid; - grid-template-columns: repeat(auto-fill, minmax(10px, 1fr)); + grid-template-columns: repeat(auto-fill, 10px); grid-auto-flow: row; position: sticky; top: -25px; diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index dc18221309..d0dfaa4a6a 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -217,13 +217,6 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement override render() { return html` -

    - WIP Feature - Rows, groups, and item order currently have no effect. -
    - However, items added to the toolbar will be saved and displayed in the editor according to their weight in the - manifest. -

    ${repeat(this.#value, (row, rowIndex) => this.#renderRow(row, rowIndex))} this.#addRow(this.#value.length)}>+ ${this.#renderExtensions()} From 2e0de831f77fefc780e7f788a811ee1015b8a338 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:51:21 +0200 Subject: [PATCH 229/241] feat: dispatch a Change event when inserting blocks in tiny --- .../rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts b/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts index 4cdabebefb..ddfc1e5a01 100644 --- a/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts +++ b/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts @@ -116,6 +116,7 @@ export default class UmbTinyMceMultiUrlPickerPlugin extends UmbTinyMcePluginBase editor.selection.setContent(blockEl); editor.setDirty(true); + editor.dispatch('Change'); }); } } From 986eb124207cd045c2213af15ad267b66cc806a7 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Sep 2024 16:07:54 +0200 Subject: [PATCH 230/241] chore: formatting --- src/packages/rte/tiptap/property-editors/tiptap/manifests.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index acdc401c54..3efdc1912d 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -50,9 +50,7 @@ export const manifests: Array = [ weight: 50, }, ], - defaultData: [ - { alias: 'overlaySize', value: 'medium' }, - ], + defaultData: [{ alias: 'overlaySize', value: 'medium' }], }, }, }, From 6e17a7a02b94ff15078402744b8306cd686c38ed Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 15:28:12 +0100 Subject: [PATCH 231/241] Localized Tiptap extension group name --- src/packages/rte/tiptap/extensions/core/manifests.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/packages/rte/tiptap/extensions/core/manifests.ts index 45c0784597..88bd91bcf0 100644 --- a/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/packages/rte/tiptap/extensions/core/manifests.ts @@ -136,7 +136,7 @@ export const manifests: Array = [ meta: { icon: 'icon-subscript', label: 'Subscript', - group: 'Text Formatting', + group: '#tiptap_extGroup_formatting', }, }, { @@ -149,7 +149,7 @@ export const manifests: Array = [ meta: { icon: 'icon-superscript', label: 'Superscript', - group: 'Text Formatting', + group: '#tiptap_extGroup_formatting', }, }, { From 9cb75e754d41c588b864135fc961ed8566af7ed8 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 15:35:17 +0100 Subject: [PATCH 232/241] Switched `maxImageSize` to use compact editor also sets the default config value. --- .../rte/tiptap/property-editors/tiptap/manifests.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 3efdc1912d..67edb85388 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -38,9 +38,8 @@ export const manifests: Array = [ alias: 'maxImageSize', label: 'Maximum size for inserted images', description: 'Maximum width or height - enter 0 to disable resizing', - propertyEditorUiAlias: 'Umb.PropertyEditorUi.Integer', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.TinyMCE.MaxImageSizeConfiguration', weight: 40, - config: [{ alias: 'min', value: 0 }], }, { alias: 'overlaySize', @@ -50,7 +49,10 @@ export const manifests: Array = [ weight: 50, }, ], - defaultData: [{ alias: 'overlaySize', value: 'medium' }], + defaultData: [ + { alias: 'maxImageSize', value: 500 }, + { alias: 'overlaySize', value: 'medium' }, + ], }, }, }, From abf2fea4ece08ce90ae525804b0364b6c390384f Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 17:00:18 +0100 Subject: [PATCH 233/241] Refactored default Tiptap toolbar configuration Removed the `isDefault` property, replaced with property-editor's `defaultData`. --- .../rte/tiptap/extensions/manifests.ts | 2 - .../extensions/tiptap-toolbar-extension.ts | 1 - .../tiptap/extensions/toolbar/manifests.ts | 17 -- ...tiptap-extensions-configuration.element.ts | 39 ++-- ...ui-tiptap-toolbar-configuration.element.ts | 169 +++++++++--------- .../property-editors/tiptap/manifests.ts | 18 ++ 6 files changed, 122 insertions(+), 124 deletions(-) diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index 02fdd6dcb8..c5daba1fa6 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -44,7 +44,6 @@ const umbToolbarExtensions: Array = []; @property({ attribute: false }) config?: UmbPropertyEditorConfigCollection; @state() - private _extensionCategories: ExtensionGroup[] = []; + private _extensionCategories: UmbTiptapExtensionGroup[] = []; @state() - private _extensionConfigs: ExtensionConfig[] = []; + private _extensionConfigs: UmbTiptapExtensionConfig[] = []; protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); @@ -69,7 +64,7 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement if (!this.value) { // The default value is all extensions enabled - this.#value = this._extensionConfigs.map((ext) => ext.alias); + this.value = this._extensionConfigs.map((ext) => ext.alias); this.dispatchEvent(new UmbPropertyValueChangeEvent()); } @@ -88,7 +83,7 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error - const grouped = Object.groupBy(withSelectedProperty, (item: ExtensionConfig) => item.group || 'Uncategorized'); + const grouped = Object.groupBy(withSelectedProperty, (item: UmbTiptapExtensionConfig) => item.group || 'Uncategorized'); this._extensionCategories = Object.keys(grouped) .sort((a, b) => a.localeCompare(b)) @@ -98,7 +93,7 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement })); } - #onExtensionClick(item: ExtensionGroupItem) { + #onExtensionClick(item: UmbTiptapExtensionGroupItem) { item.selected = !item.selected; if (!this.value) { @@ -106,9 +101,9 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement } if (item.selected) { - this.#value = [...this.value, item.alias]; + this.value = [...this.value, item.alias]; } else { - this.#value = this.value.filter((alias) => alias !== item.alias); + this.value = this.value.filter((alias) => alias !== item.alias); } this.requestUpdate('_extensionCategories'); @@ -203,10 +198,10 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement ]; } -export default UmbPropertyEditorUiTiptapExtensionsConfigurationElement; +export { UmbPropertyEditorUiTiptapExtensionsConfigurationElement as element }; declare global { interface HTMLElementTagNameMap { - 'umb-property-editor-ui-tiptap-extensions-configuration': UmbPropertyEditorUiTiptapExtensionsConfigurationElement; + [elementName]: UmbPropertyEditorUiTiptapExtensionsConfigurationElement; } } diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 7e28112c18..19d3a5d9a4 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -6,64 +6,52 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbPropertyValueChangeEvent, type UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/property-editor'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; -type Extension = { +type UmbTiptapToolbarExtension = { alias: string; label: string; icon: string; }; +const elementName = 'umb-property-editor-ui-tiptap-toolbar-configuration'; -@customElement('umb-property-editor-ui-tiptap-toolbar-configuration') +@customElement(elementName) export class UmbPropertyEditorUiTiptapToolbarConfigurationElement extends UmbLitElement implements UmbPropertyEditorUiElement { + #inUse: Set = new Set(); + + #currentDragItem?: { + alias: string; + fromPos?: [number, number, number]; + }; + + #lookup?: Map; + + @state() + private _extensions: Array = []; + @property({ attribute: false }) set value(value: UmbTiptapToolbarValue | undefined) { if (!value) { - this.#useDefault = true; this.#value = [[[]]]; - return; + } else { + // TODO: This can be optimized with cashing; + this.#value = value ? value.map((rows) => rows.map((groups) => [...groups])) : [[[]]]; + value.forEach((row) => row.forEach((group) => group.forEach((alias) => this.#inUse.add(alias)))); } - - // TODO: This can be optimized with cashing; - this.#value = value.map((rows) => rows.map((groups) => [...groups])); } - get value(): UmbTiptapToolbarValue { // TODO: This can be optimized with cashing; return this.#value.map((rows) => rows.map((groups) => [...groups])); } - - #useDefault = false; - #value: UmbTiptapToolbarValue = [[[]]]; - @state() - _extensions: Extension[] = []; - - #currentDragItem?: { - alias: string; - fromPos?: [number, number, number]; - }; - protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); this.observe(umbExtensionsRegistry.byType('tiptapToolbarExtension'), (extensions) => { - this._extensions = extensions.map((ext) => { - if (this.#useDefault && ext.meta.isDefault) { - this.#value[0][0].push(ext.alias); - } - return { - alias: ext.alias, - label: ext.meta.label, - icon: ext.meta.icon, - }; - }); - - if (this.#useDefault) { - this.dispatchEvent(new UmbPropertyValueChangeEvent()); - } + this._extensions = extensions.map((ext) => ({ alias: ext.alias, label: ext.meta.label, icon: ext.meta.icon })); + this.#lookup = new Map(this._extensions.map((ext) => [ext.alias, ext])); }); } @@ -121,15 +109,19 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement #insertItem = (alias: string, toPos: [number, number, number]) => { const [rowIndex, groupIndex, itemIndex] = toPos; + // Insert the item into the new position - this.#value[rowIndex][groupIndex].splice(itemIndex, 0, alias); + const inserted = this.#value[rowIndex][groupIndex].splice(itemIndex, 0, alias); + inserted.forEach((alias) => this.#inUse.add(alias)); this.dispatchEvent(new UmbPropertyValueChangeEvent()); }; #removeItem(from: [number, number, number]) { const [rowIndex, groupIndex, itemIndex] = from; - this.#value[rowIndex][groupIndex].splice(itemIndex, 1); + + const removed = this.#value[rowIndex][groupIndex].splice(itemIndex, 1); + removed.forEach((alias) => this.#inUse.delete(alias)); this.dispatchEvent(new UmbPropertyValueChangeEvent()); } @@ -140,12 +132,16 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement }; #removeGroup = (rowIndex: number, groupIndex: number) => { - if (groupIndex === 0) { - // Prevent removing the last group + if (this.#value[rowIndex].length > groupIndex) { + const removed = this.#value[rowIndex].splice(groupIndex, 1); + removed.forEach((group) => group.forEach((alias) => this.#inUse.delete(alias))); + } + + // Prevent leaving an empty group + if (this.#value[rowIndex].length === 0) { this.#value[rowIndex][groupIndex] = []; - } else { - this.#value[rowIndex].splice(groupIndex, 1); } + this.dispatchEvent(new UmbPropertyValueChangeEvent()); }; @@ -155,26 +151,46 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement }; #removeRow = (rowIndex: number) => { - if (rowIndex === 0) { - // Prevent removing the last row + if (this.#value.length > rowIndex) { + const removed = this.#value.splice(rowIndex, 1); + removed.forEach((row) => row.forEach((group) => group.forEach((alias) => this.#inUse.delete(alias)))); + } + + // Prevent leaving an empty row + if (this.#value.length === 0) { this.#value[rowIndex] = [[]]; - } else { - this.#value.splice(rowIndex, 1); } + this.dispatchEvent(new UmbPropertyValueChangeEvent()); }; - #renderItem(alias: string, rowIndex: number, groupIndex: number, itemIndex: number) { - const extension = this._extensions.find((ext) => ext.alias === alias); - if (!extension) return nothing; + override render() { return html` -
    this.#onDragStart(e, alias, [rowIndex, groupIndex, itemIndex])}> - + ${repeat(this.#value, (row, rowIndex) => this.#renderRow(row, rowIndex))} + this.#addRow(this.#value.length)}> + + Add row + + ${this.#renderExtensions()} + `; + } + + #renderRow(row: string[][], rowIndex: number) { + return html` +
    + ${repeat(row, (group, groupIndex) => this.#renderGroup(group, rowIndex, groupIndex))} + this.#addGroup(rowIndex, row.length)}> + + Add group + + this.#removeRow(rowIndex)}> + +
    `; } @@ -188,9 +204,9 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement @drop=${(e: DragEvent) => this.#onDrop(e, [rowIndex, groupIndex, group.length])}> ${group.map((alias, itemIndex) => this.#renderItem(alias, rowIndex, groupIndex, itemIndex))} this.#removeGroup(rowIndex, groupIndex)}> @@ -199,44 +215,33 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement `; } - #renderRow(row: string[][], rowIndex: number) { + #renderItem(alias: string, rowIndex: number, groupIndex: number, itemIndex: number) { + const extension = this.#lookup?.get(alias); + if (!extension) return nothing; return html` -
    - ${repeat(row, (group, groupIndex) => this.#renderGroup(group, rowIndex, groupIndex))} - this.#addGroup(rowIndex, row.length)}>+ - this.#removeRow(rowIndex)}> - - +
    this.#onDragStart(e, alias, [rowIndex, groupIndex, itemIndex])}> +
    `; } - override render() { - return html` - ${repeat(this.#value, (row, rowIndex) => this.#renderRow(row, rowIndex))} - this.#addRow(this.#value.length)}>+ - ${this.#renderExtensions()} - `; - } - #renderExtensions() { - // TODO: Can we avoid using a flat here? or is it okay for performance? return html`
    ${repeat( - this._extensions.filter((ext) => !this.#value.flat(2).includes(ext.alias)), + this._extensions.filter((ext) => !this.#inUse.has(ext.alias)), (extension) => html`
    this.#onDragStart(e, extension.alias)}> + title=${this.localize.string(extension.label)} + @dragstart=${(e: DragEvent) => this.#onDragStart(e, extension.alias)} + @dragend=${this.#onDragEnd}>
    `, @@ -315,10 +320,10 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement ]; } -export default UmbPropertyEditorUiTiptapToolbarConfigurationElement; +export { UmbPropertyEditorUiTiptapToolbarConfigurationElement as element }; declare global { interface HTMLElementTagNameMap { - 'umb-property-editor-ui-tiptap-toolbar-configuration': UmbPropertyEditorUiTiptapToolbarConfigurationElement; + [elementName]: UmbPropertyEditorUiTiptapToolbarConfigurationElement; } } diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 67edb85388..e3adef4605 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -50,6 +50,24 @@ export const manifests: Array = [ }, ], defaultData: [ + { + alias: 'toolbar', + value: [ + [ + ['Umb.Tiptap.Toolbar.CodeEditor'], + ['Umb.Tiptap.Toolbar.Bold', 'Umb.Tiptap.Toolbar.Italic', 'Umb.Tiptap.Toolbar.Underline'], + [ + 'Umb.Tiptap.Toolbar.TextAlignLeft', + 'Umb.Tiptap.Toolbar.TextAlignCenter', + 'Umb.Tiptap.Toolbar.TextAlignRight', + ], + ['Umb.Tiptap.Toolbar.BulletList', 'Umb.Tiptap.Toolbar.OrderedList'], + ['Umb.Tiptap.Toolbar.Blockquote', 'Umb.Tiptap.Toolbar.HorizontalRule'], + ['Umb.Tiptap.Toolbar.Link', 'Umb.Tiptap.Toolbar.Unlink'], + ['Umb.Tiptap.Toolbar.MediaPicker', 'Umb.Tiptap.Toolbar.Embed'], + ], + ], + }, { alias: 'maxImageSize', value: 500 }, { alias: 'overlaySize', value: 'medium' }, ], From d47a6846d3a488a706ae1476b9884d58a5eadd07 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 17:45:57 +0100 Subject: [PATCH 234/241] Installed Tiptap's StarterKit package https://tiptap.dev/docs/editor/extensions/functionality/starterkit This is to steamline the number of direct dependencies. --- package-lock.json | 191 ++++++++++-------- package.json | 20 +- src/external/tiptap/index.ts | 9 +- .../input-tiptap/input-tiptap.element.ts | 22 +- .../extensions/core/blockquote.extension.ts | 6 - .../tiptap/extensions/core/bold.extension.ts | 6 - .../extensions/core/code-block.extension.ts | 6 - .../extensions/core/heading.extension.ts | 6 - .../core/horizontal-rule.extension.ts | 6 - .../extensions/core/italic.extension.ts | 6 - .../tiptap/extensions/core/list.extension.ts | 6 - .../rte/tiptap/extensions/core/manifests.ts | 109 +--------- .../extensions/core/strike.extension.ts | 6 - .../rte/tiptap/extensions/manifests.ts | 13 +- 14 files changed, 123 insertions(+), 289 deletions(-) delete mode 100644 src/packages/rte/tiptap/extensions/core/blockquote.extension.ts delete mode 100644 src/packages/rte/tiptap/extensions/core/bold.extension.ts delete mode 100644 src/packages/rte/tiptap/extensions/core/code-block.extension.ts delete mode 100644 src/packages/rte/tiptap/extensions/core/heading.extension.ts delete mode 100644 src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts delete mode 100644 src/packages/rte/tiptap/extensions/core/italic.extension.ts delete mode 100644 src/packages/rte/tiptap/extensions/core/list.extension.ts delete mode 100644 src/packages/rte/tiptap/extensions/core/strike.extension.ts diff --git a/package-lock.json b/package-lock.json index 1e261c3d8f..a11cae44d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,36 +13,20 @@ ], "dependencies": { "@tiptap/core": "^2.7.4", - "@tiptap/extension-blockquote": "^2.7.4", - "@tiptap/extension-bold": "^2.7.4", - "@tiptap/extension-bullet-list": "^2.7.4", - "@tiptap/extension-code": "^2.7.4", - "@tiptap/extension-code-block": "^2.7.4", - "@tiptap/extension-document": "^2.7.4", - "@tiptap/extension-dropcursor": "^2.7.4", - "@tiptap/extension-gapcursor": "^2.7.4", - "@tiptap/extension-hard-break": "^2.7.4", - "@tiptap/extension-heading": "^2.7.4", - "@tiptap/extension-history": "^2.7.4", - "@tiptap/extension-horizontal-rule": "^2.7.4", "@tiptap/extension-image": "^2.7.4", - "@tiptap/extension-italic": "^2.7.4", "@tiptap/extension-link": "^2.7.4", - "@tiptap/extension-list-item": "^2.7.4", - "@tiptap/extension-ordered-list": "^2.7.4", - "@tiptap/extension-paragraph": "^2.7.4", "@tiptap/extension-placeholder": "^2.7.4", - "@tiptap/extension-strike": "^2.7.4", "@tiptap/extension-subscript": "^2.7.4", "@tiptap/extension-superscript": "^2.7.4", "@tiptap/extension-table": "^2.7.4", "@tiptap/extension-table-cell": "^2.7.4", "@tiptap/extension-table-header": "^2.7.4", "@tiptap/extension-table-row": "^2.7.4", - "@tiptap/extension-text": "^2.7.4", "@tiptap/extension-text-align": "^2.7.4", + "@tiptap/extension-text-style": "^2.8.0", "@tiptap/extension-underline": "^2.7.4", "@tiptap/pm": "^2.7.4", + "@tiptap/starter-kit": "^2.8.0", "@types/diff": "^5.2.1", "@types/dompurify": "^3.0.5", "@types/uuid": "^10.0.0", @@ -6490,9 +6474,9 @@ } }, "node_modules/@tiptap/core": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.7.4.tgz", - "integrity": "sha512-1VTQdNQChgxdVC8+b8QEW6cUxPSD9EDTzg9YRSLWtTtUDQ09sRSVs7eHIn1LcRHVs6PwcAsNgKE4FSjBw0sRlg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.8.0.tgz", + "integrity": "sha512-xsqDI4BNzYRWRtBq7+/38ThhqEr7uG9Njip1x+9/wgR3vWPBFnBkYJTz6jSxS35NRE6BSnERm4/B/vrLuY1Hdw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6502,9 +6486,9 @@ } }, "node_modules/@tiptap/extension-blockquote": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.7.4.tgz", - "integrity": "sha512-N6rhiwVRpsxRz4Qt8cvKgpqjBxdi8GTbU/v2MV/BTONWb7Ch9ajv9HO6koEDdOeb77JVhpWztzYysTjJo2KTyQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.8.0.tgz", + "integrity": "sha512-m3CKrOIvV7fY1Ak2gYf5LkKiz6AHxHpg6wxfVaJvdBqXgLyVtHo552N+A4oSHOSRbB4AG9EBQ2NeBM8cdEQ4MA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6514,9 +6498,9 @@ } }, "node_modules/@tiptap/extension-bold": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.7.4.tgz", - "integrity": "sha512-Yq2ErekgpsOLCGYfQc1H3tUdmecKHDBWTPesVtqg0ct/3ZbKskhFoR6bPQWZH/ZRXQb1ARA+aMp/iqM/hqm+KQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.8.0.tgz", + "integrity": "sha512-U1YkZBxDkSLNvPNiqxB5g42IeJHr27C7zDb/yGQN2xL4UBeg4O9xVhCFfe32f6tLwivSL0dar4ScElpaCJuqow==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6526,21 +6510,23 @@ } }, "node_modules/@tiptap/extension-bullet-list": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.7.4.tgz", - "integrity": "sha512-uO08vui6uEgLEgLIYJSLrUb2An3u0If8XRW0Z0kB13zpwQ9pq0S1JOc0KwPTDPeIrgLQ7OOH87/bM9rGUFC3AQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.8.0.tgz", + "integrity": "sha512-H4O2X0ozbc/ce9/XF1H98sqWVUdtt7jzy7hMBunwmY8ZxI4dHtcRkeg81CZbpKTqOqRrMCLWjE3M2tgiDXrDkA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0" + "@tiptap/core": "^2.7.0", + "@tiptap/extension-list-item": "^2.7.0", + "@tiptap/extension-text-style": "^2.7.0" } }, "node_modules/@tiptap/extension-code": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.7.4.tgz", - "integrity": "sha512-GB7gR8tV1fz+70wcSN+hLVm1qET/YmkxIaOfczHEOLLH7Td0C3kyQ5Q+eQ8KN0Ds7NBHFXn3zn051Q8gk9+5tw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.8.0.tgz", + "integrity": "sha512-VSFn3sFF6qPpOGkXFhik8oYRH5iByVJpFEFd/duIEftmS0MdPzkbSItOpN3mc9xsJ5dCX80LYaResSj5hr5zkA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6550,9 +6536,9 @@ } }, "node_modules/@tiptap/extension-code-block": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.7.4.tgz", - "integrity": "sha512-jRKVAEdy3G0SMphWXCTk9SnMuTmJE6blXglU66H89j9R+hG+G0dHfOWhlubhUy6nI2BLy8jJ/isnOzg97iZuQw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.8.0.tgz", + "integrity": "sha512-POuA5Igx+Dto0DTazoBFAQTj/M/FCdkqRVD9Uhsxhv49swPyANTJRr05vgbgtHB+NDDsZfCawVh7pI0IAD/O0w==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6563,9 +6549,9 @@ } }, "node_modules/@tiptap/extension-document": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.7.4.tgz", - "integrity": "sha512-Vsq9e/uW7k/5l1K9bCmuccBSrHhK3i0fbfnTp33G1byTCizheUo3UWFl8MSDammlhRkW/soIZFGdflsj5AJWog==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.8.0.tgz", + "integrity": "sha512-mp7Isx1sVc/ifeW4uW/PexGQ9exN3NRUOebSpnLfqXeWYk4y1RS1PA/3+IHkOPVetbnapgPjFx/DswlCP3XLjA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6575,9 +6561,9 @@ } }, "node_modules/@tiptap/extension-dropcursor": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.7.4.tgz", - "integrity": "sha512-hhE0RTluEEFxfqh8/jpmQRgy5AipTcd+WMK5cBw2zCa9If/qhY0EvysydEPwDU7yDEa13NDqV63x5oN9GKv2pg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.8.0.tgz", + "integrity": "sha512-rAFvx44YuT6dtS1c+ALw0ROAGI16l5L1HxquL4hR1gtxDcTieST5xhw5bkshXlmrlfotZXPrhokzqA7qjhZtJw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6588,9 +6574,9 @@ } }, "node_modules/@tiptap/extension-gapcursor": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.7.4.tgz", - "integrity": "sha512-1HTaCR6kcw5PvUJWEGKQ/Eh2HPXUmN6k1LK0rgJC4CxqiFxNNnPKGED9LcYheJbyMYk0Fz3rtaulxd3ipdIOsQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.8.0.tgz", + "integrity": "sha512-Be1LWCmvteQInOnNVN+HTqc1XWsj1bCl+Q7et8qqNjtGtTaCbdCp8ppcH1SKJxNTM/RLUtPyJ8FDgOTj51ixCA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6601,9 +6587,9 @@ } }, "node_modules/@tiptap/extension-hard-break": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.7.4.tgz", - "integrity": "sha512-ut81vNPQyDYi8LhOzPfFZGnPToYGQbBR6bvFE0e8WY9sRfvUZHr/GvkMjPuWuA8M5sBMqS5cLNyqPrI8h4R7Jg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.8.0.tgz", + "integrity": "sha512-vqiIfviNiCmy/pJTHuDSCAGL2O4QDEdDmAvGJu8oRmElUrnlg8DbJUfKvn6DWQHNSQwRb+LDrwWlzAYj1K9u6A==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6613,9 +6599,9 @@ } }, "node_modules/@tiptap/extension-heading": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.7.4.tgz", - "integrity": "sha512-ZLFHhFvmDD6YKPf4wftZd4wtT510yHjzG90A14wyKCpm0Bq9wOYzx4Q+owvlp5vMwenqHuq3KGz4Sf3w6N5gkw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.8.0.tgz", + "integrity": "sha512-4inWgrTPiqlivPmEHFOM5ck2UsmOsbKKPtqga6bALvWPmCv24S6/EBwFp8Jz4YABabXDnkviihmGu0LpP9D69w==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6625,9 +6611,9 @@ } }, "node_modules/@tiptap/extension-history": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.7.4.tgz", - "integrity": "sha512-xRgGXNrtjDGVOeLeZzGqw4/OtwIoloLU3QLn/qaOggVS7jr1HVTqMHw4nZVcUJfnB/UQ90yl53hBKZ8z3AxcCA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.8.0.tgz", + "integrity": "sha512-u5YS0J5Egsxt8TUWMMAC3QhPZaak+IzQeyHch4gtqxftx96tprItY7AD/A3pGDF2uCSnN+SZrk6yVexm6EncDw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6638,9 +6624,9 @@ } }, "node_modules/@tiptap/extension-horizontal-rule": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.7.4.tgz", - "integrity": "sha512-6mKkiGK9O+eGDeewpUHGyM2Xjlp69Oy+N/0o5zdzfN84YqVPqLV+Y7ub6fMxZUvmRt6L0kuv/ZoDoxeUk+QNKg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.8.0.tgz", + "integrity": "sha512-Sn/MI8WVFBoIYSIHA9NJryJIyCEzZdRysau8pC5TFnfifre0QV1ksPz2bgF+DyCD69ozQiRdBBHDEwKe47ZbfQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6663,9 +6649,9 @@ } }, "node_modules/@tiptap/extension-italic": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.7.4.tgz", - "integrity": "sha512-j/86hNMRd2PbJX6DOs7CbrYgFJSXvZMnWkYRRol7XEELvEuIWoAgyJrW5HkDbVxmGfWPnLlqsoW7iTHml7P+Bg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.8.0.tgz", + "integrity": "sha512-PwwSE2LTYiHI47NJnsfhBmPiLE8IXZYqaSoNPU6flPrk1KxEzqvRI1joKZBmD9wuqzmHJ93VFIeZcC+kfwi8ZA==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6691,9 +6677,9 @@ } }, "node_modules/@tiptap/extension-list-item": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.7.4.tgz", - "integrity": "sha512-2EiXAtkZdCUHCfYRQsslniQhUzvo8zEm+M6JHcsIRBRf27iE+nXrD6jq1WH2ZIUNLDUs4JsJhtc89aoSYkJGKw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.8.0.tgz", + "integrity": "sha512-o7OGymGxB0B9x3x2prp3KBDYFuBYGc5sW69O672jk8G52DqhzzndgPnkk0qUn8nXAUKuDGbJmpmHVA2kagqnRg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6703,21 +6689,23 @@ } }, "node_modules/@tiptap/extension-ordered-list": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.7.4.tgz", - "integrity": "sha512-Y7fnw3lTyOd1h6t5hKSkYqbJXteafIviRdmrQ/ERRayojV934DjRPBeMQnYcArE6nI178/wLI9YMt1HSMJklRw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.8.0.tgz", + "integrity": "sha512-sCvNbcTS1+5QTTXwUPFa10vf5I1pr8sGcOTIh0G+a5ZkS5+6FxT12k7VLzPt39QyNbOi+77U2o4Xr4XyaEkfSg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^2.7.0" + "@tiptap/core": "^2.7.0", + "@tiptap/extension-list-item": "^2.7.0", + "@tiptap/extension-text-style": "^2.7.0" } }, "node_modules/@tiptap/extension-paragraph": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.7.4.tgz", - "integrity": "sha512-Pv3zsyuE+RItlkZVFcjcnz+Omp/UCEO03n9daeHljMUl7Rt775fXtcTNKPqO65f2B2MPBxrSdJpTsoMK0bbcjA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.8.0.tgz", + "integrity": "sha512-XgxxNNbuBF48rAGwv7/s6as92/xjm/lTZIGTq9aG13ClUKFtgdel7C33SpUCcxg3cO2WkEyllXVyKUiauFZw/A==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6740,9 +6728,9 @@ } }, "node_modules/@tiptap/extension-strike": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.7.4.tgz", - "integrity": "sha512-ELMFUCE9MlF0qsGzHJl0AxzGUVyS9rglk6pzidoB0iU1LuzUa/K1el5ID2ksSFdq2+STK17rOWQxUiv3X8C7gw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.8.0.tgz", + "integrity": "sha512-ezkDiXxQ3ME/dDMMM7tAMkKRi6UWw7tIu+Mx7Os0z8HCGpVBk1gFhLlhEd8I5rJaPZr4tK1wtSehMA9bscFGQw==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6825,9 +6813,9 @@ } }, "node_modules/@tiptap/extension-text": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.7.4.tgz", - "integrity": "sha512-1bF9LdfUumqXOz0A6xnOo7UHx+YLshxjMnjoMXjv7cOFOjdHbLmwKNTKGd2ltoCy3bSajoCPhPZL2Id89XDZfQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.8.0.tgz", + "integrity": "sha512-EDAdFFzWOvQfVy7j3qkKhBpOeE5thkJaBemSWfXI93/gMVc0ZCdLi24mDvNNgUHlT+RjlIoQq908jZaaxLKN2A==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -6848,6 +6836,18 @@ "@tiptap/core": "^2.7.0" } }, + "node_modules/@tiptap/extension-text-style": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.8.0.tgz", + "integrity": "sha512-jJp0vcZ2Ty7RvIL0VU6dm1y+fTfXq1lN2GwtYzYM0ueFuESa+Qo8ticYOImyWZ3wGJGVrjn7OV9r0ReW0/NYkQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.7.0" + } + }, "node_modules/@tiptap/extension-underline": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.7.4.tgz", @@ -6861,9 +6861,9 @@ } }, "node_modules/@tiptap/pm": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.7.4.tgz", - "integrity": "sha512-YXjgPLN6/msTkKakuzgBm6Dd/Li3ORtysSki3fHnOFcy8R4c5JZLkYECQk6aJHsxvl/vGvNgaJy5yCDbhnaTAg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.8.0.tgz", + "integrity": "sha512-eMGpRooUMvKz/vOpnKKppApMSoNM325HxTdAJvTlVAmuHp5bOY5kyY1kfUlePRiVx1t1UlFcXs3kecFwkkBD3Q==", "dependencies": { "prosemirror-changeset": "^2.2.1", "prosemirror-collab": "^1.3.1", @@ -6889,6 +6889,37 @@ "url": "https://github.com/sponsors/ueberdosis" } }, + "node_modules/@tiptap/starter-kit": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.8.0.tgz", + "integrity": "sha512-r7UwaTrECkQoheWVZKFDqtL5tBx07x7IFT+prfgnsVlYFutGWskVVqzCDvD3BDmrg5PzeCWYZrQGlPaLib7tjg==", + "dependencies": { + "@tiptap/core": "^2.8.0", + "@tiptap/extension-blockquote": "^2.8.0", + "@tiptap/extension-bold": "^2.8.0", + "@tiptap/extension-bullet-list": "^2.8.0", + "@tiptap/extension-code": "^2.8.0", + "@tiptap/extension-code-block": "^2.8.0", + "@tiptap/extension-document": "^2.8.0", + "@tiptap/extension-dropcursor": "^2.8.0", + "@tiptap/extension-gapcursor": "^2.8.0", + "@tiptap/extension-hard-break": "^2.8.0", + "@tiptap/extension-heading": "^2.8.0", + "@tiptap/extension-history": "^2.8.0", + "@tiptap/extension-horizontal-rule": "^2.8.0", + "@tiptap/extension-italic": "^2.8.0", + "@tiptap/extension-list-item": "^2.8.0", + "@tiptap/extension-ordered-list": "^2.8.0", + "@tiptap/extension-paragraph": "^2.8.0", + "@tiptap/extension-strike": "^2.8.0", + "@tiptap/extension-text": "^2.8.0", + "@tiptap/pm": "^2.8.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", diff --git a/package.json b/package.json index 1c1d071407..5a8122f56e 100644 --- a/package.json +++ b/package.json @@ -195,36 +195,20 @@ }, "dependencies": { "@tiptap/core": "^2.7.4", - "@tiptap/extension-blockquote": "^2.7.4", - "@tiptap/extension-bold": "^2.7.4", - "@tiptap/extension-bullet-list": "^2.7.4", - "@tiptap/extension-code": "^2.7.4", - "@tiptap/extension-code-block": "^2.7.4", - "@tiptap/extension-document": "^2.7.4", - "@tiptap/extension-dropcursor": "^2.7.4", - "@tiptap/extension-gapcursor": "^2.7.4", - "@tiptap/extension-hard-break": "^2.7.4", - "@tiptap/extension-heading": "^2.7.4", - "@tiptap/extension-history": "^2.7.4", - "@tiptap/extension-horizontal-rule": "^2.7.4", "@tiptap/extension-image": "^2.7.4", - "@tiptap/extension-italic": "^2.7.4", "@tiptap/extension-link": "^2.7.4", - "@tiptap/extension-list-item": "^2.7.4", - "@tiptap/extension-ordered-list": "^2.7.4", - "@tiptap/extension-paragraph": "^2.7.4", "@tiptap/extension-placeholder": "^2.7.4", - "@tiptap/extension-strike": "^2.7.4", "@tiptap/extension-subscript": "^2.7.4", "@tiptap/extension-superscript": "^2.7.4", "@tiptap/extension-table": "^2.7.4", "@tiptap/extension-table-cell": "^2.7.4", "@tiptap/extension-table-header": "^2.7.4", "@tiptap/extension-table-row": "^2.7.4", - "@tiptap/extension-text": "^2.7.4", "@tiptap/extension-text-align": "^2.7.4", + "@tiptap/extension-text-style": "^2.8.0", "@tiptap/extension-underline": "^2.7.4", "@tiptap/pm": "^2.7.4", + "@tiptap/starter-kit": "^2.8.0", "@types/diff": "^5.2.1", "@types/dompurify": "^3.0.5", "@types/uuid": "^10.0.0", diff --git a/src/external/tiptap/index.ts b/src/external/tiptap/index.ts index d7daf5502a..16db5db676 100644 --- a/src/external/tiptap/index.ts +++ b/src/external/tiptap/index.ts @@ -1,13 +1,8 @@ // REQUIRED EXTENSIONS export * from '@tiptap/core'; -export { Document } from '@tiptap/extension-document'; -export { Dropcursor } from '@tiptap/extension-dropcursor'; -export { Gapcursor } from '@tiptap/extension-gapcursor'; -export { HardBreak } from '@tiptap/extension-hard-break'; -export { History } from '@tiptap/extension-history'; -export { Paragraph } from '@tiptap/extension-paragraph'; +export { StarterKit } from '@tiptap/starter-kit'; export { Placeholder } from '@tiptap/extension-placeholder'; -export { Text } from '@tiptap/extension-text'; +export { TextStyle } from '@tiptap/extension-text-style'; // OPTIONAL EXTENSIONS export { Blockquote } from '@tiptap/extension-blockquote'; diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index cb1d68b538..631d04fc9e 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -2,17 +2,7 @@ import type { UmbTiptapExtensionApi, UmbTiptapToolbarValue } from '../../extensi import { css, customElement, html, property, state, when } from '@umbraco-cms/backoffice/external/lit'; import { loadManifestApi } from '@umbraco-cms/backoffice/extension-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; -import { - Document, - Dropcursor, - Editor, - Gapcursor, - HardBreak, - History, - Paragraph, - Placeholder, - Text, -} from '@umbraco-cms/backoffice/external/tiptap'; +import { Editor, Placeholder, StarterKit, TextStyle } from '@umbraco-cms/backoffice/external/tiptap'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; @@ -26,12 +16,7 @@ const elementName = 'umb-input-tiptap'; @customElement(elementName) export class UmbInputTiptapElement extends UmbFormControlMixin(UmbLitElement) { readonly #requiredExtensions = [ - Document, - Dropcursor, - Gapcursor, - HardBreak, - History, - Paragraph, + StarterKit, Placeholder.configure({ placeholder: ({ node }) => { if (node.type.name === 'heading') { @@ -41,7 +26,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin('dimensions'); + console.log(this.configuration, dimensions); if (dimensions?.width) this.setAttribute('style', `max-width: ${dimensions.width}px;`); if (dimensions?.height) element.setAttribute('style', `max-height: ${dimensions.height}px;`); diff --git a/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts b/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts deleted file mode 100644 index 8d605978bd..0000000000 --- a/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { UmbTiptapExtensionApiBase } from '../types.js'; -import { Blockquote } from '@umbraco-cms/backoffice/external/tiptap'; - -export default class UmbTiptapBlockquoteExtensionApi extends UmbTiptapExtensionApiBase { - getTiptapExtensions = () => [Blockquote]; -} diff --git a/src/packages/rte/tiptap/extensions/core/bold.extension.ts b/src/packages/rte/tiptap/extensions/core/bold.extension.ts deleted file mode 100644 index 824b62c874..0000000000 --- a/src/packages/rte/tiptap/extensions/core/bold.extension.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { UmbTiptapExtensionApiBase } from '../types.js'; -import { Bold } from '@umbraco-cms/backoffice/external/tiptap'; - -export default class UmbTiptapBoldExtensionApi extends UmbTiptapExtensionApiBase { - getTiptapExtensions = () => [Bold]; -} diff --git a/src/packages/rte/tiptap/extensions/core/code-block.extension.ts b/src/packages/rte/tiptap/extensions/core/code-block.extension.ts deleted file mode 100644 index 2c1310ba3b..0000000000 --- a/src/packages/rte/tiptap/extensions/core/code-block.extension.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { UmbTiptapExtensionApiBase } from '../types.js'; -import { Code, CodeBlock } from '@umbraco-cms/backoffice/external/tiptap'; - -export default class UmbTiptapCodeBlockExtensionApi extends UmbTiptapExtensionApiBase { - getTiptapExtensions = () => [Code, CodeBlock]; -} diff --git a/src/packages/rte/tiptap/extensions/core/heading.extension.ts b/src/packages/rte/tiptap/extensions/core/heading.extension.ts deleted file mode 100644 index 255d8832a9..0000000000 --- a/src/packages/rte/tiptap/extensions/core/heading.extension.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { UmbTiptapExtensionApiBase } from '../types.js'; -import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; - -export default class UmbTiptapHeading1ExtensionApi extends UmbTiptapExtensionApiBase { - getTiptapExtensions = () => [Heading]; -} diff --git a/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts b/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts deleted file mode 100644 index 1d78bc9c96..0000000000 --- a/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { UmbTiptapExtensionApiBase } from '../types.js'; -import { HorizontalRule } from '@umbraco-cms/backoffice/external/tiptap'; - -export default class UmbTiptapHorizontalRuleExtensionApi extends UmbTiptapExtensionApiBase { - getTiptapExtensions = () => [HorizontalRule]; -} diff --git a/src/packages/rte/tiptap/extensions/core/italic.extension.ts b/src/packages/rte/tiptap/extensions/core/italic.extension.ts deleted file mode 100644 index 5eccccf8dd..0000000000 --- a/src/packages/rte/tiptap/extensions/core/italic.extension.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { UmbTiptapExtensionApiBase } from '../types.js'; -import { Italic } from '@umbraco-cms/backoffice/external/tiptap'; - -export default class UmbTiptapItalicExtensionApi extends UmbTiptapExtensionApiBase { - getTiptapExtensions = () => [Italic]; -} diff --git a/src/packages/rte/tiptap/extensions/core/list.extension.ts b/src/packages/rte/tiptap/extensions/core/list.extension.ts deleted file mode 100644 index 1391383388..0000000000 --- a/src/packages/rte/tiptap/extensions/core/list.extension.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { UmbTiptapExtensionApiBase } from '../types.js'; -import { BulletList, ListItem, OrderedList } from '@umbraco-cms/backoffice/external/tiptap'; - -export default class UmbTiptapListExtensionApi extends UmbTiptapExtensionApiBase { - getTiptapExtensions = () => [BulletList, OrderedList, ListItem]; -} diff --git a/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/packages/rte/tiptap/extensions/core/manifests.ts index 88bd91bcf0..12ff89ce56 100644 --- a/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/packages/rte/tiptap/extensions/core/manifests.ts @@ -1,51 +1,11 @@ import type { ManifestTiptapExtension } from '../tiptap-extension.js'; export const manifests: Array = [ - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Blockquote', - name: 'Blockquote Tiptap Extension', - api: () => import('./blockquote.extension.js'), - weight: 995, - meta: { - icon: 'icon-blockquote', - label: 'Blockquote', - group: '#tiptap_extGroup_structure', - }, - }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Bold', - name: 'Bold Tiptap Extension', - api: () => import('./bold.extension.js'), - weight: 999, - meta: { - icon: 'icon-bold', - label: 'Bold', - group: '#tiptap_extGroup_formatting', - }, - }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.CodeBlock', - name: 'Code Block Tiptap Extension', - api: () => import('./code-block.extension.js'), - weight: 994, - meta: { - icon: 'icon-code', - label: 'Code Block', - group: '#tiptap_extGroup_structure', - }, - }, { type: 'tiptapExtension', alias: 'Umb.Tiptap.Embed', name: 'Embed Tiptap Extension', api: () => import('./embedded-media.extension.js'), - weight: 70, meta: { icon: 'icon-embed', label: '#general_embed', @@ -57,7 +17,6 @@ export const manifests: Array = [ alias: 'Umb.Tiptap.Link', name: 'Link Tiptap Extension', api: () => import('./link.extension.js'), - weight: 102, meta: { icon: 'icon-link', label: '#defaultdialogs_urlLinkPicker', @@ -69,26 +28,12 @@ export const manifests: Array = [ alias: 'Umb.Tiptap.Figure', name: 'Figure Tiptap Extension', api: () => import('./figure.extension.js'), - weight: 955, meta: { icon: 'icon-frame', label: 'Figure', group: '#tiptap_extGroup_media', }, }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.HorizontalRule', - name: 'Horizontal Rule Tiptap Extension', - api: () => import('./horizontal-rule.extension.js'), - weight: 991, - meta: { - icon: 'icon-horizontal-rule', - label: 'Horizontal Rule', - group: '#tiptap_extGroup_structure', - }, - }, { type: 'tiptapExtension', alias: 'Umb.Tiptap.Image', @@ -100,39 +45,12 @@ export const manifests: Array = [ group: '#tiptap_extGroup_media', }, }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Italic', - name: 'Italic Tiptap Extension', - api: () => import('./italic.extension.js'), - weight: 998, - meta: { - icon: 'icon-italic', - label: 'Italic', - group: '#tiptap_extGroup_formatting', - }, - }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Strike', - name: 'Strike Tiptap Extension', - api: () => import('./strike.extension.js'), - weight: 998, - meta: { - icon: 'icon-strikethrough', - label: 'Strike', - group: '#tiptap_extGroup_formatting', - }, - }, { type: 'tiptapExtension', kind: 'button', alias: 'Umb.Tiptap.Subscript', name: 'Subscript Tiptap Extension', api: () => import('./subscript.extension.js'), - weight: 1010, meta: { icon: 'icon-subscript', label: 'Subscript', @@ -145,7 +63,6 @@ export const manifests: Array = [ alias: 'Umb.Tiptap.Superscript', name: 'Superscript Tiptap Extension', api: () => import('./superscript.extension.js'), - weight: 1011, meta: { icon: 'icon-superscript', label: 'Superscript', @@ -158,7 +75,6 @@ export const manifests: Array = [ alias: 'Umb.Tiptap.Table', name: 'Table Tiptap Extension', api: () => import('./table.extension.js'), - weight: 909, meta: { icon: 'icon-table', label: 'Table', @@ -171,35 +87,12 @@ export const manifests: Array = [ alias: 'Umb.Tiptap.Underline', name: 'Underline Tiptap Extension', api: () => import('./underline.extension.js'), - weight: 997, meta: { icon: 'icon-underline', label: 'Underline', group: '#tiptap_extGroup_formatting', }, }, - { - type: 'tiptapExtension', - alias: 'Umb.Tiptap.Heading', - name: 'Heading Tiptap Extension', - api: () => import('./heading.extension.js'), - meta: { - icon: 'icon-heading-1', - label: 'Heading', - group: '#tiptap_extGroup_formatting', - }, - }, - { - type: 'tiptapExtension', - alias: 'Umb.Tiptap.List', - name: 'List Tiptap Extension', - api: () => import('./list.extension.js'), - meta: { - icon: 'icon-ordered-list', - label: 'Ordered List', - group: '#tiptap_extGroup_structure', - }, - }, { type: 'tiptapExtension', alias: 'Umb.Tiptap.TextAlign', @@ -208,7 +101,7 @@ export const manifests: Array = [ meta: { icon: 'icon-text-align-justify', label: 'Text Align', - group: '#tiptap_extGroup_structure', + group: '#tiptap_extGroup_formatting', }, }, ]; diff --git a/src/packages/rte/tiptap/extensions/core/strike.extension.ts b/src/packages/rte/tiptap/extensions/core/strike.extension.ts deleted file mode 100644 index 88096bacfe..0000000000 --- a/src/packages/rte/tiptap/extensions/core/strike.extension.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { UmbTiptapExtensionApiBase } from '../types.js'; -import { Strike } from '@umbraco-cms/backoffice/external/tiptap'; - -export default class UmbTiptapStrikeExtensionApi extends UmbTiptapExtensionApiBase { - getTiptapExtensions = () => [Strike]; -} diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index c5daba1fa6..9effd9786d 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -1,10 +1,7 @@ import type { ManifestTiptapExtension } from './tiptap-extension.js'; import { manifests as core } from './core/manifests.js'; import { manifests as toolbar } from './toolbar/manifests.js'; -import type { - ManifestTiptapToolbarExtension, - ManifestTiptapToolbarExtensionButtonKind, -} from './tiptap-toolbar-extension.js'; +import type { ManifestTiptapToolbarExtension } from './tiptap-toolbar-extension.js'; import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; const kinds: Array = [ @@ -19,14 +16,13 @@ const kinds: Array = [ }, ]; -const umbToolbarExtensions: Array = [ +const umbToolbarExtensions: Array = [ { type: 'tiptapToolbarExtension', kind: 'button', alias: 'Umb.Tiptap.Toolbar.CodeEditor', name: 'Code Editor Tiptap Extension', api: () => import('./umb/code-editor.extension.js'), - weight: 1000, meta: { alias: 'umb-code-editor', icon: 'icon-code', @@ -39,7 +35,6 @@ const umbToolbarExtensions: Array import('./umb/link.extension.js'), - weight: 102, meta: { alias: 'umbLink', icon: 'icon-link', @@ -52,7 +47,6 @@ const umbToolbarExtensions: Array import('./umb/mediapicker.extension.js'), - weight: 80, meta: { alias: 'umbMedia', icon: 'icon-picture', @@ -65,7 +59,6 @@ const umbToolbarExtensions: Array import('./umb/embedded-media.extension.js'), - weight: 70, meta: { alias: 'umbEmbeddedMedia', icon: 'icon-embed', @@ -82,7 +75,7 @@ const umbExtensions: Array = [ api: () => import('./umb/media-upload.extension.js'), meta: { icon: 'icon-image-up', - label: 'Media upload', + label: 'Media Upload', group: '#tiptap_extGroup_media', }, }, From c6f784bc733c7021f75b8676e8feb910ad046967 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 17:46:20 +0100 Subject: [PATCH 235/241] Styled loader for Tiptap input component --- .../components/input-tiptap/input-tiptap.element.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 631d04fc9e..786045550d 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -91,7 +91,6 @@ export class UmbInputTiptapElement extends UmbFormControlMixin('dimensions'); - console.log(this.configuration, dimensions); if (dimensions?.width) this.setAttribute('style', `max-width: ${dimensions.width}px;`); if (dimensions?.height) element.setAttribute('style', `max-height: ${dimensions.height}px;`); @@ -120,7 +119,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin html``, + () => html`
    `, () => html` Date: Mon, 30 Sep 2024 18:10:17 +0100 Subject: [PATCH 236/241] Consolidated Tiptap extension manifests "core" is for Tiptap editor native extensions. "toolbar" is custom buttons for the toolbar. Added consistency with the file and class names. Renamed "Code Editor" to "Source Editor", since it's editing the raw HTML source of the RTE, (not "code"). --- .../rte/tiptap/extensions/core/manifests.ts | 107 ----- .../{umb => core}/media-upload.extension.ts | 17 +- .../extensions/core/text-align.extension.ts | 2 +- .../rte/tiptap/extensions/manifests.ts | 443 +++++++++++++++++- .../toolbar/blockquote.extension.ts | 2 +- .../extensions/toolbar/bold.extension.ts | 2 +- .../toolbar/bullet-list.extension.ts | 2 +- .../toolbar/code-block.extension.ts | 2 +- .../embedded-media.extension.ts | 2 +- .../extensions/toolbar/heading1.extension.ts | 2 +- .../extensions/toolbar/heading2.extension.ts | 2 +- .../extensions/toolbar/heading3.extension.ts | 2 +- .../toolbar/horizontal-rule.extension.ts | 2 +- .../extensions/toolbar/italic.extension.ts | 2 +- .../{umb => toolbar}/link.extension.ts | 2 +- .../tiptap/extensions/toolbar/manifests.ts | 296 ------------ .../media-picker.extension.ts} | 13 +- .../toolbar/ordered-list.extension.ts | 2 +- .../extensions/toolbar/redo.extension.ts | 2 +- .../source-editor.extension.ts} | 5 +- .../extensions/toolbar/strike.extension.ts | 2 +- .../style-select.extension.ts | 6 +- .../extensions/toolbar/subscript.extension.ts | 2 +- .../toolbar/superscript.extension.ts | 2 +- .../extensions/toolbar/table.extension.ts | 2 +- .../toolbar/text-align-center.extension.ts | 2 +- .../toolbar/text-align-justify.extension.ts | 2 +- .../toolbar/text-align-left.extension.ts | 2 +- .../toolbar/text-align-right.extension.ts | 2 +- .../extensions/toolbar/underline.extension.ts | 2 +- .../extensions/toolbar/undo.extension.ts | 2 +- .../extensions/toolbar/unlink.extension.ts | 2 +- .../property-editors/tiptap/manifests.ts | 2 +- 33 files changed, 462 insertions(+), 477 deletions(-) delete mode 100644 src/packages/rte/tiptap/extensions/core/manifests.ts rename src/packages/rte/tiptap/extensions/{umb => core}/media-upload.extension.ts (87%) rename src/packages/rte/tiptap/extensions/{umb => toolbar}/embedded-media.extension.ts (92%) rename src/packages/rte/tiptap/extensions/{umb => toolbar}/link.extension.ts (97%) delete mode 100644 src/packages/rte/tiptap/extensions/toolbar/manifests.ts rename src/packages/rte/tiptap/extensions/{umb/mediapicker.extension.ts => toolbar/media-picker.extension.ts} (92%) rename src/packages/rte/tiptap/extensions/{umb/code-editor.extension.ts => toolbar/source-editor.extension.ts} (84%) rename src/packages/rte/tiptap/extensions/{umb => toolbar}/style-select.extension.ts (71%) diff --git a/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/packages/rte/tiptap/extensions/core/manifests.ts deleted file mode 100644 index 12ff89ce56..0000000000 --- a/src/packages/rte/tiptap/extensions/core/manifests.ts +++ /dev/null @@ -1,107 +0,0 @@ -import type { ManifestTiptapExtension } from '../tiptap-extension.js'; - -export const manifests: Array = [ - { - type: 'tiptapExtension', - alias: 'Umb.Tiptap.Embed', - name: 'Embed Tiptap Extension', - api: () => import('./embedded-media.extension.js'), - meta: { - icon: 'icon-embed', - label: '#general_embed', - group: '#tiptap_extGroup_media', - }, - }, - { - type: 'tiptapExtension', - alias: 'Umb.Tiptap.Link', - name: 'Link Tiptap Extension', - api: () => import('./link.extension.js'), - meta: { - icon: 'icon-link', - label: '#defaultdialogs_urlLinkPicker', - group: '#tiptap_extGroup_interactive', - }, - }, - { - type: 'tiptapExtension', - alias: 'Umb.Tiptap.Figure', - name: 'Figure Tiptap Extension', - api: () => import('./figure.extension.js'), - meta: { - icon: 'icon-frame', - label: 'Figure', - group: '#tiptap_extGroup_media', - }, - }, - { - type: 'tiptapExtension', - alias: 'Umb.Tiptap.Image', - name: 'Image Tiptap Extension', - api: () => import('./image.extension.js'), - meta: { - icon: 'icon-picture', - label: 'Image', - group: '#tiptap_extGroup_media', - }, - }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Subscript', - name: 'Subscript Tiptap Extension', - api: () => import('./subscript.extension.js'), - meta: { - icon: 'icon-subscript', - label: 'Subscript', - group: '#tiptap_extGroup_formatting', - }, - }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Superscript', - name: 'Superscript Tiptap Extension', - api: () => import('./superscript.extension.js'), - meta: { - icon: 'icon-superscript', - label: 'Superscript', - group: '#tiptap_extGroup_formatting', - }, - }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Table', - name: 'Table Tiptap Extension', - api: () => import('./table.extension.js'), - meta: { - icon: 'icon-table', - label: 'Table', - group: '#tiptap_extGroup_interactive', - }, - }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.Underline', - name: 'Underline Tiptap Extension', - api: () => import('./underline.extension.js'), - meta: { - icon: 'icon-underline', - label: 'Underline', - group: '#tiptap_extGroup_formatting', - }, - }, - { - type: 'tiptapExtension', - alias: 'Umb.Tiptap.TextAlign', - name: 'Text Align Tiptap Extension', - api: () => import('./text-align.extension.js'), - meta: { - icon: 'icon-text-align-justify', - label: 'Text Align', - group: '#tiptap_extGroup_formatting', - }, - }, -]; diff --git a/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts b/src/packages/rte/tiptap/extensions/core/media-upload.extension.ts similarity index 87% rename from src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts rename to src/packages/rte/tiptap/extensions/core/media-upload.extension.ts index ac8fd6eb4f..1af085137c 100644 --- a/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/media-upload.extension.ts @@ -1,18 +1,17 @@ -import { UmbTiptapExtensionApiBase, type UmbTiptapExtensionArgs } from '../types.js'; -import { - TemporaryFileStatus, - UmbTemporaryFileManager, - type UmbTemporaryFileModel, -} from '@umbraco-cms/backoffice/temporary-file'; +import { UmbTiptapExtensionApiBase } from '../types.js'; +import type { UmbTiptapExtensionArgs } from '../types.js'; import { imageSize } from '@umbraco-cms/backoffice/utils'; -import { type Editor, Extension } from '@umbraco-cms/backoffice/external/tiptap'; +import { Extension } from '@umbraco-cms/backoffice/external/tiptap'; +import { TemporaryFileStatus, UmbTemporaryFileManager } from '@umbraco-cms/backoffice/temporary-file'; import { UmbId } from '@umbraco-cms/backoffice/id'; +import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; +import type { UmbTemporaryFileModel } from '@umbraco-cms/backoffice/temporary-file'; -export default class UmbTiptapMediaUploadExtension extends UmbTiptapExtensionApiBase { +export default class UmbTiptapMediaUploadExtensionApi extends UmbTiptapExtensionApiBase { #configuration?: UmbPropertyEditorConfigCollection; /** diff --git a/src/packages/rte/tiptap/extensions/core/text-align.extension.ts b/src/packages/rte/tiptap/extensions/core/text-align.extension.ts index 3d902dd62a..9855c4d150 100644 --- a/src/packages/rte/tiptap/extensions/core/text-align.extension.ts +++ b/src/packages/rte/tiptap/extensions/core/text-align.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapExtensionApiBase } from '../types.js'; import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapTextAlignCenterExtensionApi extends UmbTiptapExtensionApiBase { +export default class UmbTiptapTextAlignExtensionApi extends UmbTiptapExtensionApiBase { getTiptapExtensions = () => [ TextAlign.configure({ types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index 9effd9786d..200802b943 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -1,6 +1,4 @@ import type { ManifestTiptapExtension } from './tiptap-extension.js'; -import { manifests as core } from './core/manifests.js'; -import { manifests as toolbar } from './toolbar/manifests.js'; import type { ManifestTiptapToolbarExtension } from './tiptap-toolbar-extension.js'; import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; @@ -16,16 +14,425 @@ const kinds: Array = [ }, ]; -const umbToolbarExtensions: Array = [ +const coreExtensions: Array = [ + { + type: 'tiptapExtension', + alias: 'Umb.Tiptap.Embed', + name: 'Embed Tiptap Extension', + api: () => import('./core/embedded-media.extension.js'), + meta: { + icon: 'icon-embed', + label: '#general_embed', + group: '#tiptap_extGroup_media', + }, + }, + { + type: 'tiptapExtension', + alias: 'Umb.Tiptap.Link', + name: 'Link Tiptap Extension', + api: () => import('./core/link.extension.js'), + meta: { + icon: 'icon-link', + label: '#defaultdialogs_urlLinkPicker', + group: '#tiptap_extGroup_interactive', + }, + }, + { + type: 'tiptapExtension', + alias: 'Umb.Tiptap.Figure', + name: 'Figure Tiptap Extension', + api: () => import('./core/figure.extension.js'), + meta: { + icon: 'icon-frame', + label: 'Figure', + group: '#tiptap_extGroup_media', + }, + }, + { + type: 'tiptapExtension', + alias: 'Umb.Tiptap.Image', + name: 'Image Tiptap Extension', + api: () => import('./core/image.extension.js'), + meta: { + icon: 'icon-picture', + label: 'Image', + group: '#tiptap_extGroup_media', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Subscript', + name: 'Subscript Tiptap Extension', + api: () => import('./core/subscript.extension.js'), + meta: { + icon: 'icon-subscript', + label: 'Subscript', + group: '#tiptap_extGroup_formatting', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Superscript', + name: 'Superscript Tiptap Extension', + api: () => import('./core/superscript.extension.js'), + meta: { + icon: 'icon-superscript', + label: 'Superscript', + group: '#tiptap_extGroup_formatting', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Table', + name: 'Table Tiptap Extension', + api: () => import('./core/table.extension.js'), + meta: { + icon: 'icon-table', + label: 'Table', + group: '#tiptap_extGroup_interactive', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Underline', + name: 'Underline Tiptap Extension', + api: () => import('./core/underline.extension.js'), + meta: { + icon: 'icon-underline', + label: 'Underline', + group: '#tiptap_extGroup_formatting', + }, + }, + { + type: 'tiptapExtension', + alias: 'Umb.Tiptap.TextAlign', + name: 'Text Align Tiptap Extension', + api: () => import('./core/text-align.extension.js'), + meta: { + icon: 'icon-text-align-justify', + label: 'Text Align', + group: '#tiptap_extGroup_formatting', + }, + }, + { + type: 'tiptapExtension', + alias: 'Umb.Tiptap.MediaUpload', + name: 'Media Upload Tiptap Extension', + api: () => import('./core/media-upload.extension.js'), + meta: { + icon: 'icon-image-up', + label: 'Media Upload', + group: '#tiptap_extGroup_media', + }, + }, +]; + +const toolbarExtensions: Array = [ + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Blockquote', + name: 'Blockquote Tiptap Extension', + api: () => import('./toolbar/blockquote.extension.js'), + weight: 995, + meta: { + alias: 'blockquote', + icon: 'icon-blockquote', + label: 'Blockquote', + }, + }, { type: 'tiptapToolbarExtension', kind: 'button', - alias: 'Umb.Tiptap.Toolbar.CodeEditor', - name: 'Code Editor Tiptap Extension', - api: () => import('./umb/code-editor.extension.js'), + alias: 'Umb.Tiptap.Toolbar.Bold', + name: 'Bold Tiptap Extension', + api: () => import('./toolbar/bold.extension.js'), + weight: 999, meta: { - alias: 'umb-code-editor', + alias: 'bold', + icon: 'icon-bold', + label: 'Bold', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.CodeBlock', + name: 'Code Block Tiptap Extension', + api: () => import('./toolbar/code-block.extension.js'), + weight: 994, + meta: { + alias: 'codeBlock', icon: 'icon-code', + label: 'Code Block', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.BulletList', + name: 'Bullet List Tiptap Extension', + api: () => import('./toolbar/bullet-list.extension.js'), + weight: 993, + meta: { + alias: 'bulletList', + icon: 'icon-bulleted-list', + label: 'Bullet List', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.OrderedList', + name: 'Ordered List Tiptap Extension', + api: () => import('./toolbar/ordered-list.extension.js'), + weight: 992, + meta: { + alias: 'orderedList', + icon: 'icon-ordered-list', + label: 'Ordered List', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Redo', + name: 'Redo Tiptap Extension', + api: () => import('./toolbar/redo.extension.js'), + element: () => import('../components/toolbar/tiptap-toolbar-button-disabled.element.js'), + weight: 994, + meta: { + alias: 'redo', + icon: 'icon-redo', + label: 'Redo', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Strike', + name: 'Strike Tiptap Extension', + api: () => import('./toolbar/strike.extension.js'), + weight: 996, + meta: { + alias: 'strike', + icon: 'icon-strikethrough', + label: 'Strike', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Subscript', + name: 'Subscript Tiptap Extension', + api: () => import('./toolbar/subscript.extension.js'), + weight: 1010, + meta: { + alias: 'subscript', + icon: 'icon-subscript', + label: 'Subscript', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Superscript', + name: 'Superscript Tiptap Extension', + api: () => import('./toolbar/superscript.extension.js'), + weight: 1011, + meta: { + alias: 'superscript', + icon: 'icon-superscript', + label: 'Superscript', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Table', + name: 'Table Tiptap Extension', + api: () => import('./toolbar/table.extension.js'), + weight: 909, + meta: { + alias: 'table', + icon: 'icon-table', + label: 'Table', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Heading1', + name: 'Heading 1 Tiptap Extension', + api: () => import('./toolbar/heading1.extension.js'), + weight: 949, + meta: { + alias: 'heading1', + icon: 'icon-heading-1', + label: 'Heading 1', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Heading2', + name: 'Heading 2 Tiptap Extension', + api: () => import('./toolbar/heading2.extension.js'), + weight: 948, + meta: { + alias: 'heading2', + icon: 'icon-heading-2', + label: 'Heading 2', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Heading3', + name: 'Heading 3 Tiptap Extension', + api: () => import('./toolbar/heading3.extension.js'), + weight: 947, + meta: { + alias: 'heading3', + icon: 'icon-heading-3', + label: 'Heading 3', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.HorizontalRule', + name: 'Horizontal Rule Tiptap Extension', + api: () => import('./toolbar/horizontal-rule.extension.js'), + weight: 991, + meta: { + alias: 'horizontalRule', + icon: 'icon-horizontal-rule', + label: 'Horizontal Rule', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Italic', + name: 'Italic Tiptap Extension', + api: () => import('./toolbar/italic.extension.js'), + weight: 998, + meta: { + alias: 'italic', + icon: 'icon-italic', + label: 'Italic', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.TextAlignCenter', + name: 'Text Align Center Tiptap Extension', + api: () => import('./toolbar/text-align-center.extension.js'), + weight: 918, + meta: { + alias: 'text-align-center', + icon: 'icon-text-align-center', + label: 'Text Align Center', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.TextAlignJustify', + name: 'Text Align Justify Tiptap Extension', + api: () => import('./toolbar/text-align-justify.extension.js'), + weight: 916, + meta: { + alias: 'text-align-justify', + icon: 'icon-text-align-justify', + label: 'Text Align Justify', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.TextAlignLeft', + name: 'Text Align Left Tiptap Extension', + api: () => import('./toolbar/text-align-left.extension.js'), + weight: 919, + meta: { + alias: 'text-align-left', + icon: 'icon-text-align-left', + label: 'Text Align Left', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.TextAlignRight', + name: 'Text Align Right Tiptap Extension', + api: () => import('./toolbar/text-align-right.extension.js'), + weight: 917, + meta: { + alias: 'text-align-right', + icon: 'icon-text-align-right', + label: 'Text Align Right', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Underline', + name: 'Underline Tiptap Extension', + api: () => import('./toolbar/underline.extension.js'), + weight: 997, + meta: { + alias: 'underline', + icon: 'icon-underline', + label: 'Underline', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Undo', + name: 'Undo Tiptap Extension', + api: () => import('./toolbar/undo.extension.js'), + element: () => import('../components/toolbar/tiptap-toolbar-button-disabled.element.js'), + weight: 994, + meta: { + alias: 'undo', + icon: 'icon-undo', + label: 'Undo', + }, + }, + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.Unlink', + name: 'Unlink Tiptap Extension', + api: () => import('./toolbar/unlink.extension.js'), + element: () => import('../components/toolbar/tiptap-toolbar-button-disabled.element.js'), + weight: 101, + meta: { + alias: 'unlink', + icon: 'icon-unlink', + label: 'Unlink', + }, + }, +]; + +const umbToolbarExtensions: Array = [ + { + type: 'tiptapToolbarExtension', + kind: 'button', + alias: 'Umb.Tiptap.Toolbar.SourceEditor', + name: 'Source Editor Tiptap Extension', + api: () => import('./toolbar/source-editor.extension.js'), + meta: { + alias: 'umbSourceEditor', + icon: 'icon-code-xml', label: '#general_viewSourceCode', }, }, @@ -34,7 +441,7 @@ const umbToolbarExtensions: Array = [ kind: 'button', alias: 'Umb.Tiptap.Toolbar.Link', name: 'Link Tiptap Extension', - api: () => import('./umb/link.extension.js'), + api: () => import('./toolbar/link.extension.js'), meta: { alias: 'umbLink', icon: 'icon-link', @@ -46,7 +453,7 @@ const umbToolbarExtensions: Array = [ kind: 'button', alias: 'Umb.Tiptap.Toolbar.MediaPicker', name: 'Media Picker Tiptap Extension', - api: () => import('./umb/mediapicker.extension.js'), + api: () => import('./toolbar/media-picker.extension.js'), meta: { alias: 'umbMedia', icon: 'icon-picture', @@ -58,7 +465,7 @@ const umbToolbarExtensions: Array = [ kind: 'button', alias: 'Umb.Tiptap.Toolbar.Embed', name: 'Embed Tiptap Extension', - api: () => import('./umb/embedded-media.extension.js'), + api: () => import('./toolbar/embedded-media.extension.js'), meta: { alias: 'umbEmbeddedMedia', icon: 'icon-embed', @@ -67,20 +474,6 @@ const umbToolbarExtensions: Array = [ }, ]; -const umbExtensions: Array = [ - { - type: 'tiptapExtension', - alias: 'Umb.Tiptap.MediaUpload', - name: 'Media Upload Tiptap Extension', - api: () => import('./umb/media-upload.extension.js'), - meta: { - icon: 'icon-image-up', - label: 'Media Upload', - group: '#tiptap_extGroup_media', - }, - }, -]; - -const extensions = [...core, ...toolbar, ...umbToolbarExtensions, ...umbExtensions]; +const extensions = [...coreExtensions, ...toolbarExtensions, ...umbToolbarExtensions]; export const manifests = [...kinds, ...extensions]; diff --git a/src/packages/rte/tiptap/extensions/toolbar/blockquote.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/blockquote.extension.ts index 4dc06019d6..f061e7e6b6 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/blockquote.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/blockquote.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapBlockquoteExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarBlockquoteExtensionApi extends UmbTiptapToolbarElementApiBase { override execute(editor?: Editor) { editor?.chain().focus().toggleBlockquote().run(); } diff --git a/src/packages/rte/tiptap/extensions/toolbar/bold.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/bold.extension.ts index 8ee5950edd..5ec1124963 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/bold.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/bold.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapBoldExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarBoldExtensionApi extends UmbTiptapToolbarElementApiBase { override execute(editor?: Editor) { editor?.chain().focus().toggleBold().run(); } diff --git a/src/packages/rte/tiptap/extensions/toolbar/bullet-list.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/bullet-list.extension.ts index 4212b563e0..983adb0c3d 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/bullet-list.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/bullet-list.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapBulletListExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarBulletListExtensionApi extends UmbTiptapToolbarElementApiBase { override execute(editor?: Editor) { editor?.chain().focus().toggleBulletList().run(); } diff --git a/src/packages/rte/tiptap/extensions/toolbar/code-block.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/code-block.extension.ts index 65273349ae..a3a30b0f5d 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/code-block.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/code-block.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapCodeBlockExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarCodeBlockExtensionApi extends UmbTiptapToolbarElementApiBase { override execute(editor?: Editor) { // editor.chain().focus().toggleCode().run(); editor?.chain().focus().toggleCodeBlock().run(); diff --git a/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/embedded-media.extension.ts similarity index 92% rename from src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts rename to src/packages/rte/tiptap/extensions/toolbar/embedded-media.extension.ts index b439a18101..b50872c1f0 100644 --- a/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/embedded-media.extension.ts @@ -4,7 +4,7 @@ import { UMB_EMBEDDED_MEDIA_MODAL } from '@umbraco-cms/backoffice/embedded-media import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapEmbedExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarEmbeddedMediaExtensionApi extends UmbTiptapToolbarElementApiBase { override isActive = (editor: Editor) => editor.isActive(umbEmbeddedMedia.name) === true; override async execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/toolbar/heading1.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/heading1.extension.ts index 331c0b7136..b301f438e1 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/heading1.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/heading1.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapHeading1ExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarHeading1ExtensionApi extends UmbTiptapToolbarElementApiBase { override isActive(editor?: Editor) { return editor?.isActive('heading', { level: 1 }) === true; } diff --git a/src/packages/rte/tiptap/extensions/toolbar/heading2.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/heading2.extension.ts index cfe4304ef0..663bc09d22 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/heading2.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/heading2.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapHeading2ExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarHeading2ExtensionApi extends UmbTiptapToolbarElementApiBase { override isActive(editor?: Editor) { return editor?.isActive('heading', { level: 2 }) === true; } diff --git a/src/packages/rte/tiptap/extensions/toolbar/heading3.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/heading3.extension.ts index 032c62fbf3..3c3aed7cba 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/heading3.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/heading3.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapHeading3ExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarHeading3ExtensionApi extends UmbTiptapToolbarElementApiBase { override isActive(editor?: Editor) { return editor?.isActive('heading', { level: 3 }) === true; } diff --git a/src/packages/rte/tiptap/extensions/toolbar/horizontal-rule.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/horizontal-rule.extension.ts index 1d7ab6456c..58836d2257 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/horizontal-rule.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/horizontal-rule.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapHorizontalRuleExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarHorizontalRuleExtensionApi extends UmbTiptapToolbarElementApiBase { override execute(editor?: Editor) { editor?.chain().focus().setHorizontalRule().run(); } diff --git a/src/packages/rte/tiptap/extensions/toolbar/italic.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/italic.extension.ts index 6b4e7465c8..2ac2c77649 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/italic.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/italic.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapItalicExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarItalicExtensionApi extends UmbTiptapToolbarElementApiBase { override execute(editor?: Editor) { editor?.chain().focus().toggleItalic().run(); } diff --git a/src/packages/rte/tiptap/extensions/umb/link.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/link.extension.ts similarity index 97% rename from src/packages/rte/tiptap/extensions/umb/link.extension.ts rename to src/packages/rte/tiptap/extensions/toolbar/link.extension.ts index bbf928d420..d78648f6fb 100644 --- a/src/packages/rte/tiptap/extensions/umb/link.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/link.extension.ts @@ -6,7 +6,7 @@ import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbLinkPickerLink } from '@umbraco-cms/backoffice/multi-url-picker'; import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui'; -export default class UmbTiptapLinkExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarLinkExtensionApi extends UmbTiptapToolbarElementApiBase { override async execute(editor?: Editor) { const attrs = editor?.getAttributes(UmbLink.name) ?? {}; const link = this.#getLinkData(attrs); diff --git a/src/packages/rte/tiptap/extensions/toolbar/manifests.ts b/src/packages/rte/tiptap/extensions/toolbar/manifests.ts deleted file mode 100644 index 9c4dfc5068..0000000000 --- a/src/packages/rte/tiptap/extensions/toolbar/manifests.ts +++ /dev/null @@ -1,296 +0,0 @@ -import type { - ManifestTiptapToolbarExtension, - ManifestTiptapToolbarExtensionButtonKind, -} from '../tiptap-toolbar-extension.js'; - -export const manifests: Array = [ - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.Blockquote', - name: 'Blockquote Tiptap Extension', - api: () => import('./blockquote.extension.js'), - weight: 995, - meta: { - alias: 'blockquote', - icon: 'icon-blockquote', - label: 'Blockquote', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.Bold', - name: 'Bold Tiptap Extension', - api: () => import('./bold.extension.js'), - weight: 999, - meta: { - alias: 'bold', - icon: 'icon-bold', - label: 'Bold', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.CodeBlock', - name: 'Code Block Tiptap Extension', - api: () => import('./code-block.extension.js'), - weight: 994, - meta: { - alias: 'codeBlock', - icon: 'icon-code', - label: 'Code Block', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.BulletList', - name: 'Bullet List Tiptap Extension', - api: () => import('./bullet-list.extension.js'), - weight: 993, - meta: { - alias: 'bulletList', - icon: 'icon-bulleted-list', - label: 'Bullet List', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.OrderedList', - name: 'Ordered List Tiptap Extension', - api: () => import('./ordered-list.extension.js'), - weight: 992, - meta: { - alias: 'orderedList', - icon: 'icon-ordered-list', - label: 'Ordered List', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.Redo', - name: 'Redo Tiptap Extension', - api: () => import('./redo.extension.js'), - element: () => import('../../components/toolbar/tiptap-toolbar-button-disabled.element.js'), - weight: 994, - meta: { - alias: 'redo', - icon: 'icon-redo', - label: 'Redo', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.Strike', - name: 'Strike Tiptap Extension', - api: () => import('./strike.extension.js'), - weight: 996, - meta: { - alias: 'strike', - icon: 'icon-strikethrough', - label: 'Strike', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.Subscript', - name: 'Subscript Tiptap Extension', - api: () => import('./subscript.extension.js'), - weight: 1010, - meta: { - alias: 'subscript', - icon: 'icon-subscript', - label: 'Subscript', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.Superscript', - name: 'Superscript Tiptap Extension', - api: () => import('./superscript.extension.js'), - weight: 1011, - meta: { - alias: 'superscript', - icon: 'icon-superscript', - label: 'Superscript', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.Table', - name: 'Table Tiptap Extension', - api: () => import('./table.extension.js'), - weight: 909, - meta: { - alias: 'table', - icon: 'icon-table', - label: 'Table', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.Heading1', - name: 'Heading 1 Tiptap Extension', - api: () => import('./heading1.extension.js'), - weight: 949, - meta: { - alias: 'heading1', - icon: 'icon-heading-1', - label: 'Heading 1', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.Heading2', - name: 'Heading 2 Tiptap Extension', - api: () => import('./heading2.extension.js'), - weight: 948, - meta: { - alias: 'heading2', - icon: 'icon-heading-2', - label: 'Heading 2', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.Heading3', - name: 'Heading 3 Tiptap Extension', - api: () => import('./heading3.extension.js'), - weight: 947, - meta: { - alias: 'heading3', - icon: 'icon-heading-3', - label: 'Heading 3', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.HorizontalRule', - name: 'Horizontal Rule Tiptap Extension', - api: () => import('./horizontal-rule.extension.js'), - weight: 991, - meta: { - alias: 'horizontalRule', - icon: 'icon-horizontal-rule', - label: 'Horizontal Rule', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.Italic', - name: 'Italic Tiptap Extension', - api: () => import('./italic.extension.js'), - weight: 998, - meta: { - alias: 'italic', - icon: 'icon-italic', - label: 'Italic', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.TextAlignCenter', - name: 'Text Align Center Tiptap Extension', - api: () => import('./text-align-center.extension.js'), - weight: 918, - meta: { - alias: 'text-align-center', - icon: 'icon-text-align-center', - label: 'Text Align Center', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.TextAlignJustify', - name: 'Text Align Justify Tiptap Extension', - api: () => import('./text-align-justify.extension.js'), - weight: 916, - meta: { - alias: 'text-align-justify', - icon: 'icon-text-align-justify', - label: 'Text Align Justify', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.TextAlignLeft', - name: 'Text Align Left Tiptap Extension', - api: () => import('./text-align-left.extension.js'), - weight: 919, - meta: { - alias: 'text-align-left', - icon: 'icon-text-align-left', - label: 'Text Align Left', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.TextAlignRight', - name: 'Text Align Right Tiptap Extension', - api: () => import('./text-align-right.extension.js'), - weight: 917, - meta: { - alias: 'text-align-right', - icon: 'icon-text-align-right', - label: 'Text Align Right', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.Underline', - name: 'Underline Tiptap Extension', - api: () => import('./underline.extension.js'), - weight: 997, - meta: { - alias: 'underline', - icon: 'icon-underline', - label: 'Underline', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.Undo', - name: 'Undo Tiptap Extension', - api: () => import('./undo.extension.js'), - element: () => import('../../components/toolbar/tiptap-toolbar-button-disabled.element.js'), - weight: 994, - meta: { - alias: 'undo', - icon: 'icon-undo', - label: 'Undo', - }, - }, - { - type: 'tiptapToolbarExtension', - kind: 'button', - alias: 'Umb.Tiptap.Toolbar.Unlink', - name: 'Unlink Tiptap Extension', - api: () => import('./unlink.extension.js'), - element: () => import('../../components/toolbar/tiptap-toolbar-button-disabled.element.js'), - weight: 101, - meta: { - alias: 'unlink', - icon: 'icon-unlink', - label: 'Unlink', - }, - }, -]; diff --git a/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/media-picker.extension.ts similarity index 92% rename from src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts rename to src/packages/rte/tiptap/extensions/toolbar/media-picker.extension.ts index cbca94f7ff..0ba24357bd 100644 --- a/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/media-picker.extension.ts @@ -1,16 +1,13 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; -import { - UMB_MEDIA_CAPTION_ALT_TEXT_MODAL, - UMB_MEDIA_PICKER_MODAL, - type UmbMediaCaptionAltTextModalValue, -} from '@umbraco-cms/backoffice/media'; +import { getGuidFromUdi, getProcessedImageUrl, imageSize } from '@umbraco-cms/backoffice/utils'; +import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api'; +import { UMB_MEDIA_CAPTION_ALT_TEXT_MODAL, UMB_MEDIA_PICKER_MODAL } from '@umbraco-cms/backoffice/media'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { getGuidFromUdi, getProcessedImageUrl, imageSize } from '@umbraco-cms/backoffice/utils'; -import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api'; +import type { UmbMediaCaptionAltTextModalValue } from '@umbraco-cms/backoffice/media'; -export default class UmbTiptapMediaPickerToolbarExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarMediaPickerToolbarExtensionApi extends UmbTiptapToolbarElementApiBase { #modalManager?: typeof UMB_MODAL_MANAGER_CONTEXT.TYPE; /** diff --git a/src/packages/rte/tiptap/extensions/toolbar/ordered-list.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/ordered-list.extension.ts index 471d794e1c..f95d7da3d2 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/ordered-list.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/ordered-list.extension.ts @@ -2,7 +2,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import { OrderedList, ListItem } from '@umbraco-cms/backoffice/external/tiptap'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapOrderedListExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarOrderedListExtensionApi extends UmbTiptapToolbarElementApiBase { getTiptapExtensions = () => [OrderedList, ListItem]; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/extensions/toolbar/redo.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/redo.extension.ts index 879fc90cec..0aae301bba 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/redo.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/redo.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapRedoExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarRedoExtensionApi extends UmbTiptapToolbarElementApiBase { override isActive(editor: Editor): boolean { return editor.can().redo(); } diff --git a/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/source-editor.extension.ts similarity index 84% rename from src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts rename to src/packages/rte/tiptap/extensions/toolbar/source-editor.extension.ts index 2c3c8c69c8..7ff4fcd4ca 100644 --- a/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/source-editor.extension.ts @@ -1,11 +1,10 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import { UMB_CODE_EDITOR_MODAL } from '@umbraco-cms/backoffice/code-editor'; -import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapCodeEditorExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarSourceEditorExtensionApi extends UmbTiptapToolbarElementApiBase { override async execute(editor?: Editor) { - console.log('umb-code-editor.execute', editor); if (!editor) return; const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); diff --git a/src/packages/rte/tiptap/extensions/toolbar/strike.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/strike.extension.ts index 2f4f284633..1428e2009b 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/strike.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/strike.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapStrikeExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarStrikeExtensionApi extends UmbTiptapToolbarElementApiBase { override execute(editor?: Editor) { editor?.chain().focus().toggleStrike().run(); } diff --git a/src/packages/rte/tiptap/extensions/umb/style-select.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/style-select.extension.ts similarity index 71% rename from src/packages/rte/tiptap/extensions/umb/style-select.extension.ts rename to src/packages/rte/tiptap/extensions/toolbar/style-select.extension.ts index 6429d7021d..96868aa349 100644 --- a/src/packages/rte/tiptap/extensions/umb/style-select.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/style-select.extension.ts @@ -4,7 +4,7 @@ import { customElement, state } from '@umbraco-cms/backoffice/external/lit'; const elementName = 'umb-tiptap-style-select-toolbar-element'; @customElement(elementName) -export class UmbTiptapStyleSelectToolbarElement extends UmbTiptapToolbarDropdownBaseElement { +export class UmbTiptapToolbarStyleSelectToolbarElement extends UmbTiptapToolbarDropdownBaseElement { protected override label = 'Style select'; @state() @@ -15,10 +15,10 @@ export class UmbTiptapStyleSelectToolbarElement extends UmbTiptapToolbarDropdown static override readonly styles = UmbTiptapToolbarDropdownBaseElement.styles; } -export { UmbTiptapStyleSelectToolbarElement as element }; +export { UmbTiptapToolbarStyleSelectToolbarElement as element }; declare global { interface HTMLElementTagNameMap { - [elementName]: UmbTiptapStyleSelectToolbarElement; + [elementName]: UmbTiptapToolbarStyleSelectToolbarElement; } } diff --git a/src/packages/rte/tiptap/extensions/toolbar/subscript.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/subscript.extension.ts index 6753abec31..121e5210a6 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/subscript.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/subscript.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapBoldExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarSubscriptExtensionApi extends UmbTiptapToolbarElementApiBase { override execute(editor?: Editor) { editor?.chain().focus().toggleSubscript().run(); } diff --git a/src/packages/rte/tiptap/extensions/toolbar/superscript.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/superscript.extension.ts index 18bc7a7505..d46e966d29 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/superscript.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/superscript.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapBoldExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarSuperscriptExtensionApi extends UmbTiptapToolbarElementApiBase { override execute(editor?: Editor) { editor?.chain().focus().toggleSuperscript().run(); } diff --git a/src/packages/rte/tiptap/extensions/toolbar/table.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/table.extension.ts index ee997c7280..3ef38f6f13 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/table.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/table.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapTableExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarTableExtensionApi extends UmbTiptapToolbarElementApiBase { override execute(editor?: Editor) { editor?.commands.insertTable({ rows: 3, cols: 3, withHeaderRow: true }); } diff --git a/src/packages/rte/tiptap/extensions/toolbar/text-align-center.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/text-align-center.extension.ts index 50eb9ccaf3..a027bc18c1 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/text-align-center.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/text-align-center.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapTextAlignCenterExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarTextAlignCenterExtensionApi extends UmbTiptapToolbarElementApiBase { override isActive(editor?: Editor) { return editor?.isActive({ textAlign: 'center' }) === true; } diff --git a/src/packages/rte/tiptap/extensions/toolbar/text-align-justify.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/text-align-justify.extension.ts index c7e71ca6a7..38a53fc008 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/text-align-justify.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/text-align-justify.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapTextAlignJustifyExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarTextAlignJustifyExtensionApi extends UmbTiptapToolbarElementApiBase { override isActive(editor?: Editor) { return editor?.isActive({ textAlign: 'justify' }) === true; } diff --git a/src/packages/rte/tiptap/extensions/toolbar/text-align-left.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/text-align-left.extension.ts index 4f3b0f87d6..86b90b7e81 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/text-align-left.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/text-align-left.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapTextAlignLeftExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarTextAlignLeftExtensionApi extends UmbTiptapToolbarElementApiBase { override isActive(editor?: Editor) { return editor?.isActive({ textAlign: 'left' }) === true; } diff --git a/src/packages/rte/tiptap/extensions/toolbar/text-align-right.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/text-align-right.extension.ts index 0cf0313208..8f3984bb5c 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/text-align-right.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/text-align-right.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapTextAlignRightExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarTextAlignRightExtensionApi extends UmbTiptapToolbarElementApiBase { override isActive(editor?: Editor) { return editor?.isActive({ textAlign: 'right' }) === true; } diff --git a/src/packages/rte/tiptap/extensions/toolbar/underline.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/underline.extension.ts index 82436dc55a..9a2aab8b50 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/underline.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/underline.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapUnderlineExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarUnderlineExtensionApi extends UmbTiptapToolbarElementApiBase { override execute(editor?: Editor) { editor?.chain().focus().toggleUnderline().run(); } diff --git a/src/packages/rte/tiptap/extensions/toolbar/undo.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/undo.extension.ts index a206efaa4e..1cc600442b 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/undo.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/undo.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapUndoExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarUndoExtensionApi extends UmbTiptapToolbarElementApiBase { override isActive(editor: Editor): boolean { return editor.can().undo(); } diff --git a/src/packages/rte/tiptap/extensions/toolbar/unlink.extension.ts b/src/packages/rte/tiptap/extensions/toolbar/unlink.extension.ts index fcf5f60c78..a3137f6ec5 100644 --- a/src/packages/rte/tiptap/extensions/toolbar/unlink.extension.ts +++ b/src/packages/rte/tiptap/extensions/toolbar/unlink.extension.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; -export default class UmbTiptapUnlinkExtensionApi extends UmbTiptapToolbarElementApiBase { +export default class UmbTiptapToolbarUnlinkExtensionApi extends UmbTiptapToolbarElementApiBase { override isActive = (editor?: Editor) => editor?.isActive('umbLink') ?? false; override execute(editor?: Editor) { diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index e3adef4605..b5797e180a 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -54,7 +54,7 @@ export const manifests: Array = [ alias: 'toolbar', value: [ [ - ['Umb.Tiptap.Toolbar.CodeEditor'], + ['Umb.Tiptap.Toolbar.SourceEditor'], ['Umb.Tiptap.Toolbar.Bold', 'Umb.Tiptap.Toolbar.Italic', 'Umb.Tiptap.Toolbar.Underline'], [ 'Umb.Tiptap.Toolbar.TextAlignLeft', From d60fb36705975ecba1429b82c13477ffbe98dfb4 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 18:10:35 +0100 Subject: [PATCH 237/241] Localize selected toolbar button labels --- .../property-editor-ui-tiptap-toolbar-configuration.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 19d3a5d9a4..079bb9ea49 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -220,7 +220,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement if (!extension) return nothing; return html`
    Date: Mon, 30 Sep 2024 18:11:28 +0100 Subject: [PATCH 238/241] Added "code-xml" icon for Tiptap's "Source Editor" toolbar button Previously it was using "icon-code", which could be confused with the "Code Block" action. --- .../core/icon-registry/icon-dictionary.json | 4 ++++ src/packages/core/icon-registry/icons.ts | 4 ++++ .../core/icon-registry/icons/icon-code-xml.ts | 16 ++++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 src/packages/core/icon-registry/icons/icon-code-xml.ts diff --git a/src/packages/core/icon-registry/icon-dictionary.json b/src/packages/core/icon-registry/icon-dictionary.json index 4a097fd118..a249ba32dc 100644 --- a/src/packages/core/icon-registry/icon-dictionary.json +++ b/src/packages/core/icon-registry/icon-dictionary.json @@ -399,6 +399,10 @@ "name": "icon-code", "file": "code.svg" }, + { + "name": "icon-code-xml", + "file": "code-xml.svg" + }, { "name": "icon-coffee", "file": "coffee.svg" diff --git a/src/packages/core/icon-registry/icons.ts b/src/packages/core/icon-registry/icons.ts index 9849a23c35..a153ffeff8 100644 --- a/src/packages/core/icon-registry/icons.ts +++ b/src/packages/core/icon-registry/icons.ts @@ -387,6 +387,10 @@ name: "icon-code", path: () => import("./icons/icon-code.js"), },{ +name: "icon-code-xml", + +path: () => import("./icons/icon-code-xml.js"), +},{ name: "icon-coffee", path: () => import("./icons/icon-coffee.js"), diff --git a/src/packages/core/icon-registry/icons/icon-code-xml.ts b/src/packages/core/icon-registry/icons/icon-code-xml.ts new file mode 100644 index 0000000000..22e1bb6911 --- /dev/null +++ b/src/packages/core/icon-registry/icons/icon-code-xml.ts @@ -0,0 +1,16 @@ +export default ` + + + + + +`; \ No newline at end of file From 27550986e56d20da02937a5173f46dd8337d614f Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 18:20:32 +0100 Subject: [PATCH 239/241] Moved the Tiptap config editors to a "components" folder, to be under "tiptap" folder. --- .../rte/tiptap/property-editors/manifests.ts | 26 +------------------ ...tiptap-extensions-configuration.element.ts | 5 +++- ...ui-tiptap-toolbar-configuration.element.ts | 2 +- .../property-editors/tiptap/manifests.ts | 22 ++++++++++++++++ 4 files changed, 28 insertions(+), 27 deletions(-) rename src/packages/rte/tiptap/property-editors/{ => tiptap/components}/property-editor-ui-tiptap-extensions-configuration.element.ts (97%) rename src/packages/rte/tiptap/property-editors/{ => tiptap/components}/property-editor-ui-tiptap-toolbar-configuration.element.ts (99%) diff --git a/src/packages/rte/tiptap/property-editors/manifests.ts b/src/packages/rte/tiptap/property-editors/manifests.ts index 9f9f7706f4..734619f0cf 100644 --- a/src/packages/rte/tiptap/property-editors/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/manifests.ts @@ -2,28 +2,4 @@ import { manifests as tiptapManifests } from './tiptap/manifests.js'; import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/property-editor'; -export const manifests: Array = [ - ...tiptapManifests, - { - type: 'propertyEditorUi', - alias: 'Umb.PropertyEditorUi.Tiptap.ToolbarConfiguration', - name: 'Tiptap Toolbar Property Editor UI', - js: () => import('./property-editor-ui-tiptap-toolbar-configuration.element.js'), - meta: { - label: 'Tiptap Toolbar Configuration', - icon: 'icon-autofill', - group: 'common', - }, - }, - { - type: 'propertyEditorUi', - alias: 'Umb.PropertyEditorUi.Tiptap.ExtensionsConfiguration', - name: 'Tiptap Extensions Property Editor UI', - js: () => import('./property-editor-ui-tiptap-extensions-configuration.element.js'), - meta: { - label: 'Tiptap Extensions Configuration', - icon: 'icon-autofill', - group: 'common', - }, - }, -]; +export const manifests: Array = [...tiptapManifests]; diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/components/property-editor-ui-tiptap-extensions-configuration.element.ts similarity index 97% rename from src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts rename to src/packages/rte/tiptap/property-editors/tiptap/components/property-editor-ui-tiptap-extensions-configuration.element.ts index 583cda4c56..d020ec400d 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/components/property-editor-ui-tiptap-extensions-configuration.element.ts @@ -83,7 +83,10 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error - const grouped = Object.groupBy(withSelectedProperty, (item: UmbTiptapExtensionConfig) => item.group || 'Uncategorized'); + const grouped = Object.groupBy( + withSelectedProperty, + (item: UmbTiptapExtensionConfig) => item.group || 'Uncategorized', + ); this._extensionCategories = Object.keys(grouped) .sort((a, b) => a.localeCompare(b)) diff --git a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/packages/rte/tiptap/property-editors/tiptap/components/property-editor-ui-tiptap-toolbar-configuration.element.ts similarity index 99% rename from src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts rename to src/packages/rte/tiptap/property-editors/tiptap/components/property-editor-ui-tiptap-toolbar-configuration.element.ts index 079bb9ea49..4453ff1146 100644 --- a/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/components/property-editor-ui-tiptap-toolbar-configuration.element.ts @@ -1,4 +1,4 @@ -import type { UmbTiptapToolbarValue } from '../extensions/types.js'; +import type { UmbTiptapToolbarValue } from '../../../extensions/types.js'; import { customElement, css, html, property, state, repeat, nothing } from '@umbraco-cms/backoffice/external/lit'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index b5797e180a..85751a607f 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -74,4 +74,26 @@ export const manifests: Array = [ }, }, }, + { + type: 'propertyEditorUi', + alias: 'Umb.PropertyEditorUi.Tiptap.ToolbarConfiguration', + name: 'Tiptap Toolbar Property Editor UI', + js: () => import('./components/property-editor-ui-tiptap-toolbar-configuration.element.js'), + meta: { + label: 'Tiptap Toolbar Configuration', + icon: 'icon-autofill', + group: 'common', + }, + }, + { + type: 'propertyEditorUi', + alias: 'Umb.PropertyEditorUi.Tiptap.ExtensionsConfiguration', + name: 'Tiptap Extensions Property Editor UI', + js: () => import('./components/property-editor-ui-tiptap-extensions-configuration.element.js'), + meta: { + label: 'Tiptap Extensions Configuration', + icon: 'icon-autofill', + group: 'common', + }, + }, ]; From e41c1df7091c6b5be12e561d23e21d201c5aad2f Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 18:20:48 +0100 Subject: [PATCH 240/241] Corrected "Umb.Tiptap.Toolbar.EmbeddedMedia" manifest alias --- src/packages/rte/tiptap/extensions/manifests.ts | 4 ++-- src/packages/rte/tiptap/property-editors/tiptap/manifests.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/packages/rte/tiptap/extensions/manifests.ts b/src/packages/rte/tiptap/extensions/manifests.ts index 200802b943..ed8f9f7f56 100644 --- a/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/packages/rte/tiptap/extensions/manifests.ts @@ -463,8 +463,8 @@ const umbToolbarExtensions: Array = [ { type: 'tiptapToolbarExtension', kind: 'button', - alias: 'Umb.Tiptap.Toolbar.Embed', - name: 'Embed Tiptap Extension', + alias: 'Umb.Tiptap.Toolbar.EmbeddedMedia', + name: 'Embedded Media Tiptap Extension', api: () => import('./toolbar/embedded-media.extension.js'), meta: { alias: 'umbEmbeddedMedia', diff --git a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 85751a607f..a2791c4935 100644 --- a/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts @@ -64,7 +64,7 @@ export const manifests: Array = [ ['Umb.Tiptap.Toolbar.BulletList', 'Umb.Tiptap.Toolbar.OrderedList'], ['Umb.Tiptap.Toolbar.Blockquote', 'Umb.Tiptap.Toolbar.HorizontalRule'], ['Umb.Tiptap.Toolbar.Link', 'Umb.Tiptap.Toolbar.Unlink'], - ['Umb.Tiptap.Toolbar.MediaPicker', 'Umb.Tiptap.Toolbar.Embed'], + ['Umb.Tiptap.Toolbar.MediaPicker', 'Umb.Tiptap.Toolbar.EmbeddedMedia'], ], ], }, From 466435f452c67475eb315c194bdb39c593c916ea Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 30 Sep 2024 18:38:18 +0100 Subject: [PATCH 241/241] Fixed several @sonarcloud issues --- ...ui-block-rte-type-configuration.element.ts | 19 ++++---- ...y-mce-stylesheets-configuration.element.ts | 8 ++-- ...-tiny-mce-toolbar-configuration.element.ts | 18 ++++---- .../input-tiptap/input-tiptap.element.ts | 9 ++-- ...ui-tiptap-toolbar-configuration.element.ts | 44 +++++++++---------- 5 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts b/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts index 873121f6dc..a830297e36 100644 --- a/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts +++ b/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts @@ -1,14 +1,15 @@ -import { type UmbBlockTypeBaseModel, UmbInputBlockTypeElement } from '@umbraco-cms/backoffice/block-type'; -import { UMB_BLOCK_RTE_TYPE } from '@umbraco-cms/backoffice/block-rte'; -import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/property-editor'; -import { html, customElement, property, state, nothing } from '@umbraco-cms/backoffice/external/lit'; -import { - UmbPropertyValueChangeEvent, - type UmbPropertyEditorConfigCollection, -} from '@umbraco-cms/backoffice/property-editor'; +import { customElement, html, property, state, nothing } from '@umbraco-cms/backoffice/external/lit'; +import { UmbInputBlockTypeElement } from '@umbraco-cms/backoffice/block-type'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/workspace'; import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; +import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; +import { UMB_BLOCK_RTE_TYPE } from '@umbraco-cms/backoffice/block-rte'; +import { UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/workspace'; +import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type'; +import type { + UmbPropertyEditorUiElement, + UmbPropertyEditorConfigCollection, +} from '@umbraco-cms/backoffice/property-editor'; /** * @element umb-property-editor-ui-block-rte-type-configuration diff --git a/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts b/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts index 98f4510c25..a0efa34c22 100644 --- a/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts +++ b/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts @@ -1,9 +1,11 @@ -import { UmbServerFilePathUniqueSerializer } from '@umbraco-cms/backoffice/server-file-system'; import { customElement, html, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; -import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/property-editor'; +import { UmbServerFilePathUniqueSerializer } from '@umbraco-cms/backoffice/server-file-system'; +import type { + UmbPropertyEditorConfigCollection, + UmbPropertyEditorUiElement, +} from '@umbraco-cms/backoffice/property-editor'; import type { UmbStylesheetInputElement } from '@umbraco-cms/backoffice/stylesheet'; /** diff --git a/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts b/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts index a7d2208b1b..9559126d33 100644 --- a/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts +++ b/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts @@ -1,15 +1,15 @@ +import { css, customElement, html, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; +import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs'; +import { tinymce } from '@umbraco-cms/backoffice/external/tinymce'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; -import { customElement, css, html, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/property-editor'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; -import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs'; -import { - UmbPropertyValueChangeEvent, - type UmbPropertyEditorConfigCollection, +import type { + UmbPropertyEditorUiElement, + UmbPropertyEditorConfigCollection, } from '@umbraco-cms/backoffice/property-editor'; -import { tinymce } from '@umbraco-cms/backoffice/external/tinymce'; const tinyIconSet = tinymce.IconManager.get('default'); diff --git a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 786045550d..dee7a0609d 100644 --- a/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -30,7 +30,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin = []; + private readonly _extensions: Array = []; @property({ type: String }) override set value(value: string) { @@ -312,9 +312,10 @@ export class UmbInputTiptapElement extends UmbFormControlMixin = new Set(); + readonly #inUse: Set = new Set(); #currentDragItem?: { alias: string; @@ -55,17 +55,17 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement }); } - #onDragStart = (event: DragEvent, alias: string, fromPos?: [number, number, number]) => { + #onDragStart(event: DragEvent, alias: string, fromPos?: [number, number, number]) { event.dataTransfer!.effectAllowed = 'move'; this.#currentDragItem = { alias, fromPos }; - }; + } - #onDragOver = (event: DragEvent) => { + #onDragOver(event: DragEvent) { event.preventDefault(); event.dataTransfer!.dropEffect = 'move'; - }; + } - #onDragEnd = (event: DragEvent) => { + #onDragEnd(event: DragEvent) { event.preventDefault(); if (event.dataTransfer?.dropEffect === 'none') { const { fromPos } = this.#currentDragItem ?? {}; @@ -73,9 +73,9 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement this.#removeItem(fromPos); } - }; + } - #onDrop = (event: DragEvent, toPos?: [number, number, number]) => { + #onDrop(event: DragEvent, toPos?: [number, number, number]) { event.preventDefault(); const { alias, fromPos } = this.#currentDragItem ?? {}; @@ -93,9 +93,9 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement if (alias && toPos) { this.#insertItem(alias, toPos); } - }; + } - #moveItem = (from: [number, number, number], to: [number, number, number]) => { + #moveItem(from: [number, number, number], to: [number, number, number]) { const [rowIndex, groupIndex, itemIndex] = from; // Get the item to move from the 'from' position @@ -105,9 +105,9 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement this.#value[rowIndex][groupIndex].splice(itemIndex, 1); this.#insertItem(itemToMove, to); - }; + } - #insertItem = (alias: string, toPos: [number, number, number]) => { + #insertItem(alias: string, toPos: [number, number, number]) { const [rowIndex, groupIndex, itemIndex] = toPos; // Insert the item into the new position @@ -115,7 +115,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement inserted.forEach((alias) => this.#inUse.add(alias)); this.dispatchEvent(new UmbPropertyValueChangeEvent()); - }; + } #removeItem(from: [number, number, number]) { const [rowIndex, groupIndex, itemIndex] = from; @@ -126,12 +126,12 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement this.dispatchEvent(new UmbPropertyValueChangeEvent()); } - #addGroup = (rowIndex: number, groupIndex: number) => { + #addGroup(rowIndex: number, groupIndex: number) { this.#value[rowIndex].splice(groupIndex, 0, []); this.dispatchEvent(new UmbPropertyValueChangeEvent()); - }; + } - #removeGroup = (rowIndex: number, groupIndex: number) => { + #removeGroup(rowIndex: number, groupIndex: number) { if (this.#value[rowIndex].length > groupIndex) { const removed = this.#value[rowIndex].splice(groupIndex, 1); removed.forEach((group) => group.forEach((alias) => this.#inUse.delete(alias))); @@ -143,14 +143,14 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement } this.dispatchEvent(new UmbPropertyValueChangeEvent()); - }; + } - #addRow = (rowIndex: number) => { + #addRow(rowIndex: number) { this.#value.splice(rowIndex, 0, [[]]); this.dispatchEvent(new UmbPropertyValueChangeEvent()); - }; + } - #removeRow = (rowIndex: number) => { + #removeRow(rowIndex: number) { if (this.#value.length > rowIndex) { const removed = this.#value.splice(rowIndex, 1); removed.forEach((row) => row.forEach((group) => group.forEach((alias) => this.#inUse.delete(alias)))); @@ -162,7 +162,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement } this.dispatchEvent(new UmbPropertyValueChangeEvent()); - }; + } override render() { return html` @@ -250,7 +250,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement `; } - static override styles = [ + static override readonly styles = [ UmbTextStyles, css` :host {