From 6ff93d7721da7fa542b17ca84e1d4afc668185b5 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 --- src/Umbraco.Web.UI.Client/package-lock.json | 521 +++++++++++++++++++- src/Umbraco.Web.UI.Client/package.json | 3 + 2 files changed, 514 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 826baef473..cbc477b3b5 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 0d7af660b1..911ef8621f 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/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 1c54cbeb5d77757870479da2889d001e0f31c724 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 + src/Umbraco.Web.UI.Client/tsconfig.json | 1 + 13 files changed, 478 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiptap/components/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiptap/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiptap/umbraco-package.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiptap/vite.config.ts diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts index d59e98126c..42c5f423d6 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts new file mode 100644 index 0000000000..1d5fb7fe1e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts @@ -0,0 +1,2 @@ +export * from '@tiptap/core'; +export * from '@tiptap/starter-kit'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/index.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/index.ts new file mode 100644 index 0000000000..d7e1544b7d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/index.ts @@ -0,0 +1 @@ +export * from './input-tiptap/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/index.ts new file mode 100644 index 0000000000..f11b53c7d9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/index.ts @@ -0,0 +1 @@ +export * from './input-tiptap.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts new file mode 100644 index 0000000000..1932b8d77e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts new file mode 100644 index 0000000000..28a3a2185f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/manifests.ts new file mode 100644 index 0000000000..1dcd948374 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/manifests.ts new file mode 100644 index 0000000000..1a38fe7fe9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/manifests.ts new file mode 100644 index 0000000000..ba824b19d6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts new file mode 100644 index 0000000000..27390b2a0d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/umbraco-package.ts new file mode 100644 index 0000000000..79a0937305 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/vite.config.ts new file mode 100644 index 0000000000..7e6c0e3f77 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index 13058d558f..23ba90be60 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/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 2f5da3974ba4b7240c6c04429fe51038ea4aa690 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/Umbraco.Web.UI.Client/src/packages/tiptap/package.json diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/package.json b/src/Umbraco.Web.UI.Client/src/packages/tiptap/package.json new file mode 100644 index 0000000000..00d278e4d9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/package.json @@ -0,0 +1,8 @@ +{ + "name": "@umbraco-backoffice/tiptap", + "private": true, + "type": "module", + "scripts": { + "build": "vite build" + } +} From ebfd8159cb1834c2c1f8ab6e921d7ef7242c7bc6 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/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/Umbraco.Tiptap.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/Umbraco.Tiptap.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/Umbraco.Tiptap.ts new file mode 100644 index 0000000000..7837aea024 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 1cb7440efe6036bd889f028f12dfd4ed76d7a659 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/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/Umbraco.Tiptap.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/Umbraco.Tiptap.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/Umbraco.Tiptap.ts deleted file mode 100644 index 7837aea024..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/manifests.ts index ba824b19d6..140d8e4b96 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 688b83367d127fab8b272f23414b47e078203b88 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/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts index 1932b8d77e..2c6bfc01bc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index 27390b2a0d..c1683214d7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 088b02ae8c51a74323a157dc62be358ae467f3bf 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/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index c1683214d7..23fd1fbfb1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 d54940643d28eae607aac390f2fca0a491f8fc53 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 --- src/Umbraco.Web.UI.Client/package-lock.json | 26 +++++++++++++++++++++ src/Umbraco.Web.UI.Client/package.json | 2 ++ 2 files changed, 28 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index cbc477b3b5..2707efe191 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 911ef8621f..b63b9a48a1 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/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 be768c4e084391ba929f5fa44e0ec3b0ec61c211 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/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/icons.ts diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts index 1d5fb7fe1e..42ab4c52b1 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/icons.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/icons.ts new file mode 100644 index 0000000000..1d6e9544aa --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts index 2c6bfc01bc..60e01af1c8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 28a3a2185f..8dd8d78f6a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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 cdacf11d0ef9a0c8dc1f061f90c23bc562d2ce59 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 --- src/Umbraco.Web.UI.Client/package-lock.json | 22 +++++++++++++++++++++ src/Umbraco.Web.UI.Client/package.json | 1 + 2 files changed, 23 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 2707efe191..1f042effe8 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index b63b9a48a1..321787d1bd 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/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 a8c69371fc17dbfbe4a697c1ce1809788886d1e1 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/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts index 42ab4c52b1..9eae18bcdd 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/icons.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/icons.ts index 1d6e9544aa..2676404411 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/icons.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts index 60e01af1c8..209fb1836d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 8dd8d78f6a..e34eb1445f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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 2d6c358a98f9c36eb99090a7012486d5821fdbfa 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 | 36 +++++++++--- .../input-tiptap/tiptap-hover-menu.element.ts | 55 +++++++++++++++++++ 3 files changed, 87 insertions(+), 20 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts index 209fb1836d..0b632b56d4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index e34eb1445f..a63fb72121 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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; - - 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'); + 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; + + #onUpdate = () => { + this.requestUpdate(); + }; override render() { return html` diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts new file mode 100644 index 0000000000..6dfc91ac56 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 ce8d6225ef24a39366ae027c59ef6c8ea64544bf 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/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts index 0b632b56d4..1615081f67 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index a63fb72121..598b787211 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts index 6dfc91ac56..a280a832ed 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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 250bc846e1a02724ab16d094f7ec8612b1be2d47 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 --- src/Umbraco.Web.UI.Client/package.json | 1 + .../src/apps/backoffice/backoffice.element.ts | 2 +- src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts rename src/Umbraco.Web.UI.Client/src/packages/{tiptap => rte}/package.json (67%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiptap/components/index.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiptap/components/input-tiptap/icons.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiptap/components/input-tiptap/index.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiptap/components/input-tiptap/input-tiptap.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiptap/manifests.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiptap/property-editors/manifests.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiptap/property-editors/tiptap/manifests.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts (100%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/umbraco-package.ts rename src/Umbraco.Web.UI.Client/src/packages/{tiptap => rte}/vite.config.ts (84%) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiptap/umbraco-package.ts diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 321787d1bd..167886f918 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts index 42c5f423d6..21a07f340f 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts new file mode 100644 index 0000000000..f4d7d8f1ff --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/package.json b/src/Umbraco.Web.UI.Client/src/packages/rte/package.json similarity index 67% rename from src/Umbraco.Web.UI.Client/src/packages/tiptap/package.json rename to src/Umbraco.Web.UI.Client/src/packages/rte/package.json index 00d278e4d9..20394e7e36 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/package.json +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/components/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiptap/components/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/icons.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icons.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/icons.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icons.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/manifests.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiptap/manifests.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/manifests.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/manifests.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/umbraco-package.ts new file mode 100644 index 0000000000..68eece9518 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/vite.config.ts similarity index 84% rename from src/Umbraco.Web.UI.Client/src/packages/tiptap/vite.config.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/vite.config.ts index 7e6c0e3f77..1385afcf23 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/vite.config.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiptap/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/umbraco-package.ts deleted file mode 100644 index 79a0937305..0000000000 --- a/src/Umbraco.Web.UI.Client/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 844a342eda1d525cb0bf27789b1666a5c5155ff4 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 --- src/Umbraco.Web.UI.Client/package.json | 1 + src/Umbraco.Web.UI.Client/tsconfig.json | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 167886f918..e8c1739816 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index 23ba90be60..e43e8f36d1 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/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 c3b99d360fa5622ece338a25f7151857ced34bb4 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 --- src/Umbraco.Web.UI.Client/package-lock.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index a2d1864d46..b4f40bd654 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/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 1273d11a431cb6ac29a4adff7298d38eea7ba87d 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 --- .../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/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts index f3c0ab8d4d..eec1070db2 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts index 1011e3c467..4fef647c2e 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts index 2e3dce12d4..c8eb2ab5f0 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts +++ b/src/Umbraco.Web.UI.Client/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 9ca38487efef6cde2dfa2461aa32a5626abffcfe 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/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts index c8eb2ab5f0..09ec5524e0 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts +++ b/src/Umbraco.Web.UI.Client/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 9cab2e4e8ec789459293b28f805bce05a530f3ff 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/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts index d40071b228..8cb80938c6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts +++ b/src/Umbraco.Web.UI.Client/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 801f4c66272dafb185ce4a825a8e12be001b7cf8 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 1615081f67..10d3ee9432 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index 23fd1fbfb1..7ab482cb77 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 0b476a1e88669a02fbe1d9535d043c5c87cefec3 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/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tiptap-extension.model.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts index 00fec52d8f..d79b3f9e4a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tiptap-extension.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tiptap-extension.model.ts new file mode 100644 index 0000000000..5d9ff73dfa --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts index f4d7d8f1ff..1a38fe7fe9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/index.ts index f11b53c7d9..0d9456b123 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-extension.ts new file mode 100644 index 0000000000..aa01ea3420 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/index.ts new file mode 100644 index 0000000000..8a8c2711ca --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/index.ts @@ -0,0 +1 @@ +export * from './components/index.js'; From a42068607ba18b359592a29ced472b5c9e9e9bd2 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts new file mode 100644 index 0000000000..6780ce1c09 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts new file mode 100644 index 0000000000..be14701cef --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/manifests.ts index 1dcd948374..8e6836b5cc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 37ec15f440bdebfd7cc3a343d09b501e981db5f0 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 10d3ee9432..87e7b4e387 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 firstUpdated(): void { - const editor = this.shadowRoot?.querySelector('#editor'); + protected override async firstUpdated() { + await Promise.all([await this.#loadExtensions(), await this.#loadEditor()]); + } - if (!editor) return; + async #loadExtensions() { + 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); + 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 a8be452caa602e8017d27d434bbe8d8b551df066 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 598b787211..ab47a21d1a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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 df1a3a9bdc2c5c9b3d78e9da2d69a77929605428 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 140d8e4b96..ada8881892 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 6a10f62ffd9e89387727ba306829d36016239608 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 10d3ee9432..9c8ec93f70 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 140d8e4b96..d3bdd9e2db 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 29dce93a5d5b29adb66dbfe5a0cfa3ac86ada5e7 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts index 1a38fe7fe9..b9b660577c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/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 45386956b9c0a00e95709c373e5e2ebf2ad07a3b 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index ab47a21d1a..da000da655 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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 6cb7964507d06b5370b520cec95b446c24bf3d51 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 + src/Umbraco.Web.UI.Client/package-lock.json | 235 +++--------------- src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/.vscode/settings.json b/src/Umbraco.Web.UI.Client/.vscode/settings.json index f4ca096129..bc10a8c132 100644 --- a/src/Umbraco.Web.UI.Client/.vscode/settings.json +++ b/src/Umbraco.Web.UI.Client/.vscode/settings.json @@ -6,7 +6,9 @@ "combobox", "ctrls", "devs", + "Dropcursor", "Elementable", + "Gapcursor", "iframes", "invariantable", "lucide", diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 20b45d5a49..fdd671ea90 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/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==", + "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/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" + "@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==", + "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" - } - }, - "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" + "@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/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 4034bcc87c..ccc67189dd 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts index 9eae18bcdd..fcd28e8cf6 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 87e7b4e387..c766a512ef 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 1a495893301f637898e0a533e0c0ff8e5858f518 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 --- src/Umbraco.Web.UI.Client/package-lock.json | 145 ++++++++++++++++++ src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index fdd671ea90..40a4bafd3d 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index ccc67189dd..3b6ffcf294 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts index fcd28e8cf6..1e3e36e59d 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index c766a512ef..aa453dd069 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 4309445b76a1f0b75cda0d48077b08b41c756ce9 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 --- src/Umbraco.Web.UI.Client/package-lock.json | 13 +++++++++++++ src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 40a4bafd3d..df7c63d807 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 3b6ffcf294..310f0d869a 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts index 1e3e36e59d..e050542465 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index aa453dd069..0540719aac 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 01dc79c8c15921d61cdcd199e5784c32b248cb79 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/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tiptap-extension.model.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/index.ts rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/{components/input-tiptap => extensions}/tiptap-extension.ts (71%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts index d79b3f9e4a..00fec52d8f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tiptap-extension.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/tiptap-extension.model.ts deleted file mode 100644 index 5d9ff73dfa..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/index.ts index 0d9456b123..f11b53c7d9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 0540719aac..891d8ef366 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index ab47a21d1a..1f0fe4c463 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/index.ts new file mode 100644 index 0000000000..6ff82ee0b6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/index.ts @@ -0,0 +1 @@ +export * from './tiptap-extension.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index 6780ce1c09..4de8b54d84 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts similarity index 71% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts index aa01ea3420..b35de22127 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts index be14701cef..1b98dbb40f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/index.ts index 8a8c2711ca..f0f1ade33d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/index.ts @@ -1 +1,2 @@ export * from './components/index.js'; +export * from './extensions/index.js'; From bab94a015215a70a458447b1f8baf8c60a14ee7c 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 +- .../packages/rte/tiptap/extensions/index.ts | 3 ++- .../rte/tiptap/extensions/tiptap-extension.ts | 25 ++----------------- .../tiptap-mediapicker.extension.ts | 2 +- .../packages/rte/tiptap/extensions/types.ts | 22 ++++++++++++++++ 6 files changed, 29 insertions(+), 27 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 891d8ef366..b5741d6c14 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 1f0fe4c463..4764ef5727 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/index.ts index 6ff82ee0b6..20b2f7a29a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts index b35de22127..d7fb3485b5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts index 1b98dbb40f..1b69751c54 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts new file mode 100644 index 0000000000..aa01ea3420 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 a051713a0144a8cd37ead79fd54d0f2bf1056be9 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 | 319 ++++++++++++++---- 1 file changed, 258 insertions(+), 61 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 6524700bb1..de1c6e67ab 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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'); + #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; + } - const plugins = await firstValueFrom(plugin$); + // if no group is provided, add to the last group in the row + if (group === undefined) { + group = this._selectedValuesNew[row].length - 1; + } - 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), - }); - }); - } - }); + // 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'); + } - const value = this._toolbarConfig - .filter((t) => (t.alias !== alias && t.selected) || (t.alias === alias && checkbox.checked)) - .map((v) => v.alias); + #addRow() { + this._selectedValuesNew.push([[]]); + this.requestUpdate('_selectedValuesNew'); + } - this.value = value; + #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(); + 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 2fb32161ef090807b6cbe1e92937b79829156ced 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 --- .../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/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-tree.manager.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-tree.manager.ts index fc6a0c7334..61ebfea7a5 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-tree.manager.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/mocks/handlers/document/tree.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/document/tree.handlers.ts index e148fc83da..df3400b392 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/document/tree.handlers.ts +++ b/src/Umbraco.Web.UI.Client/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 a7a74a9bc353288d8c8ebfeeb12a1a8c78f08c00 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index b5741d6c14..413111b5c4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 4764ef5727..0fb04b34d6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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 a8be2c709f0a85447abf1c4e44712267334f5557 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 0fb04b34d6..3675670ab3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts index a280a832ed..28c9feb62e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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 0c4c33ad0c2f87d38073d09db3db191199586899 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index b5741d6c14..07d0542e0c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 f2e70c8f63d28d1abb141c9a34c8422240b7329e 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/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts index 09ec5524e0..cbdce9da9f 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts +++ b/src/Umbraco.Web.UI.Client/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 1efdc3d98af58979a0da8a63da985d253214172c 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/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts new file mode 100644 index 0000000000..899c775895 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts index e050542465..5c6fabf333 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 07d0542e0c..57d42f1cbe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 eab3232374d83f9fe267566c88c17bbea721e716 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 --- .../external/tiptap/extensions/tiptap-umb-image.extension.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts index 899c775895..e2f1e7f630 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 e61f4af84c3e98ca840b71df6f69f2aad5441905 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 57d42f1cbe..897355b90d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index 4de8b54d84..1d192608da 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts new file mode 100644 index 0000000000..5e0f0f3b81 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 df78ab6d7ac3a6c5b0567352f020e50927007a74 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/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts index e2f1e7f630..2f0eceedd4 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 249cb2883dd26d9af3f160cb085b5821029d0964 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index de1c6e67ab..b273529074 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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; } - .group-name { + .extension-item { + display: grid; + grid-template-columns: 36px 1fr auto; + grid-template-rows: 1fr; + } + .category-name { grid-column: 1 / -1; margin-bottom: 0; font-weight: bold; + display: flex; } `, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index eb0521df3f..d1a5464205 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 e015c0c7b657aca983606daf64089a461e430663 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 ++++++++++++++++++ .../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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts index 1a38fe7fe9..5e69aa24fd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index 1d192608da..8afb03e629 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts index d7fb3485b5..40c9fd6186 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts index 5e0f0f3b81..f6d64e11a2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts index 1b69751c54..7d96091761 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts new file mode 100644 index 0000000000..3d1ad704a6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts index aa01ea3420..5a2d19ccd4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/manifests.ts index 8e6836b5cc..a3e9604def 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 e107dbfc8ae8bcfdddee8ada6cb151f0f356bc94 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index db32b07231..1aca063f49 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 3675670ab3..1567febabb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index 7ab482cb77..60dedc1240 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 2d9eb20d68770b3df50e994dad44b7e014d2d276 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 1aca063f49..4bb0a53188 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -75,7 +75,6 @@ export class UmbInputTiptapElement extends UmbFormControlMixin html``, () => html` - + `, )}
    From 3eb899b72ccbb903711be67f062f85354f7a3d5f 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index b273529074..cb9dc749f5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 bd11ebb622335d2c8b34c3df91fedb0750d31453 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index cb9dc749f5..6dc6643142 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index d1a5464205..9110cbbd33 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 48a3777f588bda8b44e3dc5e0a5b1b90626ee178 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 9110cbbd33..471dd9dd5e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 65ca1f205cf33779b6d5e4a31a62a30bd39a89ab 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 6dc6643142..0abd2dc95e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 462efda81666c55bc6f3d1d97880f21402f8e85d 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index a8e9a0ed09..2041b8bcec 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -122,7 +122,6 @@ export class UmbInputTiptapElement extends UmbFormControlMixin html``, () => html` - `, )} From cd348dd424c3c609922b81fc648ed2fa0366227a 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 0abd2dc95e..1014ceda82 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 7f7bc2bc392895c552308c7efe7661997d198175 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/Umbraco.Web.UI.Client/src/packages/core/temporary-file/temporary-file-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/temporary-file-manager.class.ts index 9fcf08824d..4157d4cbea 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/temporary-file-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/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 54ab8491bbc3e8836419ebc2d99fc558c8724e8e 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/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts index 2f0eceedd4..6db61379c9 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 4c79e784bcffccf8e452faf5cf211ec5cb6206bc 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index 8afb03e629..7e56bfe4f9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts new file mode 100644 index 0000000000..d0cf48d79e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 95d685534132f3d7ce58933ee7a3e10c86159b1d 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/Umbraco.Web.UI.Client/src/mocks/handlers/temporary-file/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/mocks/handlers/temporary-file/temporary-file.handlers.ts diff --git a/src/Umbraco.Web.UI.Client/src/mocks/browser-handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/browser-handlers.ts index c8847d1eef..f3a3749350 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/browser-handlers.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/mocks/handlers/temporary-file/index.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/temporary-file/index.ts new file mode 100644 index 0000000000..57daf6e209 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/temporary-file/index.ts @@ -0,0 +1 @@ +export { handlers } from './temporary-file.handlers.js'; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/temporary-file/temporary-file.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/temporary-file/temporary-file.handlers.ts new file mode 100644 index 0000000000..a4cb1c7692 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 d7212162e5aa0230f9da19613823b0fbbb765a9b 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 --- .../mocks/handlers/temporary-file/temporary-file.handlers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/temporary-file/temporary-file.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/temporary-file/temporary-file.handlers.ts index a4cb1c7692..0c1eeb3c05 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/temporary-file/temporary-file.handlers.ts +++ b/src/Umbraco.Web.UI.Client/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 57ace824b9f4b34159c82063f8d8e8c779d46f65 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts index d0cf48d79e..bdd1579ea2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 7295c6abf0fa7835217f960344b6a7c9c8a2c4ae 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts index bdd1579ea2..c565dbb2b8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 401f39d47c8845ad3801b204245d2938e6157062 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 4bb0a53188..1dbd36c8d8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts index 5a2d19ccd4..7881c11c05 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts +++ b/src/Umbraco.Web.UI.Client/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 3a007a0046d5256f00cd8edde05d195a01201e64 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts index c565dbb2b8..7f4e34a2c9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 3fa5e21cb8ded67112b07023de5b15817ad03229 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 1dbd36c8d8..3881b8164c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 9860a9ad2a63f8ab0b890118a9d0bc148443dde5 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index 60dedc1240..f47d165a69 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 98ffbba68d8e2059900936879ddf01f8e459f7c4 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index f47d165a69..bf15ff95fd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 1b2b35a94d823140fb8a76a9d247e480565831eb 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index bf15ff95fd..fff49220dc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 fc5c3e8dcfb685a32dbfdc8e16585fc6d40a5402 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/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts index 6db61379c9..5b3737576e 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts index 7f4e34a2c9..a56d7184f7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 24c098b89118e524b3d7e74e4862dc30913a1dde 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index fff49220dc..fffb7fdcc5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 c8bdfd2ac3ac33aca29b48505f8e391dfec3e26b 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 3881b8164c..95280f1bd2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 4587ff63b930d6e27b64a3a5c9ec3dd44eaca272 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 95280f1bd2..0c5ee68662 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 0c5ee68662..b1ea239182 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 4f06d692963605fdde06e800e396441ac050efcd 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index b1ea239182..e5358e8387 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 d3c73009745d93d6d21de7311dc4c7e90ab9e6fe 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index e5358e8387..5baeb98148 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 5baeb98148..9d8f1e57fd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 9d8f1e57fd..28946adece 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 825dbcd4eb7c67d4d115303c306fafcf711f0e5d 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 --- .../packages/core/temporary-file/temporary-file-manager.class.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/temporary-file-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/temporary-file-manager.class.ts index 4157d4cbea..bade3a5086 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/temporary-file-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/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 d599b6085bb25f675ac4236e53ed8a77d24cabd5 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index fffb7fdcc5..2698adfdce 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 d19caa9a6ec364c6a28405e9828c98b3726d7d6e 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icons.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/embed.extension.ts rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{tiptap-mediapicker.extension.ts => mediapicker.extension.ts} (83%) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts new file mode 100644 index 0000000000..996fc4482b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icons.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icons.ts deleted file mode 100644 index 2676404411..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 4bb0a53188..095567fd41 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 1567febabb..39e75dc1b8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts index 28c9feb62e..30f1458cb7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts new file mode 100644 index 0000000000..101299b5fb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts new file mode 100644 index 0000000000..6524e15b04 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts new file mode 100644 index 0000000000..853cbb202f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts new file mode 100644 index 0000000000..42dea9c02f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts new file mode 100644 index 0000000000..6f1ac09400 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts new file mode 100644 index 0000000000..8f7ad839b2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts new file mode 100644 index 0000000000..f1d11a912e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts new file mode 100644 index 0000000000..4e29557f2a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts new file mode 100644 index 0000000000..1bd069617f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts new file mode 100644 index 0000000000..2343355c08 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts new file mode 100644 index 0000000000..f45ce80e1b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts new file mode 100644 index 0000000000..85e650e273 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts new file mode 100644 index 0000000000..a3e5feb0a9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts new file mode 100644 index 0000000000..5e6caad8cd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts new file mode 100644 index 0000000000..0c021383b5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts new file mode 100644 index 0000000000..aedfbc2d85 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts new file mode 100644 index 0000000000..049781dd1e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts new file mode 100644 index 0000000000..52387b9b62 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/embed.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/embed.extension.ts new file mode 100644 index 0000000000..4967051267 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index 8afb03e629..754fb3dcf1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts similarity index 83% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts index 7d96091761..ee3923cfa9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts deleted file mode 100644 index f6d64e11a2..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts new file mode 100644 index 0000000000..6f01aa8bcd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 2489d29f18ed13b919ac115dce14c959f9d1a11c 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) --- .../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 +- .../packages/rte/tiptap/extensions/core/code-block.extension.ts | 2 +- .../packages/rte/tiptap/extensions/core/heading1.extension.ts | 2 +- .../packages/rte/tiptap/extensions/core/heading2.extension.ts | 2 +- .../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 +- .../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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts index 101299b5fb..eca4ee1558 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts index 6524e15b04..7e2637c589 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts index 853cbb202f..c29233cc26 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts index 42dea9c02f..91ae3254b8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts index 6f1ac09400..9dd8170117 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts index 8f7ad839b2..f4f20cc56f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts index f1d11a912e..7f9ff33aef 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts index 4e29557f2a..41905a33ca 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts index 1bd069617f..caa62dd7fc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts index 2343355c08..c73aecc53a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts index 85e650e273..6ba8804acb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts index a3e5feb0a9..91967c14ca 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts index 5e6caad8cd..472a77b8a2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts index 0c021383b5..c7c54b8d43 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts index aedfbc2d85..543e2389be 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts index 049781dd1e..a83d7b8a80 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts index 52387b9b62..d2a0c98244 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/embed.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/embed.extension.ts index 4967051267..33de910ce6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/embed.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts index ee3923cfa9..40bbf7222a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts index 6f01aa8bcd..c711f68d46 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 02a3cf8e8d6779ae746bb6f1a42483aabc663dcd 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 095567fd41..8474cbc62d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 39e75dc1b8..fdc846296e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index 60dedc1240..d1dee314ae 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 b22314867ad9355d2ec584322330151d91984745 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 +++++++++++++++++-- .../packages/rte/tiptap/extensions/types.ts | 33 ++++++++++++----- .../tiptap/extensions/urlpicker.extension.ts | 8 ++-- 24 files changed, 177 insertions(+), 109 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index fdc846296e..ede9344903 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts index eca4ee1558..94089225fc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts index 7e2637c589..c29bd5408c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts index c29233cc26..7915fc7b5b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts index 91ae3254b8..b91bc201b5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts index 9dd8170117..62a542e753 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts index f4f20cc56f..1d144bdfc4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts index 7f9ff33aef..99683dbc7b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts index 41905a33ca..2cea4a9f3d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts index caa62dd7fc..f71f01ebd2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts index c73aecc53a..2bc8b8a2fa 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts index 6ba8804acb..878f199d26 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts index 91967c14ca..bfef2a61fd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts index 472a77b8a2..6fefff51b2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts index c7c54b8d43..a073705b6c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts index 543e2389be..3f814bc8ad 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts index a83d7b8a80..dcef9a523b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts index d2a0c98244..e538e850c1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/embed.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/embed.extension.ts index 33de910ce6..3750438e5f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/embed.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts index 40bbf7222a..7e14c797dc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts index 40c9fd6186..5ecb61da5d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts index 5a2d19ccd4..949d73f056 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts index c711f68d46..dec5d64d05 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 c90eb7ae32487f95f45de5672d2636a24e84f88d 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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-blockquote.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-bold.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-embed.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-heading-1.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-heading-2.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-heading-3.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-horizontal-rule.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-italic.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-strikethrough.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-text-align-center.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-text-align-justify.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-text-align-left.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-text-align-right.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-underline.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json index 4fae5c0ec7..f8dd3ed1b4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json @@ -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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts index ff42d07bc6..acc2b0e7c6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts @@ -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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-blockquote.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-blockquote.ts new file mode 100644 index 0000000000..7d4802defc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-bold.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-bold.ts new file mode 100644 index 0000000000..6b4b1d986b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-embed.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-embed.ts new file mode 100644 index 0000000000..22fdef036f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-heading-1.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-heading-1.ts new file mode 100644 index 0000000000..20fe0c5dd1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-heading-2.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-heading-2.ts new file mode 100644 index 0000000000..f062a49ca9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-heading-3.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-heading-3.ts new file mode 100644 index 0000000000..cb237bb4a2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-horizontal-rule.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-horizontal-rule.ts new file mode 100644 index 0000000000..424f13bb2f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-italic.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-italic.ts new file mode 100644 index 0000000000..d70979bfe6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-strikethrough.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-strikethrough.ts new file mode 100644 index 0000000000..9af35d7e81 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-text-align-center.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-text-align-center.ts new file mode 100644 index 0000000000..5d985fd584 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-text-align-justify.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-text-align-justify.ts new file mode 100644 index 0000000000..7279356fc8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-text-align-left.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-text-align-left.ts new file mode 100644 index 0000000000..43f4ebf794 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-text-align-right.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-text-align-right.ts new file mode 100644 index 0000000000..a03f55eec2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-underline.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-underline.ts new file mode 100644 index 0000000000..84f133257c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts deleted file mode 100644 index 996fc4482b..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index ede9344903..88f211e458 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts index 7e14c797dc..1c649a472a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts index 2a0fd2273e..3695f22d77 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts index dec5d64d05..2781a41f1d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 41151a52dbe2e3e5073355b5cc5052a7c602dab8 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/code-editor.extension.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/code-editor.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/code-editor.extension.ts new file mode 100644 index 0000000000..92ed2a4022 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index 754fb3dcf1..d6fca8368f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts index 3695f22d77..17a418e613 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts +++ b/src/Umbraco.Web.UI.Client/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 d9c0ec9dab6b2bd65092bb8afb77fc4688224f29 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts index a56d7184f7..a27f019fb6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-media-upload.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts index da03130245..b10b98f2a3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts +++ b/src/Umbraco.Web.UI.Client/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 528feb094fa1cfcbc86e9b9baf5321bdb98e72b5 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/{extensions => components/toolbar}/tiptap-toolbar-button.element.ts (91%) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/embed.extension.ts rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{ => umb}/code-editor.extension.ts (63%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embed.extension.ts rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{ => umb}/mediapicker.extension.ts (80%) rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{ => umb}/urlpicker.extension.ts (62%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 88f211e458..5b879b5c4a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts similarity index 91% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts index 17a418e613..fc3c7219bd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-button.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts index 22c2144b4e..d1ecfdfe19 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts index c0d2d8485f..e65d167551 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts index b73532a98c..8dd956279c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts index 722c6c1c83..7cfa5861b2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts index 5872321e07..7543e321fb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts index 22ca614266..3edcf7b57a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts index 5662dd9636..9def84dc2c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts index e796db3b8e..0219f45673 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts index f71f01ebd2..bf8b9956e9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts index 04de717a74..ff122f81e4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts index f45ce80e1b..9ef58cd081 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts index afa3433551..471d794e1c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts index b00939b028..b073c12dd9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts index c495cd77fd..fa9c90855c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts index d9818b16c0..03e197654f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts index 584a4dd5f4..2f35da46d2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts index 1fddf39ae0..62de9a54f9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts index 6b40dc881a..4e1bac6d6a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/embed.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/embed.extension.ts deleted file mode 100644 index d3aca48d5b..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index d6fca8368f..4b9394020d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/code-editor.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts similarity index 63% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/code-editor.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts index 92ed2a4022..be55c65dbe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/code-editor.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embed.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embed.extension.ts new file mode 100644 index 0000000000..f09ecfd084 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts similarity index 80% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts index 1c649a472a..6947d772b8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/urlpicker.extension.ts similarity index 62% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/urlpicker.extension.ts index 2781a41f1d..7bedab735b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 fe4796ae277c6cd39c4e7fa0ff55b87e91f1dfb1 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/utils/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts index 57d02b8ccf..a1412bdc07 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/index.ts index f0f1ade33d..5a6a198bdb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/utils/index.ts new file mode 100644 index 0000000000..cbd93b58fe --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 161099caa49660102bc59d299a86618f3692c912 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/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/index.ts | 1 - 4 files changed, 3 insertions(+), 2 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/{rte/tiptap/utils/index.ts => core/utils/media/image-size.function.ts} (100%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts index 483767c50a..abbc560792 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts @@ -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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/media/image-size.function.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/utils/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/utils/media/image-size.function.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts index a1412bdc07..ef23e3c0ed 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/index.ts index 5a6a198bdb..f0f1ade33d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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 2c3dd9ab916a2fcb0ae1532677146789a007f962 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 --- .../packages/rte/tiptap/extensions/umb/media-upload.extension.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts index ef23e3c0ed..2a2278597f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 1d48c1e86d5fcb5f3385aadabc65cd041bff180a 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts index 2a2278597f..ec9e7cfd5d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 481dc519f5f5b047e5536ddd8b9ff59cec99465f 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 471dd9dd5e..10051e7b93 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 bcb5b660a16aa26d84d8e10a289d759577069d77 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index b735aee02a..a4fac1bf92 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 d75161e145f1319275f028dcdad393dc0936330f 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 1014ceda82..54107b1c7b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts new file mode 100644 index 0000000000..5f15c61848 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 9bb2e3e62348a4dee78b1e2c994b487f68781258 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index 5f15c61848..6a9339d3dc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 26cf9674a206702fb9a56fe54840daa12b8deb99 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 | 261 +++++------------- ...ap-toolbar-groups-configuration.element.ts | 79 +++--- 2 files changed, 111 insertions(+), 229 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 54107b1c7b..d59e2a6ed0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 = []; + private _extensionCategories: ExtensionCategory = []; @state() - private _toolbarConfig: Array = []; - - @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 (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); } - // 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'); - } - - #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 cf4ae698870de244307343713b3f184902a9d5bd 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index d59e2a6ed0..58560697cb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index 88fd08c1f4..b057fc12a7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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; + icon?: string; }; -type ToolbarGroup = { - groupId: string; - buttons: ToolbarButton[]; -}; - -type ToolbarRow = { - rowId: string; - groups: ToolbarGroup[]; -}; - -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; + private moveItem(from: [number, number, number], to: [number, number, number]) { + const [fromRow, fromGroup, fromItem] = from; + const [toRow, toGroup, toItem] = to; - row.groups.push({ - groupId: UmbId.new(), - buttons: [], - }); + // Get the item to move from the 'from' position + const itemToMove = this.#value[fromRow][fromGroup][fromItem]; - this.requestUpdate(); - }; + // Remove the item from the original position + this.#value[fromRow][fromGroup].splice(fromItem, 1); - #removeGroup(groupId: string) { - const row = this.#value.find((row) => row.groups.some((group) => group.groupId === groupId)); - if (!row) return; + // Insert the item into the new position + this.#value[toRow][toGroup].splice(toItem, 0, itemToMove); - row.groups = row.groups.filter((group) => group.groupId !== groupId); - - this.requestUpdate(); + // Trigger an update to re-render the UI + this.requestUpdate('value'); } - #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; - - // 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 = this.#value.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); + #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))} + +
    + `; + } + + private renderRow(row: string[][], rowIndex: number) { + return html` +
    + ${repeat(row, (group, groupIndex) => this.renderGroup(group, rowIndex, groupIndex))} + +
    + `; + } override render() { - return html` - - - ${repeat(this.#value, (row) => row.rowId, this.#renderRow)}`; + 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 7acf3d4b01bf9b6dfec7edadb4cec1bbc5c386f2 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 --- src/Umbraco.Web.UI.Client/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 +- .../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 +- .../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/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/index.html b/src/Umbraco.Web.UI.Client/index.html index b92c54f0d6..5b7473a4d2 100644 --- a/src/Umbraco.Web.UI.Client/index.html +++ b/src/Umbraco.Web.UI.Client/index.html @@ -6,6 +6,13 @@ Umbraco + diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts index 4fef647c2e..0145765978 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-custom-view/block-editor-custom-view.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-custom-view/block-editor-custom-view.extension.ts index d41818eae6..36f849fa13 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-custom-view/block-editor-custom-view.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts index 416ce658ba..d85cf71cea 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts @@ -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/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-manager.context.ts index 1acd32840f..86d95a3cd5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts index 54a59940f8..1e6d55a034 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts @@ -1,10 +1,14 @@ -import 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/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/ref-rte-block/ref-rte-block.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/ref-rte-block/ref-rte-block.element.ts index 153538eaa9..c20abdfb1b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/ref-rte-block/ref-rte-block.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entries.context.ts index ac2501c80a..7b356d3eea 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-manager.context.ts index 8f7e86d2a9..f568f2b474 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-rte/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/manifests.ts index 0f70e08783..7b3a871ae7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts index c941292048..4abbf3c32a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts @@ -1,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/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts new file mode 100644 index 0000000000..d70c9e9adc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/manifests.ts new file mode 100644 index 0000000000..2181248d85 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-rte/types.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/types.ts index c97075ec68..dca59515c5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/types.ts @@ -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/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts index b56ef6ac6f..ce98e0bee7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts @@ -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/Umbraco.Web.UI.Client/src/packages/block/custom-view/manifest.ts b/src/Umbraco.Web.UI.Client/src/packages/block/custom-view/manifest.ts index 46e74f3b3a..317189167a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/custom-view/manifest.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 0aa053cde2..b37d8d3f0c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts index fc3c7219bd..d412ec4682 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts index bf8b9956e9..7254da3b61 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts index b10b98f2a3..2ab9a0179e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index a4fac1bf92..fcd9ce238a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/vite.config.ts index 1385afcf23..231d180c29 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/vite.config.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts index 8e8f81cafa..e2d3c3b877 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts index 8cb80938c6..7f95d97cdf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts +++ b/src/Umbraco.Web.UI.Client/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 a1f4f1e955f1c75b0da037a0da7464dcb893e611 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 | 94 ++++++++----------- ...ap-toolbar-groups-configuration.element.ts | 71 ++++++++------ 2 files changed, 79 insertions(+), 86 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 58560697cb..eab44def55 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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)), + ); } + + this.requestUpdate(); } - #onChange() { - this.value = this._internalValue.map((x) => ({ - alias: x.alias, - hideInToolbar: x.hideInToolbar, - position: x.position, - })); - + #onChange = (event: CustomEvent) => { + const value = (event.target as UmbTiptapToolbarGroupsConfigurationElement).value; + this.value = { + ...this.value, + toolbarLayout: value, + }; this.dispatchEvent(new UmbPropertyValueChangeEvent()); - } - - #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` - - +
    ${repeat( this._extensionCategories, diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index b057fc12a7..8530910fee 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 4612c93af69db2ead16cf978fcfc5639570a68e7 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 5b879b5c4a..f276ecc7df 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts new file mode 100644 index 0000000000..205af34d1e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 55da8663cae38f8ace2fb7a442de304880faf2e6 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 | 83 ++++++++++--------- 2 files changed, 46 insertions(+), 44 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index f276ecc7df..9ae665be09 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts index 205af34d1e..863bd5d2c9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts +++ b/src/Umbraco.Web.UI.Client/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 { + .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); + } + + .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); - } - - .dropdown-item { - background-color: var(--uui-color-surface); - display: flex; - flex-direction: column; - text-wrap: nowrap; + padding: var(--uui-size-space-1); } `; } From 194cc2d67c97ee7cc50d936863163ac9ba038750 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts index 863bd5d2c9..bb8fff58c0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts +++ b/src/Umbraco.Web.UI.Client/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 02777daeb566271c5a66f0784d945d40a73ba7b1 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts index bb8fff58c0..f9c2f1cc1e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts +++ b/src/Umbraco.Web.UI.Client/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 3d18464474af2ab3abbe036de78ecdc92a9e1a65 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 --- .../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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown-base.element.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/style-select.extension.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/index.ts index d7e1544b7d..f03903fb07 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 9ae665be09..0dadca09f2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown-base.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown-base.element.ts new file mode 100644 index 0000000000..d9750baa8b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown.element.ts deleted file mode 100644 index f9c2f1cc1e..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/style-select.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/style-select.extension.ts new file mode 100644 index 0000000000..6429d7021d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 5984cb5da09b40ffb73092fa7dcc7614bfb762b4 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 10051e7b93..fd5d2ce6de 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 7f850738ddb92a031d1a8ee0a33e3930c791a5b3 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 | 76 +++++++++---------- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index eab44def55..df5b2896dd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index 8530910fee..b74105b57d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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,37 +35,44 @@ 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])); - - 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; - } + // Get the item to move from the 'from' position + const itemToMove = this.value[fromRow][fromGroup][fromItem]; // Remove the item from the original position - fromGroupArray.splice(fromItem, 1); + this.value[fromRow][fromGroup].splice(fromItem, 1); // Insert the item into the new position - toGroupArray.splice(toItem, 0, itemToMove); - - // Replace the whole value with the modified cloned structure - this.#value = clonedValue; + this.value[toRow][toGroup].splice(toItem, 0, itemToMove); // Trigger a re-render this.requestUpdate('value'); this.dispatchEvent(new UmbChangeEvent()); }; + #addGroup = (rowIndex: number, groupIndex: number) => { + this.value[rowIndex].splice(groupIndex, 0, []); + this.requestUpdate('value'); + this.dispatchEvent(new UmbChangeEvent()); + }; + + #removeGroup = (rowIndex: number, groupIndex: number) => { + this.value[rowIndex].splice(groupIndex, 1); + this.requestUpdate('value'); + this.dispatchEvent(new UmbChangeEvent()); + }; + + #addRow = (rowIndex: number) => { + this.value.splice(rowIndex, 0, [[]]); + this.requestUpdate('value'); + this.dispatchEvent(new UmbChangeEvent()); + }; + + #removeRow = (rowIndex: number) => { + this.value.splice(rowIndex, 1); + this.requestUpdate('value'); + this.dispatchEvent(new UmbChangeEvent()); + }; + #onDragStart = (event: DragEvent, pos: [number, number, number]) => { event.dataTransfer!.setData('application/json', JSON.stringify(pos)); event.dataTransfer!.dropEffect = 'move'; @@ -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 c0dcce7a0fceb421be15adf043c8457346941e9b 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 --- .../external/tiptap/extensions/tiptap-umb-image.extension.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts index 5b3737576e..fb3d645b4e 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 7ac4d154b4d4a96d9650777a138ea0d8a105f736 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/Umbraco.Web.UI.Client/src/packages/core/utils/media/image-size.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/media/image-size.function.ts index cbd93b58fe..8958da1c0b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/media/image-size.function.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts index ec9e7cfd5d..2cfd79dca1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 902bcbb54340e8389cbde51cdeadd37fc5b9a12d 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/Umbraco.Web.UI.Client/src/packages/{tiny-mce => media/media}/modals/media-caption-alt-text/index.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{tiny-mce/modals => media/media/modals/media-caption-alt-text}/manifests.ts (63%) rename src/Umbraco.Web.UI.Client/src/packages/{tiny-mce => media/media}/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{tiny-mce => media/media}/modals/media-caption-alt-text/media-caption-alt-text-modal.token.ts (100%) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiny-mce/modals/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/index.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/index.ts index 8ee054676a..ca9211b392 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/media/media/modals/index.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/index.ts index 380266b300..4dc380687c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/media/media/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/manifests.ts index 260e1366e4..fb40e44c2f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiny-mce/modals/media-caption-alt-text/index.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/modals/media-caption-alt-text/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/manifests.ts similarity index 63% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/modals/manifests.ts rename to src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/manifests.ts index cd73c2fa3f..61cb7b6d72 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiny-mce/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/modals/media-caption-alt-text/media-caption-alt-text-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.token.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/modals/media-caption-alt-text/media-caption-alt-text-modal.token.ts rename to src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.token.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/manifests.ts index 7c24fabe3e..0c978efd63 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiny-mce/modals/index.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/modals/index.ts deleted file mode 100644 index 011799ec00..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/modals/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './media-caption-alt-text/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index 91b79639a7..4f8194adcb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/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 7d19b901fc96eb199ce31cba4a244fa122b6481f 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/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts | 1 + .../packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/{tiny-mce/utils.ts => core/utils/get-guid.function.ts} (94%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/utils.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/get-guid.function.ts similarity index 94% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/utils.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/utils/get-guid.function.ts index d1236320b4..3078f5cf6a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/utils.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts index abbc560792..5babb4a5fe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts @@ -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/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index 4f8194adcb..7e3658e8d1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/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 3e741ca49242f7564d01331070eee82d17cc7b8d 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 7d19b901fc96eb199ce31cba4a244fa122b6481f. --- src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts | 1 - .../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/Umbraco.Web.UI.Client/src/packages/{core/utils/get-guid.function.ts => tiny-mce/utils.ts} (94%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts index 5babb4a5fe..abbc560792 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts @@ -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/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index 7e3658e8d1..4f8194adcb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/utils/get-guid.function.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/utils.ts similarity index 94% rename from src/Umbraco.Web.UI.Client/src/packages/core/utils/get-guid.function.ts rename to src/Umbraco.Web.UI.Client/src/packages/tiny-mce/utils.ts index 3078f5cf6a..d1236320b4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/get-guid.function.ts +++ b/src/Umbraco.Web.UI.Client/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 bbff445851f111ecb9c1bcfe677f85dce89b7ef6 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index df5b2896dd..7daabb016f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 4700d10344ee80b237853831694b469f051a8a3d 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 | 75 +++++++++---------- 2 files changed, 54 insertions(+), 63 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 7daabb016f..df518a84bf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index b74105b57d..3b8e86bd83 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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; @@ -13,64 +11,63 @@ 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; - } - - _value: Array = [[[]]]; - @property({ attribute: false }) extensions: Array = []; + @state() + private _toolbar: string[][][] = [[[]]]; + + #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 d7832d92c675c50cf196f6161be114d48b7f1fec 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index df518a84bf..0daa31014a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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; + this.#value = value; } - get value(): NewestValue { - return this.#deepClone(this.#value) as NewestValue; + get value(): ServerValue { + return this.#value; } - #deepClone(value: unknown): unknown { - return JSON.parse(JSON.stringify(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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index 3b8e86bd83..411be39421 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 0daa31014a..d288504bd5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index 411be39421..8c9051ef06 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 72c8037984e4cb56d3ff4d7065a86889e1590711 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index d288504bd5..1ccb15acee 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index 0ecdff6ad6..d0b0018a5e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/Umbraco.Web.UI.Client/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 980f6caec65c3455fe1d607d52dc93b360525cb2 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 1ccb15acee..6af3211475 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index d0b0018a5e..5d48132cea 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/Umbraco.Web.UI.Client/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 001590dd10703e6825645fbc4bc1c5574df57699 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 6af3211475..6b309a4a61 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index 5d48132cea..f7f79c73ed 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/Umbraco.Web.UI.Client/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 3dc10183ce9b4b8b64d1f880aab97d2b86ae11df 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index f7f79c73ed..9f52d8b66b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/Umbraco.Web.UI.Client/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 f7373564b65f158d0c9ba5574cd90da4b14ee1bb 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 6b309a4a61..92d16dea1b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 9886d6a2110f9faa7546fbec24199d4287805682 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 92d16dea1b..91fddc754b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index 9f52d8b66b..090cbfe4a3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/Umbraco.Web.UI.Client/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 3b1526a979c33b22e79c328b7c167f6f670eb70c 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index 090cbfe4a3..d26adcab39 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/Umbraco.Web.UI.Client/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 3f83bd5bce6024b09db6b7e634e52334b9657fa2 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index d26adcab39..8fca14cd4f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/Umbraco.Web.UI.Client/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 48d01de5ca0c7d06435b79318c4ffa0ef802e541 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts index be55c65dbe..32722af243 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 2f82ca46d21242479e6bf56473c8f9384ad7e5ba 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/Umbraco.Web.UI.Client/src/packages/tiny-mce/index.ts | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts index 6b512da1de..f220a31719 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiny-mce/index.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/index.ts index 97c9d86639..8a8c2711ca 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/index.ts @@ -1,2 +1 @@ export * from './components/index.js'; -export * from './modals/index.js'; From 88eea51c6ae8c4b925f7d940f7a1aba5a70ea0e9 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/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts | 1 + .../packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/{tiny-mce/utils.ts => core/utils/get-guid.function.ts} (94%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/utils.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/get-guid.function.ts similarity index 94% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/utils.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/utils/get-guid.function.ts index d1236320b4..3078f5cf6a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/utils.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts index abbc560792..5babb4a5fe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts @@ -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/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index 4f8194adcb..7e3658e8d1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/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 ab3712a6054f6f244acb35a06412520859acf051 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/Umbraco.Web.UI.Client/src/packages/core/utils/{get-guid.function.ts => get-guid-from-udi.function.ts} (58%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/get-guid.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/get-guid-from-udi.function.ts similarity index 58% rename from src/Umbraco.Web.UI.Client/src/packages/core/utils/get-guid.function.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/utils/get-guid-from-udi.function.ts index 3078f5cf6a..92655aae55 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/get-guid.function.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts index 5babb4a5fe..7bbf20844e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts @@ -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/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index 7e3658e8d1..b8ab127669 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/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 9405c671637d6b96603a98b8d563368777762793 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index b37d8d3f0c..85e18d8a67 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts index d70c9e9adc..42e48b8a7d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 e1121364d47319a69acee58e32ccb1c7eddf4e6a 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/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embed.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts new file mode 100644 index 0000000000..bcef5ee6eb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts index 5c6fabf333..6f9245ee78 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/embedded-media/modal/embedded-media-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/embedded-media/modal/embedded-media-modal.element.ts index fcf5ff01a6..62f8468c17 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/embedded-media/modal/embedded-media-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index cb135c4773..827e80d2fa 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embed.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embed.extension.ts deleted file mode 100644 index f09ecfd084..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts new file mode 100644 index 0000000000..ccd8ee60e0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 d528a555b5bc70f8620a62c1e734c9a3cf87197a 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 0aa053cde2..8370dc7cdb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 5b879b5c4a..ad36b8bd8c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 91fddc754b..9138d3847f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index 8fca14cd4f..6d94938f87 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 10051e7b93..57baf235eb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 6b1d80cedea6fda3822e60527adf4ef740aa4e91 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/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts index adb87d2e7d..40581dd0a5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/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 422b89c0e7d6bac9652c8926831fb1341f8fb137 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/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-figcaption.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-figure.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/figure.extension.ts diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-figcaption.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-figcaption.extension.ts new file mode 100644 index 0000000000..23b188b7bf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-figure.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-figure.extension.ts new file mode 100644 index 0000000000..0cae9f7b47 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts index 5c6fabf333..a2c146f446 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/figure.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/figure.extension.ts new file mode 100644 index 0000000000..1da6bdec35 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts index 9ef58cd081..b4c0961f2b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 72d7093fbf7322c5844bb305388a5710d82a281d 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/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts index fb3d645b4e..81807bc9ad 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 3adb862c603ec5c2854187349ffc6b119ccf37b4 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts index 6947d772b8..bafba48bd4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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'); - const selection = await this.#openMediaPicker(); + 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(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 e6fe6539d26765aef735e8b155ea39b7833beabc 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 85e18d8a67..b2eb2ecd14 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 a7e93e82bb6df7e0d2ec2e6ed8ee1e2749ffa7cf 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 --- src/Umbraco.Web.UI.Client/package-lock.json | 53 ++++++++++++++++ src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-table.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/table.extension.ts diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 24c064470c..d1233645ab 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index d058a21df1..d50b98524f 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts index a2c146f446..e186f0ccf3 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json index 4d592bfb10..9c53e18410 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json @@ -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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts index 3139743a15..1f55c16a7f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts @@ -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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-table.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-table.ts new file mode 100644 index 0000000000..283a8a901c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 85e18d8a67..d6e9872427 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts index b4c0961f2b..309598a14f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/table.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/table.extension.ts new file mode 100644 index 0000000000..ae2d71fa73 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 7f2c0e32ac1648024feb8005743c69913a485900 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/Umbraco.Web.UI.Client/src/packages/embedded-media/modal/embedded-media-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/embedded-media/modal/embedded-media-modal.element.ts index 62f8468c17..e91c24cbcd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/embedded-media/modal/embedded-media-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/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 e07718c2b6993d7d12104ad90c7c395a75809788 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/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts index bcef5ee6eb..3a13add2cb 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 20eaf2a193032fdd1d6db05e4fd51eeaf6690dfd 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 0dadca09f2..457eb7ad8c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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 16a5815d77f7dc67dfb1d0b1d7645658e79b9d79 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 8370dc7cdb..0a625ac6cd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 b7e11f94dc718a4cc72dc884d61c632da6896bc1 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 --- src/Umbraco.Web.UI.Client/package-lock.json | 306 ++++++++++---------- src/Umbraco.Web.UI.Client/package.json | 56 ++-- 2 files changed, 181 insertions(+), 181 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index d1233645ab..7f836c586a 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index d50b98524f..5db1596f1d 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/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 ad88d3a08f3d1991e0cd4c258151ae61b8260107 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/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts | 1 + .../src/packages/rte/tiptap/property-editors/manifests.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts index 5e69aa24fd..6b7ca9ed76 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts index b9b660577c..71f28f1a7c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 7b0fda5591d0a03555d2d25ac416da8a3c6ff5fb 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts index ccd8ee60e0..9d0a1f71c8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 ad1c1163519fcd423006763e005e444241a6cab7 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index fcd9ce238a..ade8960b91 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts index 7f95d97cdf..11a10a4862 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts +++ b/src/Umbraco.Web.UI.Client/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 55a7298ae10b84991345cddcfe200aa56b623671 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/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts index 1e6d55a034..435711cb3e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts @@ -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/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts index 4abbf3c32a..642128893c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiny-mce-plugin/tiny-mce-block-picker.plugin.ts @@ -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/Umbraco.Web.UI.Client/src/packages/core/temporary-file/temporary-file-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/temporary-file-manager.class.ts index bade3a5086..e89f8979aa 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/temporary-file-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts index 40581dd0a5..48acc80eb5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index cdce8380da..dc42f4fc16 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts index 30f1458cb7..36a75174bd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts index d412ec4682..e7b2fd0b81 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown-base.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown-base.element.ts index d9750baa8b..a5308a8539 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-dropdown-base.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts index 2cfd79dca1..ac8fd6eb4f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts index 6947d772b8..32f38d2300 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 1014ceda82..13a05350c9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 d3c91097c441a62bf8b6239720e2818317abd7dc 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 9138d3847f..91d1a11c51 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index 6d94938f87..c7ddbafb35 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/Umbraco.Web.UI.Client/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 e9882d30d23c751a7ddf189e1d696d3737a10509 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 --- src/Umbraco.Web.UI.Client/package-lock.json | 40 ++++++++++----------- src/Umbraco.Web.UI.Client/package.json | 2 +- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 7f836c586a..bde89122d0 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 5db1596f1d..02542051f5 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/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 70d1918d3d4fc3a372748385a8552d68523ef479 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index c7ddbafb35..4da770c009 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/Umbraco.Web.UI.Client/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 10c3d673568d9579503d53486041c3956010167f 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index 4da770c009..cccbdaa74d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/Umbraco.Web.UI.Client/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 689beac212385d08b3549fb582036a94c733a1bb 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 | 72 ++++++++++++++----- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index cccbdaa74d..fc42e3b242 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/Umbraco.Web.UI.Client/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 { + display: none; + } .remove-group-button { position: absolute; - top: -4px; - right: -4px; - display: none; + top: -26px; + left: 50%; + transform: translateX(-50%); + z-index: 1; } + .row:hover .remove-row-button, .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; + display: flex; } `, ]; From 43efd2ecdaf31b2ebe2a7f8a6013e316320420b0 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts index fc42e3b242..b978935be1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts +++ b/src/Umbraco.Web.UI.Client/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 2fa21b889586ae650f377f25ebbf8b8dfd4c7d47 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 | 390 +++++++++++------- ...p-toolbar-groups-configuration2.element.ts | 340 --------------- 3 files changed, 246 insertions(+), 492 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 91d1a11c51..4ecf695b94 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts index 8c9051ef06..9a04507cb3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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], - }, - ]; - - 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(); - - 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])); - }); - }, - ); - - setTimeout(() => { - this._toolbar = this.toStructuredData(this.#testData); - }, 2000); + 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); } - private moveItem = (from: [number, number, number], to: [number, number, number]) => { - const [fromRow, fromGroup, fromItem] = from; - const [toRow, toGroup, toItem] = to; + get value(): TestServerValue { + return this.#originalFormat; + } - // Get the item to move from the 'from' position - const itemToMove = this._toolbar[fromRow][fromGroup][fromItem]; + @property({ attribute: false }) + extensionConfigs: Extension[] = []; - // Remove the item from the original position - this._toolbar[fromRow][fromGroup].splice(fromItem, 1); + //TODO: Use the context again so that we can remove items from the extensions list from here. - // Insert the item into the new position - this._toolbar[toRow][toGroup].splice(toItem, 0, itemToMove); + @state() + _structuredData: string[][][] = [[[]]]; - this.#toolbarLayout?.setValue(this._toolbar); - }; + #originalFormat: TestServerValue = []; - #addGroup = (rowIndex: number, groupIndex: number) => { - this._toolbar[rowIndex].splice(groupIndex, 0, []); - this.#toolbarLayout?.setValue(this._toolbar); - }; + #currentDragAlias?: string; - #removeGroup = (rowIndex: number, groupIndex: number) => { - this._toolbar[rowIndex].splice(groupIndex, 1); - this.#toolbarLayout?.setValue(this._toolbar); - }; - - #addRow = (rowIndex: number) => { - this._toolbar.splice(rowIndex, 0, [[]]); - this.#toolbarLayout?.setValue(this._toolbar); - }; - - #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'; + #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; - const fromPos: [number, number, number] = JSON.parse(event.dataTransfer!.getData('application/json') ?? '[0,0,0]'); - this.moveItem(fromPos, toPos); + if (fromPos) { + this.moveItem(fromPos, toPos); + } else if (this.#currentDragAlias) { + this.insertItem(this.#currentDragAlias, toPos); + } }; - private renderItem(alias: string, rowIndex: number, groupIndex: number, itemIndex: number) { - const extension = this.availableExtensions.find((ext) => ext.alias === alias); - if (!extension) return nothing; + 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, [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 { + display: none; + } .remove-group-button { position: absolute; - top: -4px; - right: -4px; - display: none; - } - .group:hover .remove-group-button { - display: block; + top: -26px; + left: 50%; + transform: translateX(-50%); + z-index: 1; } - .remove-row-button { - position: absolute; - left: -25px; - top: 8px; - display: none; - } - .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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration2.element.ts deleted file mode 100644 index b978935be1..0000000000 --- a/src/Umbraco.Web.UI.Client/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 b1b2b6202d04eaffe39e11315d64d05eb47b5f9b 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 --- .../packages/rte/tiptap/property-editors/tiptap/manifests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 1e00e10acc..29db309aa0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 6a27ab8ce49521bf5644ce5aecffa61f05f9a864 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/{tiptap-toolbar-groups-configuration.element.ts => input-tiptap-toolbar-layout.element.ts} (94%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts similarity index 94% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts index 9a04507cb3..e274abda5b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap-toolbar-groups-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index f03d85412a..cf90d1a615 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 29db309aa0..a263cdb25c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 c779e1348af6c2db1e657c3cf48db3f1593a68e1 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts index e274abda5b..e5e30948d0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index a263cdb25c..5de70b8af0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 e7306328ba6d7506c985580616af4e7c6ad972de 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts index e5e30948d0..b7a404fd83 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index cf90d1a615..c70e4c0518 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 15c0667c6bc78f826ca679be248e7c6e090bf943 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/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.test.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.test.ts index 0a0fe51e55..3fed06548a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.test.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts index 48acc80eb5..06ad404ab8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/modals/media-caption-alt-text/media-caption-alt-text-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/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 14605466a8e2d5362b228cb638efd4944721cdd7 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/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.context.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.context.ts index fe8fcec48b..1fabdd6526 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.context.ts +++ b/src/Umbraco.Web.UI.Client/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 194bb8c958fd7c948297f5f0b565a97c0412ee13 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/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index b8ab127669..bc53032243 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/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 20915b23b05366b98961bf9feb930426ccde4909 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 | 56 ++++++------ .../packages/rte/tiptap/extensions/types.ts | 6 +- .../tiptap/extensions/umb/link.extension.ts | 91 +++++++++++++++++++ .../extensions/umb/urlpicker.extension.ts | 27 ------ 14 files changed, 270 insertions(+), 60 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-link.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-unlink.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button-disabled.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/unlink.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/urlpicker.extension.ts diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-link.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-link.extension.ts new file mode 100644 index 0000000000..1576004644 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts index 92b7ceddbb..e17d84cb3a 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/manifests.ts index 2181248d85..5b539cfb80 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json index 9c53e18410..450cc944d9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json @@ -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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts index 1f55c16a7f..0b0df45501 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts @@ -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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-unlink.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-unlink.ts new file mode 100644 index 0000000000..d770affee6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button-disabled.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button-disabled.element.ts new file mode 100644 index 0000000000..a2311fa601 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts index e7b2fd0b81..c193554a81 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts index 309598a14f..3f4b66173e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/unlink.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/unlink.extension.ts new file mode 100644 index 0000000000..5645c154bc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index 827e80d2fa..7fc3abe7c2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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', @@ -31,14 +30,27 @@ const umbExtensions: Array import('./umb/embedded-media.extension.js'), + alias: 'Umb.Tiptap.MediaUpload', + name: 'Media Upload Tiptap Extension', + api: () => import('./umb/media-upload.extension.js'), + weight: 900, meta: { - alias: 'umb-embedded-media', - icon: 'icon-embed', - label: '#general_embed', + alias: 'umbMediaUpload', + icon: 'icon-image-up', + label: 'Media upload', + }, + }, + { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Link', + name: 'Link Tiptap Extension', + api: () => import('./umb/link.extension.js'), + weight: 102, + meta: { + 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts index 2ab9a0179e..919bd3092b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts new file mode 100644 index 0000000000..3ea28a010d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/urlpicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/urlpicker.extension.ts deleted file mode 100644 index 7bedab735b..0000000000 --- a/src/Umbraco.Web.UI.Client/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 1cae7fa86f4d7125c835b68ce7846c2e474dc726 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 --- src/Umbraco.Web.UI.Client/package-lock.json | 7 +- src/Umbraco.Web.UI.Client/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 .../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 -- src/Umbraco.Web.UI.Client/tsconfig.json | 2 +- 46 files changed, 163 insertions(+), 38 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/index.ts rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/components/index.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/components/input-tiny-mce/index.ts (100%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/components/input-tiny-mce/input-tiny-mce.handlers.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/components/input-tiny-mce/input-tiny-mce.languages.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/components/input-tiny-mce/tiny-mce-plugin.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/index.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/manifests.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/plugins/manifests.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/plugins/tiny-mce-code-editor.plugin.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/plugins/tiny-mce-embeddedmedia.plugin.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/block/manifests.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.stories.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.test.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.test.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/manifests.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.test.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.test.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/tiny-mce/manifests.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.stories.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.test.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{ => rte}/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.test.ts (100%) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiny-mce/package.json delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiny-mce/umbraco-package.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiny-mce/vite.config.ts diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index bde89122d0..2d9d650673 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 02542051f5..65331dcba2 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts index eee608f8e6..4129f330a7 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/index.ts new file mode 100644 index 0000000000..ff0b559694 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/index.ts @@ -0,0 +1 @@ +export * from './tiptap/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts index 6b7ca9ed76..81d70617d7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.handlers.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.handlers.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.handlers.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.handlers.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.languages.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.languages.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.languages.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.languages.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/tiny-mce-plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/tiny-mce-plugin.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/tiny-mce-plugin.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/tiny-mce-plugin.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/manifests.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/manifests.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/manifests.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/manifests.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-code-editor.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-code-editor.plugin.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-code-editor.plugin.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-code-editor.plugin.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-embeddedmedia.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-embeddedmedia.plugin.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-embeddedmedia.plugin.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-embeddedmedia.plugin.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/block/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/block/manifests.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/block/manifests.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/block/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.stories.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.stories.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.test.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.test.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.test.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.test.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/manifests.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/manifests.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.test.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.test.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.test.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.test.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/manifests.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.stories.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.stories.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.test.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.test.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.test.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.test.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/vite.config.ts index 231d180c29..20ec618303 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/vite.config.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiny-mce/package.json b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/package.json deleted file mode 100644 index 6362dc5347..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiny-mce/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/umbraco-package.ts deleted file mode 100644 index a5de5f04cd..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/tiny-mce/vite.config.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/vite.config.ts deleted file mode 100644 index d0c457d1e9..0000000000 --- a/src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index 9951bfe191..94463e8e5f 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/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 453b25ec2440cda815b0b362ab1df4651c8815cb 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index ade8960b91..dd806ff0c5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 1cc43c8f034fc687fb47b9f0f16dcb63f0a6e20e 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/Umbraco.Web.UI.Client/src/packages/rte/types.ts | 1 + 3 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/types.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts index 11a10a4862..897842ce27 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index dd806ff0c5..b3794c9369 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/types.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/types.ts new file mode 100644 index 0000000000..1691cf4aea --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/types.ts @@ -0,0 +1 @@ +export const UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS = 'Umbraco.TinyMCE'; From 8f7046a27285f5bf43f49c7ccec63e1811e2e454 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/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/types.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts new file mode 100644 index 0000000000..ee6387c39f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts index 897842ce27..ea0ce31cf1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index b3794c9369..105f57d563 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/types.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/types.ts deleted file mode 100644 index 1691cf4aea..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/types.ts +++ /dev/null @@ -1 +0,0 @@ -export const UMB_BLOCK_RTE_BLOCK_LAYOUT_ALIAS = 'Umbraco.TinyMCE'; From 3c81818e7161facde502083e4b04792e8010897f 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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts index e2d3c3b877..cbc5d418b0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts +++ b/src/Umbraco.Web.UI.Client/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 a67ba2d1613ab4300b50f18a070f2512b3450851 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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts index a161e267e1..a26ab76fc0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts +++ b/src/Umbraco.Web.UI.Client/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 087eb891285d8d60d1ac75aa4df0a6e318263671 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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts index fbc5e0ebd9..e43b022d5d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index bc53032243..bdd97a523a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts index 174e14fe02..7b839a0372 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts index b8b9b82d8b..e47c74dce5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts index 9fc27a5360..5c0659620d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts index cc2238a2eb..11f550c8ab 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts index 3ea28a010d..78146a0da5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 33820ee33fe39ae1e135a183f2e9b22f365c6eb3 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/Umbraco.Web.UI.Client/src/packages/core/utils/get-processed-image-url.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/get-processed-image-url.function.ts index 674e6de91b..51a43983d5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/get-processed-image-url.function.ts +++ b/src/Umbraco.Web.UI.Client/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 a5ba9f3b4dfd104f3f5ecedde632e417cd3649cf 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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts index cbc5d418b0..8c20112d9a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts index e43b022d5d..57f799f9ed 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts index bdd97a523a..e7e4ca1556 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-mediapicker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts index 11f550c8ab..1201d39626 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 4763e01beee68c7c2622adc50862d0f23f60b928 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 ++-- .../packages/rte/tiptap/extensions/umb/link.extension.ts | 2 +- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts index 8c20112d9a..3da7c60d86 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts index 57f799f9ed..d50a6ef6e3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.sanitizer.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts index ea0ce31cf1..e84e389779 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/property-editor-ui-tiny-mce.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts index 78146a0da5..e7ef26bf85 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 521c09789c9737caed5ad22920dc716f31a0eced 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 | 189 ++++---------- .../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 - .../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, 610 insertions(+), 386 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/{block-picker.extension.ts => block-picker-toolbar.extension.ts} (61%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/embedded-media.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/link.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/list.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/blockquote.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/bold.extension.ts rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{core => toolbar}/bullet-list.extension.ts (70%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/code-block.extension.ts rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{core => toolbar}/heading1.extension.ts (80%) rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{core => toolbar}/heading2.extension.ts (80%) rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{core => toolbar}/heading3.extension.ts (80%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/horizontal-rule.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/italic.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/manifests.ts rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{core => toolbar}/ordered-list.extension.ts (100%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/strike.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/table.extension.ts rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{core => toolbar}/text-align-center.extension.ts (65%) rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{core => toolbar}/text-align-justify.extension.ts (65%) rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{core => toolbar}/text-align-left.extension.ts (65%) rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{core => toolbar}/text-align-right.extension.ts (65%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/underline.extension.ts rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{core => toolbar}/unlink.extension.ts (90%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block-picker-toolbar.extension.ts similarity index 61% rename from src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block-picker-toolbar.extension.ts index 42e48b8a7d..52a16ef213 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block-picker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block.extension.ts new file mode 100644 index 0000000000..c447be0c83 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/manifests.ts index 5b539cfb80..09d197d539 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 777be27462..21b5efde7d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 457eb7ad8c..9490ad278f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts index d1ecfdfe19..8d605978bd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts index e65d167551..824b62c874 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts index 7cfa5861b2..2c1310ba3b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/embedded-media.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/embedded-media.extension.ts new file mode 100644 index 0000000000..0e64a0153a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading.extension.ts new file mode 100644 index 0000000000..255d8832a9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts index 0219f45673..1d78bc9c96 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts index ff122f81e4..5eccccf8dd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/link.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/link.extension.ts new file mode 100644 index 0000000000..4800000fda --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/list.extension.ts new file mode 100644 index 0000000000..1391383388 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts index 3f4b66173e..8131295233 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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,11 +33,32 @@ 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.Embed', + name: 'Embed Tiptap Extension', + api: () => import('./embedded-media.extension.js'), + weight: 70, + meta: { + icon: 'icon-embed', + label: '#general_embed', + }, + }, + { + type: 'tiptapExtension', + alias: 'Umb.Tiptap.Link', + name: 'Link Tiptap Extension', + api: () => import('./link.extension.js'), + weight: 102, + meta: { + icon: 'icon-link', + label: '#defaultdialogs_urlLinkPicker', + }, + }, { type: 'tiptapExtension', alias: 'Umb.Tiptap.Figure', @@ -60,50 +66,10 @@ export const manifests: Array 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, - 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', @@ -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,63 +124,10 @@ export const manifests: Array import('./table.extension.js'), weight: 909, meta: { - alias: 'table', icon: 'icon-table', label: 'Table', }, }, - { - 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', @@ -238,23 +136,38 @@ export const manifests: Array import('./underline.extension.js'), weight: 997, meta: { - alias: 'underline', icon: 'icon-underline', label: 'Underline', }, }, { 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.Heading', + name: 'Heading Tiptap Extension', + api: () => import('./heading.extension.js'), meta: { - alias: 'unlink', - icon: 'icon-unlink', - label: 'Unlink', + icon: 'icon-heading-1', + label: 'Heading', + }, + }, + { + type: 'tiptapExtension', + alias: 'Umb.Tiptap.List', + name: 'List Tiptap Extension', + api: () => import('./list.extension.js'), + meta: { + icon: 'icon-ordered-list', + label: 'Ordered List', + }, + }, + { + 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', }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts index b073c12dd9..88096bacfe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/table.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/table.extension.ts index ae2d71fa73..d8c00b5d39 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/table.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align.extension.ts new file mode 100644 index 0000000000..3d902dd62a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts index 4e1bac6d6a..f9663f91c8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index 7fc3abe7c2..39f1379c12 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts index 5ecb61da5d..bb3dcd3f88 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-extension.ts new file mode 100644 index 0000000000..a97d658328 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/blockquote.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/blockquote.extension.ts new file mode 100644 index 0000000000..4dc06019d6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/bold.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/bold.extension.ts new file mode 100644 index 0000000000..8ee5950edd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/bullet-list.extension.ts similarity index 70% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/bullet-list.extension.ts index 8dd956279c..4212b563e0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/code-block.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/code-block.extension.ts new file mode 100644 index 0000000000..65273349ae --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/heading1.extension.ts similarity index 80% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/heading1.extension.ts index 7543e321fb..331c0b7136 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/heading2.extension.ts similarity index 80% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/heading2.extension.ts index 3edcf7b57a..cfe4304ef0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/heading3.extension.ts similarity index 80% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/heading3.extension.ts index 9def84dc2c..032c62fbf3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/horizontal-rule.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/horizontal-rule.extension.ts new file mode 100644 index 0000000000..1d7ab6456c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/italic.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/italic.extension.ts new file mode 100644 index 0000000000..6b4e7465c8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/manifests.ts new file mode 100644 index 0000000000..cc39270567 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/ordered-list.extension.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/ordered-list.extension.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/strike.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/strike.extension.ts new file mode 100644 index 0000000000..2f4f284633 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/table.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/table.extension.ts new file mode 100644 index 0000000000..ee997c7280 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-center.extension.ts similarity index 65% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-center.extension.ts index fa9c90855c..50eb9ccaf3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-justify.extension.ts similarity index 65% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-justify.extension.ts index 03e197654f..c7e71ca6a7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-left.extension.ts similarity index 65% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-left.extension.ts index 2f35da46d2..4f3b0f87d6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-right.extension.ts similarity index 65% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-right.extension.ts index 62de9a54f9..0cf0313208 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/underline.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/underline.extension.ts new file mode 100644 index 0000000000..82436dc55a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/unlink.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/unlink.extension.ts similarity index 90% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/unlink.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/unlink.extension.ts index 5645c154bc..fcf5f60c78 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/unlink.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts index 919bd3092b..d297efd984 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts index 32722af243..2c3c8c69c8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts index 9d0a1f71c8..b439a18101 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts index e7ef26bf85..d15602f0c7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 035eacd301df011e810171a93af63697a7ee4d12 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/index.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/index.ts index 20b2f7a29a..8f09498433 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/index.ts +++ b/src/Umbraco.Web.UI.Client/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 bf7c6da3917864c428ab3b5b6774726f7a9ad4f1 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/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block-picker-toolbar.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block-picker-toolbar.extension.ts index 52a16ef213..241eebbb9c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block-picker-toolbar.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block.extension.ts index c447be0c83..f60235b15e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/manifests.ts index 09d197d539..b62c0f0e5c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 94b1c7c6699d122b8e0faeb183e8cd2f461d06ea 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts index 71f28f1a7c..2d8c3c7d66 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 5de70b8af0..3569959f82 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 4e8e8efed6673f3eddbba699cffc928284fda5fa 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 | 371 +++++++++++------- 1 file changed, 227 insertions(+), 144 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index c70e4c0518..b94e768b59 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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.requestUpdate('_extensionCategories'); this.dispatchEvent(new UmbPropertyValueChangeEvent()); - } + }; - #onChange(event: CustomEvent) { - this.value = (event.target as UmbTiptapToolbarGroupsConfigurationElement).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); - }); - }); + #removeItem(from: [number, number, number]) { + const [rowIndex, groupIndex, itemIndex] = from; + this.#value[rowIndex][groupIndex].splice(itemIndex, 1); this.dispatchEvent(new UmbPropertyValueChangeEvent()); } - override render() { + #addGroup = (rowIndex: number, groupIndex: number) => { + this.#value[rowIndex].splice(groupIndex, 0, []); + this.dispatchEvent(new UmbPropertyValueChangeEvent()); + }; + + #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])}> + +
    `; + } + + #renderGroup(group: string[], rowIndex: number, groupIndex: number) { return html` - -
    - ${repeat( - this._extensionCategories, - (category) => html` -
    -

    ${category.category}

    - ${repeat( - category.extensions, - (item) => - html`
    - this.#onExtensionSelect(item)} - > - ${item.label} -
    `, - )} -
    - `, - )} -
    +
    this.#onDrop(e, [rowIndex, groupIndex, group.length])}> + ${group.map((alias, itemIndex) => this.#renderItem(alias, rowIndex, groupIndex, itemIndex))} + this.#removeGroup(rowIndex, groupIndex)}> + +
    `; } - static override readonly styles = [ + #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.#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; - } - .extension-item { - display: grid; - grid-template-columns: 36px 1fr; - grid-template-rows: 1fr; - align-items: center; - gap: 9px; - } - .category { - flex: 1; + gap: 3px; + border-radius: var(--uui-border-radius); 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); + padding: 6px; + min-height: 30px; + min-width: 30px; } - .category-name { - grid-column: 1 / -1; - margin: 0; - font-weight: bold; + .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; } `, From 39dd1c9c21b108ff6e6c289acc9cb8f4b2dc6ab9 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index b94e768b59..a2a55d22b8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 83f5496ea5fcdcfaaa1ffa529d178213b05f705d 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index a2a55d22b8..61eb550192 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 a6c0eb792322ac97e2e85defbf853ed8d5cad205 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/input-tiptap-toolbar-layout.element.ts deleted file mode 100644 index b7a404fd83..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 61eb550192..6c198df93d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 bd268e74f9c9a5bc2ceb70b0f27b3be1eac138ee 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 6c198df93d..966c818db6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 9d4fb25c77bd435399b0d84fbd15ea441e9cef34 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts index 8131295233..ded8464142 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts index bb3dcd3f88..619792c6bb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-extension.ts +++ b/src/Umbraco.Web.UI.Client/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 39efec38b6126927b1897be9e92636ea18e2499c 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts index c193554a81..35212303be 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/toolbar/tiptap-toolbar-button.element.ts +++ b/src/Umbraco.Web.UI.Client/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 506914535855658c4e77318db12dcc8e8511663a 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 --- .../packages/rte/tiptap/property-editors/tiptap/manifests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 3569959f82..1adc57a016 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 fd4a4dc76fe1ca1118d3eb792b54fd9762097e1f 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 966c818db6..5b2067ef1a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 b0b09abfa1b4c3e09140431e76eb9b9fc0e1d221 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 | 86 +++++++++++++++---- 1 file changed, 68 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts index d15602f0c7..13b907f40e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts +++ b/src/Umbraco.Web.UI.Client/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. + 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. + */ + #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; + } - // If the URL is prefixed "www.", then prepend "http://" protocol scheme. - if (url && /^\s*www\./i.test(url)) { - url = `http://${url}`; + /** + * 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; + } + + /** + * 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; } - - 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; + return queryString; } } From f158b54e6ed4225d7d634b8f522195396dd7a70a 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 19f8e726fe..29bd882061 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 42874b0dba..9490ad278f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 5b2067ef1a..e56361410f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 0df93e0a07c74b39c3511f9a5f42fb2215516b6c 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 21b5efde7d..777be27462 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 8533ae122762c52d724ffa9bba988c84b6ed8afd 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts index b513b99415..40649ae3d3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index e56361410f..fdc4b089a8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 1adc57a016..906c3b67b7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 adaae486116df8af0dd25823059e6ff6e006a40d 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 29bd882061..39736b1305 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 9490ad278f..26c0aa2030 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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 65fe592dce6e6953c8ec1b91fe37bd6775ee28a8 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index fdc4b089a8..e378f3b954 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 637d29e421b713d90f097f2019f47aaf87a3e751 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts index 6b81230c59..abc90d5d2a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 463a2d063d89a133c7b9ee1c6b3e1ed5b1126228 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-extension.ts index a97d658328..fde0edb310 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-toolbar-extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/manifests.ts index cc39270567..0780c05820 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index 39f1379c12..e147fc85c6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/manifests.ts index b62c0f0e5c..9500a28c9b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 349a89e26d3bb54d461a56d1b30a2c50f14aaa32 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 --- src/Umbraco.Web.UI.Client/package-lock.json | 14 +++++++++ src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 2d9d650673..037c333a6b 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 65331dcba2..d3b621c879 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts index f35a6596ed..3a8de51a46 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts @@ -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/Umbraco.Web.UI.Client/src/assets/lang/en.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts index 495129c5cc..e588340496 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts index e17d84cb3a..587e432476 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index c203377cf2..b2837b223c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/redo.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/undo.extension.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/manifests.ts index 898b4003b7..6ac9973d8d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/redo.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/redo.extension.ts new file mode 100644 index 0000000000..879fc90cec --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/undo.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/undo.extension.ts new file mode 100644 index 0000000000..a206efaa4e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 a6cbc7cd60fa5fdce619cbb71c9ea452c87006d3 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 +++++++------------ .../packages/rte/tiptap/extensions/types.ts | 7 ++++++- .../extensions/umb/mediapicker.extension.ts | 13 +++---------- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index b2837b223c..0e9539f206 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -140,6 +140,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin `, )} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 26c0aa2030..2ed9b7774e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts index d297efd984..ec2bcc1f8c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts index abc90d5d2a..cbca94f7ff 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 31f91e586999a27027577c7d7a2e402e2cb06158 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/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts index 435711cb3e..dfe3621241 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts @@ -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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 0e9539f206..7400057c30 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/{tiny-mce/property-editors/tiny-mce => }/Umbraco.RichText.ts (93%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/Umbraco.RichText.ts similarity index 93% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/Umbraco.RichText.ts index 3dbe86e0a0..36fb6b3c94 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/Umbraco.RichText.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts index 81d70617d7..acef781d4b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts index 4e657fa2bc..cb68e5300c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 5f235b7a3a5784d19c7f1e36901a64e1a2c22086 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 ++--- .../packages/rte/tiptap/property-editors/tiptap/manifests.ts | 4 ++-- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts index 70b2d39ad3..51322338cd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/dimensions/property-editor-ui-tiny-mce-dimensions-configuration.stories.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts index 714136611c..652aa6769e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/max-image-size/property-editor-ui-tiny-mce-maximagesize.stories.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts index 1341c768a7..0c6292c87b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.stories.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts index 29f8543717..aa598bf356 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.stories.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts index 2d8c3c7d66..9f9f7706f4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts index 40649ae3d3..5805ceee12 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 39fd9f2136..136b2f73c6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 906c3b67b7..e3c51f2e65 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 9b4a88dbf887554cec03a73d81d4cea63f851a6c 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.test.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.test.ts new file mode 100644 index 0000000000..214fa7d487 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 f8b7c13cb006e3ca12d3c8aabd210a4b20b6ab39 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.stories.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.stories.ts new file mode 100644 index 0000000000..ed597afb7a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 6d96e45e28a018f7a7ffd1e59e555e54a5f720b2 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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts index cb68e5300c..561fbceb4e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/tiny-mce/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 4558e3380d545845f6b460f24803079011392484 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/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block.extension.ts index f60235b15e..2d00aa0486 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/tiptap-extension/block.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/block/block-rte/types.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/types.ts index dca59515c5..1d0449f656 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/types.ts @@ -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/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts index 0c1b6357ad..3577ebb6a4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts +++ b/src/Umbraco.Web.UI.Client/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 9519d4a7179816be3034834285bec3343eb1e024 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 7400057c30..d8267e4024 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-link.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-link.extension.ts index 1576004644..9ba9433993 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-link.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 7b0e3a757e0f12898352d4b8e86b13301e495a30 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/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts index 2e0222f8d5..d355bb27f2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts @@ -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 c64201479ccac10d64e0378a4b362696077b5c3a 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/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-manager.context.ts index a09b598419..876dfb06d8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/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 4d859f13dbe618fd3832c308cb24f56fa64b2bac 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 --- .../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/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts index 71ec6584d6..9e142e203e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts index 54d0590c55..8d56fc1943 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/property-editor-ui-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 f49870207ba9b10ddfc76267243fd7d4444d1924 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 --- src/Umbraco.Web.UI.Client/package-lock.json | 26 +++++++++++++++++ src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-subscript.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-superscript.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/subscript.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/superscript.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/subscript.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/superscript.extension.ts diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 037c333a6b..1e261c3d8f 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index d3b621c879..cecbbf61d6 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts index 587e432476..d7daf5502a 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json index f1cffa4000..4a097fd118 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json @@ -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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts index a011051d45..9849a23c35 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts @@ -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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-science.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-science.ts index b45b7ed1d5..5c6f524a0b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-science.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-superscript.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-superscript.ts new file mode 100644 index 0000000000..e2b7852d44 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-unlink.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-unlink.ts index d770affee6..7a308ee40d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-unlink.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/subscript.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/subscript.extension.ts new file mode 100644 index 0000000000..57bfb91472 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/superscript.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/superscript.extension.ts new file mode 100644 index 0000000000..453efb13e5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/manifests.ts index 6ac9973d8d..d666f66bd8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/subscript.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/subscript.extension.ts new file mode 100644 index 0000000000..6753abec31 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/superscript.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/superscript.extension.ts new file mode 100644 index 0000000000..18bc7a7505 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/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 53310ce68e47fdd004c5a66ef880f2c7847f30df 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 156a19d1b4..021ee6d607 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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 e3d00c407653f396889cbe08d97475c4c766deb2 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 ++- .../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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index d8267e4024..95ecea0805 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 021ee6d607..8466830bc9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts index ec2bcc1f8c..927d22cf63 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/types.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 136b2f73c6..dc18221309 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 d5bf2f423e93557574faa9428e4696d768b198a7 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 +- .../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/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts index 0a6d0da85d..5e7c44ae15 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts @@ -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/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts index 9e142e203e..e775b39d3a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts index d188364b6f..03194589ba 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/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 75f50f5280831407779be8d36bcf4fc4dbb7f67e 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts index 5805ceee12..a454a02829 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 ae45f57f95c370c95ff656ac559332a41e30ea2c 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts index a454a02829..394dde72a0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 04ad91cd6ce93e4f109133b9cb596ce1b9ff5094 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index e3c51f2e65..af3774c108 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 4cd8c074e7593c1c26400c8aaedc9e841c757ca5 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 +- .../packages/rte/tiptap/plugins/manifests.ts | 4 +-- ...tiptap-extensions-configuration.element.ts | 2 +- 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts index e588340496..2a35ea59ae 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts index 487db61305..45c0784597 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index d24cdfe8b7..02fdd6dcb8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/plugins/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/plugins/manifests.ts index 463d795cfc..aafd570b89 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/plugins/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts index 394dde72a0..aa9398da88 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 97e13813f4b075c9b124e6776ad3f12938b4edd3 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts index aa9398da88..cea56a64f2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 ae426b14e960bbb88b8623fabdf5e29b303790e7 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 136b2f73c6..342e733fc9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 b1615ef5c744aa7b34137b4208a911621d7228d8 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts index cea56a64f2..aaa8fc1278 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 7638643b1799799ac08fd8b4cd6f6c2c13ecc185 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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts index a26ab76fc0..45705a7ec5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts +++ b/src/Umbraco.Web.UI.Client/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 78b494e68613b07a0fd5627932ea51868b646819 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/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts index e775b39d3a..beb9d06164 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts +++ b/src/Umbraco.Web.UI.Client/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 d8d5dff130107e19b2befee57ba6a7c3efabc3b1 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/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts index d355bb27f2..e823da7bfd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts @@ -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 7d0d97eec3647872293249d0f5c43e33395bd264 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/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts index beb9d06164..c7e01a7dd5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts +++ b/src/Umbraco.Web.UI.Client/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 7ec5961444ec543a52314ff318271b3496c2b82e 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 8466830bc9..fd1e1e6099 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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 58a832de7a32e5017c2a4a7ba3742588d9f7ae33 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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts index 03194589ba..4cdabebefb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/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 dc1d69e854b37d1107d3467ccd7358ef1973a9f1 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 95ecea0805..f82d551198 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index e3c51f2e65..0dfc51e663 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 d72b473dfceb45957b99f1da044ce4ac541c3ca0 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts index 13b907f40e..bbf928d420 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 0dfc51e663..acdc401c54 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 147b0b1dd92483a1e051c5d856f8bfa52effe6cd 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index f82d551198..cb1d68b538 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index fd1e1e6099..37c837f0ce 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index dc18221309..d0dfaa4a6a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 de5f7cbf898ac78e4484aadbf61140845b20f130 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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts index 4cdabebefb..ddfc1e5a01 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/plugins/tiny-mce-block-picker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/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 5da83b7af9233d0420cc0c43b5825e265ff6ebb8 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 --- .../packages/rte/tiptap/property-editors/tiptap/manifests.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index acdc401c54..3efdc1912d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 817423e3297a843122f84582d9419f32daa0d359 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts index 45c0784597..88bd91bcf0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 1e63126c820602efb103c8793244dc92ba799197 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 3efdc1912d..67edb85388 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 fd69b63cdfb59ab90aa70ed546aa656a43408ccf 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 | 185 +++++++++--------- .../property-editors/tiptap/manifests.ts | 18 ++ 6 files changed, 130 insertions(+), 132 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index 02fdd6dcb8..c5daba1fa6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 7e28112c18..19d3a5d9a4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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 { - @property({ attribute: false }) - set value(value: UmbTiptapToolbarValue | undefined) { - if (!value) { - this.#useDefault = true; - this.#value = [[[]]]; - return; - } - - // 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[] = []; + #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.#value = [[[]]]; + } 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)))); + } + } + get value(): UmbTiptapToolbarValue { + // TODO: This can be optimized with cashing; + return this.#value.map((rows) => rows.map((groups) => [...groups])); + } + #value: UmbTiptapToolbarValue = [[[]]]; + 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 - this.#value[rowIndex][groupIndex] = []; - } else { - this.#value[rowIndex].splice(groupIndex, 1); + 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] = []; + } + this.dispatchEvent(new UmbPropertyValueChangeEvent()); }; @@ -155,26 +151,46 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement }; #removeRow = (rowIndex: number) => { - if (rowIndex === 0) { - // Prevent removing the last row - this.#value[rowIndex] = [[]]; - } else { - this.#value.splice(rowIndex, 1); + 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] = [[]]; + } + 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 67edb85388..e3adef4605 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 d696d549ba0cd18c37f12a08c42ea0e67f28cc4f 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. --- src/Umbraco.Web.UI.Client/package-lock.json | 191 ++++++++++-------- src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading.extension.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/list.extension.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 1e261c3d8f..a11cae44d9 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/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/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 1c1d071407..5a8122f56e 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts index d7daf5502a..16db5db676 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index cb1d68b538..631d04fc9e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts deleted file mode 100644 index 8d605978bd..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts deleted file mode 100644 index 824b62c874..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts deleted file mode 100644 index 2c1310ba3b..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading.extension.ts deleted file mode 100644 index 255d8832a9..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts deleted file mode 100644 index 1d78bc9c96..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts deleted file mode 100644 index 5eccccf8dd..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/list.extension.ts deleted file mode 100644 index 1391383388..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts index 88bd91bcf0..12ff89ce56 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts deleted file mode 100644 index 88096bacfe..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index c5daba1fa6..9effd9786d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 8825cccef02990b09c96b552dab78c5c7bbc4b6d 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 631d04fc9e..786045550d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 | 19 +- .../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, 463 insertions(+), 478 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{umb => core}/media-upload.extension.ts (87%) rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{umb => toolbar}/embedded-media.extension.ts (92%) rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{umb => toolbar}/link.extension.ts (97%) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/manifests.ts rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{umb/mediapicker.extension.ts => toolbar/media-picker.extension.ts} (92%) rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{umb/code-editor.extension.ts => toolbar/source-editor.extension.ts} (84%) rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/{umb => toolbar}/style-select.extension.ts (71%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts deleted file mode 100644 index 12ff89ce56..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/media-upload.extension.ts similarity index 87% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/media-upload.extension.ts index ac8fd6eb4f..1af085137c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/media-upload.extension.ts +++ b/src/Umbraco.Web.UI.Client/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 { 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 { 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 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align.extension.ts index 3d902dd62a..9855c4d150 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index 9effd9786d..200802b943 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 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.Bold', + name: 'Bold Tiptap Extension', + api: () => import('./toolbar/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('./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.CodeEditor', - name: 'Code Editor Tiptap Extension', - api: () => import('./umb/code-editor.extension.js'), + alias: 'Umb.Tiptap.Toolbar.SourceEditor', + name: 'Source Editor Tiptap Extension', + api: () => import('./toolbar/source-editor.extension.js'), meta: { - alias: 'umb-code-editor', - icon: 'icon-code', + 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/blockquote.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/blockquote.extension.ts index 4dc06019d6..f061e7e6b6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/blockquote.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/bold.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/bold.extension.ts index 8ee5950edd..5ec1124963 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/bold.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/bullet-list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/bullet-list.extension.ts index 4212b563e0..983adb0c3d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/bullet-list.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/code-block.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/code-block.extension.ts index 65273349ae..a3a30b0f5d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/code-block.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/embedded-media.extension.ts similarity index 92% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/embedded-media.extension.ts index b439a18101..b50872c1f0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/embedded-media.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/heading1.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/heading1.extension.ts index 331c0b7136..b301f438e1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/heading1.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/heading2.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/heading2.extension.ts index cfe4304ef0..663bc09d22 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/heading2.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/heading3.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/heading3.extension.ts index 032c62fbf3..3c3aed7cba 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/heading3.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/horizontal-rule.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/horizontal-rule.extension.ts index 1d7ab6456c..58836d2257 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/horizontal-rule.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/italic.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/italic.extension.ts index 6b4e7465c8..2ac2c77649 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/italic.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/link.extension.ts similarity index 97% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/link.extension.ts index bbf928d420..d78648f6fb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/link.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/manifests.ts deleted file mode 100644 index 9c4dfc5068..0000000000 --- a/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/media-picker.extension.ts similarity index 92% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/media-picker.extension.ts index cbca94f7ff..0ba24357bd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/mediapicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/ordered-list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/ordered-list.extension.ts index 471d794e1c..f95d7da3d2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/ordered-list.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/redo.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/redo.extension.ts index 879fc90cec..0aae301bba 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/redo.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/source-editor.extension.ts similarity index 84% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/source-editor.extension.ts index 2c3c8c69c8..7ff4fcd4ca 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/code-editor.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/strike.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/strike.extension.ts index 2f4f284633..1428e2009b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/strike.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/style-select.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/style-select.extension.ts similarity index 71% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/style-select.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/style-select.extension.ts index 6429d7021d..96868aa349 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/umb/style-select.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/subscript.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/subscript.extension.ts index 6753abec31..121e5210a6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/subscript.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/superscript.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/superscript.extension.ts index 18bc7a7505..d46e966d29 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/superscript.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/table.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/table.extension.ts index ee997c7280..3ef38f6f13 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/table.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-center.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-center.extension.ts index 50eb9ccaf3..a027bc18c1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-center.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-justify.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-justify.extension.ts index c7e71ca6a7..38a53fc008 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-justify.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-left.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-left.extension.ts index 4f3b0f87d6..86b90b7e81 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-left.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-right.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-right.extension.ts index 0cf0313208..8f3984bb5c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/text-align-right.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/underline.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/underline.extension.ts index 82436dc55a..9a2aab8b50 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/underline.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/undo.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/undo.extension.ts index a206efaa4e..1cc600442b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/undo.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/unlink.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/unlink.extension.ts index fcf5f60c78..a3137f6ec5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/toolbar/unlink.extension.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index e3adef4605..b5797e180a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 98e5a9d9525e4b690be8aef0be9c63e2519e3563 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts index 19d3a5d9a4..079bb9ea49 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-code-xml.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json index 4a097fd118..a249ba32dc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json @@ -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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts index 9849a23c35..a153ffeff8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts @@ -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/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-code-xml.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-code-xml.ts new file mode 100644 index 0000000000..22e1bb6911 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-code-xml.ts @@ -0,0 +1,16 @@ +export default ` + + + + + +`; \ No newline at end of file From 280c5093eb424a144e285431840293b274230657 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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/{ => tiptap/components}/property-editor-ui-tiptap-extensions-configuration.element.ts (97%) rename src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/{ => tiptap/components}/property-editor-ui-tiptap-toolbar-configuration.element.ts (99%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts index 9f9f7706f4..734619f0cf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/components/property-editor-ui-tiptap-extensions-configuration.element.ts similarity index 97% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/components/property-editor-ui-tiptap-extensions-configuration.element.ts index 583cda4c56..d020ec400d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/components/property-editor-ui-tiptap-toolbar-configuration.element.ts similarity index 99% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/components/property-editor-ui-tiptap-toolbar-configuration.element.ts index 079bb9ea49..4453ff1146 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/property-editor-ui-tiptap-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index b5797e180a..85751a607f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 457315737cf36a9362cf69f9f1b8e4e57528c551 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 ++-- .../packages/rte/tiptap/property-editors/tiptap/manifests.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index 200802b943..ed8f9f7f56 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts index 85751a607f..a2791c4935 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/property-editors/tiptap/manifests.ts +++ b/src/Umbraco.Web.UI.Client/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 e64f94e2e5f71effdb213392553551f5795967d8 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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts index 873121f6dc..a830297e36 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/block/property-editor-ui-block-rte-type-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts index 98f4510c25..a0efa34c22 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/stylesheets/property-editor-ui-tiny-mce-stylesheets-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts index a7d2208b1b..9559126d33 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiny-mce/property-editors/toolbar/property-editor-ui-tiny-mce-toolbar-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/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/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 786045550d..dee7a0609d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/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 {