Merge remote-tracking branch 'origin/v15/feature/tiptap' into tiptap/feat/toolbar
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
export default `<!-- @license lucide-static v0.424.0 - ISC -->
|
||||
<svg
|
||||
class="lucide lucide-text-quote"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M17 6H3" />
|
||||
<path d="M21 12H8" />
|
||||
<path d="M21 18H8" />
|
||||
<path d="M3 12v6" />
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,14 @@
|
||||
export default `<!-- @license lucide-static v0.424.0 - ISC -->
|
||||
<svg
|
||||
class="lucide lucide-bold"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8" />
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,17 @@
|
||||
export default `<!-- @license lucide-static v0.424.0 - ISC -->
|
||||
<svg
|
||||
class="lucide lucide-monitor-play"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M10 7.75a.75.75 0 0 1 1.142-.638l3.664 2.249a.75.75 0 0 1 0 1.278l-3.664 2.25a.75.75 0 0 1-1.142-.64z" />
|
||||
<path d="M12 17v4" />
|
||||
<path d="M8 21h8" />
|
||||
<rect x="2" y="3" width="20" height="14" rx="2" />
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,17 @@
|
||||
export default `<!-- @license lucide-static v0.424.0 - ISC -->
|
||||
<svg
|
||||
class="lucide lucide-heading-1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M4 12h8" />
|
||||
<path d="M4 18V6" />
|
||||
<path d="M12 18V6" />
|
||||
<path d="m17 12 3-2v8" />
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,17 @@
|
||||
export default `<!-- @license lucide-static v0.424.0 - ISC -->
|
||||
<svg
|
||||
class="lucide lucide-heading-2"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M4 12h8" />
|
||||
<path d="M4 18V6" />
|
||||
<path d="M12 18V6" />
|
||||
<path d="M21 18h-4c0-4 4-3 4-6 0-1.5-2-2.5-4-1" />
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,18 @@
|
||||
export default `<!-- @license lucide-static v0.424.0 - ISC -->
|
||||
<svg
|
||||
class="lucide lucide-heading-3"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M4 12h8" />
|
||||
<path d="M4 18V6" />
|
||||
<path d="M12 18V6" />
|
||||
<path d="M17.5 10.5c1.7-1 3.5 0 3.5 1.5a2 2 0 0 1-2 2" />
|
||||
<path d="M17 17.5c2 1.5 4 .3 4-1.5a2 2 0 0 0-2-2" />
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,16 @@
|
||||
export default `<!-- @license lucide-static v0.424.0 - ISC -->
|
||||
<svg
|
||||
class="lucide lucide-separator-horizontal"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<line x1="3" x2="21" y1="12" y2="12" />
|
||||
<polyline points="8 8 12 4 16 8" />
|
||||
<polyline points="16 16 12 20 8 16" />
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,16 @@
|
||||
export default `<!-- @license lucide-static v0.424.0 - ISC -->
|
||||
<svg
|
||||
class="lucide lucide-italic"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<line x1="19" x2="10" y1="4" y2="4" />
|
||||
<line x1="14" x2="5" y1="20" y2="20" />
|
||||
<line x1="15" x2="9" y1="4" y2="20" />
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,16 @@
|
||||
export default `<!-- @license lucide-static v0.424.0 - ISC -->
|
||||
<svg
|
||||
class="lucide lucide-strikethrough"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M16 4H9a3 3 0 0 0-2.83 4" />
|
||||
<path d="M14 12a4 4 0 0 1 0 8H6" />
|
||||
<line x1="4" x2="20" y1="12" y2="12" />
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,16 @@
|
||||
export default `<!-- @license lucide-static v0.424.0 - ISC -->
|
||||
<svg
|
||||
class="lucide lucide-align-center"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<line x1="21" x2="3" y1="6" y2="6" />
|
||||
<line x1="17" x2="7" y1="12" y2="12" />
|
||||
<line x1="19" x2="5" y1="18" y2="18" />
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,16 @@
|
||||
export default `<!-- @license lucide-static v0.424.0 - ISC -->
|
||||
<svg
|
||||
class="lucide lucide-align-justify"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<line x1="3" x2="21" y1="6" y2="6" />
|
||||
<line x1="3" x2="21" y1="12" y2="12" />
|
||||
<line x1="3" x2="21" y1="18" y2="18" />
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,16 @@
|
||||
export default `<!-- @license lucide-static v0.424.0 - ISC -->
|
||||
<svg
|
||||
class="lucide lucide-align-left"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<line x1="21" x2="3" y1="6" y2="6" />
|
||||
<line x1="15" x2="3" y1="12" y2="12" />
|
||||
<line x1="17" x2="3" y1="18" y2="18" />
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,16 @@
|
||||
export default `<!-- @license lucide-static v0.424.0 - ISC -->
|
||||
<svg
|
||||
class="lucide lucide-align-right"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<line x1="21" x2="3" y1="6" y2="6" />
|
||||
<line x1="21" x2="9" y1="12" y2="12" />
|
||||
<line x1="21" x2="7" y1="18" y2="18" />
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,15 @@
|
||||
export default `<!-- @license lucide-static v0.424.0 - ISC -->
|
||||
<svg
|
||||
class="lucide lucide-underline"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M6 4v6a6 6 0 0 0 12 0V4" />
|
||||
<line x1="4" x2="20" y1="20" y2="20" />
|
||||
</svg>
|
||||
`;
|
||||
@@ -1,268 +0,0 @@
|
||||
import { html } from '@umbraco-cms/backoffice/external/lit';
|
||||
|
||||
const iconSize = '16px';
|
||||
export const bold = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-bold">
|
||||
<path d="M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8" />
|
||||
</svg>`;
|
||||
|
||||
export const italic = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-italic">
|
||||
<line x1="19" x2="10" y1="4" y2="4" />
|
||||
<line x1="14" x2="5" y1="20" y2="20" />
|
||||
<line x1="15" x2="9" y1="4" y2="20" />
|
||||
</svg>`;
|
||||
|
||||
export const underline = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-underline">
|
||||
<path d="M6 4v6a6 6 0 0 0 12 0V4" />
|
||||
<line x1="4" x2="20" y1="20" y2="20" />
|
||||
</svg>`;
|
||||
|
||||
export const strikethrough = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-strikethrough">
|
||||
<path d="M16 4H9a3 3 0 0 0-2.83 4" />
|
||||
<path d="M14 12a4 4 0 0 1 0 8H6" />
|
||||
<line x1="4" x2="20" y1="12" y2="12" />
|
||||
</svg>`;
|
||||
export const heading1 = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-heading-1">
|
||||
<path d="M4 12h8" />
|
||||
<path d="M4 18V6" />
|
||||
<path d="M12 18V6" />
|
||||
<path d="m17 12 3-2v8" />
|
||||
</svg>`;
|
||||
export const heading2 = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-heading-2">
|
||||
<path d="M4 12h8" />
|
||||
<path d="M4 18V6" />
|
||||
<path d="M12 18V6" />
|
||||
<path d="M21 18h-4c0-4 4-3 4-6 0-1.5-2-2.5-4-1" />
|
||||
</svg>`;
|
||||
export const heading3 = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-heading-3">
|
||||
<path d="M4 12h8" />
|
||||
<path d="M4 18V6" />
|
||||
<path d="M12 18V6" />
|
||||
<path d="M17.5 10.5c1.7-1 3.5 0 3.5 1.5a2 2 0 0 1-2 2" />
|
||||
<path d="M17 17.5c2 1.5 4 .3 4-1.5a2 2 0 0 0-2-2" />
|
||||
</svg>`;
|
||||
export const blockquote = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-text-quote">
|
||||
<path d="M17 6H3" />
|
||||
<path d="M21 12H8" />
|
||||
<path d="M21 18H8" />
|
||||
<path d="M3 12v6" />
|
||||
</svg>`;
|
||||
export const code = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-code">
|
||||
<polyline points="16 18 22 12 16 6" />
|
||||
<polyline points="8 6 2 12 8 18" />
|
||||
</svg>`;
|
||||
export const bulletList = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-list">
|
||||
<line x1="8" x2="21" y1="6" y2="6" />
|
||||
<line x1="8" x2="21" y1="12" y2="12" />
|
||||
<line x1="8" x2="21" y1="18" y2="18" />
|
||||
<line x1="3" x2="3.01" y1="6" y2="6" />
|
||||
<line x1="3" x2="3.01" y1="12" y2="12" />
|
||||
<line x1="3" x2="3.01" y1="18" y2="18" />
|
||||
</svg>`;
|
||||
export const orderedList = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-list-ordered">
|
||||
<line x1="10" x2="21" y1="6" y2="6" />
|
||||
<line x1="10" x2="21" y1="12" y2="12" />
|
||||
<line x1="10" x2="21" y1="18" y2="18" />
|
||||
<path d="M4 6h1v4" />
|
||||
<path d="M4 10h2" />
|
||||
<path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1" />
|
||||
</svg>`;
|
||||
export const horizontalRule = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-separator-horizontal">
|
||||
<line x1="3" x2="21" y1="12" y2="12" />
|
||||
<polyline points="8 8 12 4 16 8" />
|
||||
<polyline points="16 16 12 20 8 16" />
|
||||
</svg>`;
|
||||
export const alignLeft = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-align-left">
|
||||
<line x1="21" x2="3" y1="6" y2="6" />
|
||||
<line x1="15" x2="3" y1="12" y2="12" />
|
||||
<line x1="17" x2="3" y1="18" y2="18" />
|
||||
</svg>`;
|
||||
export const alignCenter = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-align-center">
|
||||
<line x1="21" x2="3" y1="6" y2="6" />
|
||||
<line x1="17" x2="7" y1="12" y2="12" />
|
||||
<line x1="19" x2="5" y1="18" y2="18" />
|
||||
</svg>`;
|
||||
export const alignRight = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-align-right">
|
||||
<line x1="21" x2="3" y1="6" y2="6" />
|
||||
<line x1="21" x2="9" y1="12" y2="12" />
|
||||
<line x1="21" x2="7" y1="18" y2="18" />
|
||||
</svg>`;
|
||||
export const alignJustify = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-align-justify">
|
||||
<line x1="3" x2="21" y1="6" y2="6" />
|
||||
<line x1="3" x2="21" y1="12" y2="12" />
|
||||
<line x1="3" x2="21" y1="18" y2="18" />
|
||||
</svg>`;
|
||||
|
||||
export const link = html`<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width=${iconSize}
|
||||
height=${iconSize}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-link">
|
||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
|
||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
|
||||
</svg>`;
|
||||
@@ -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';
|
||||
@@ -34,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<string, typeof UmbLitElement, string>(UmbLitElement) {
|
||||
#requiredExtensions = [Document, Dropcursor, Gapcursor, HardBreak, History, Paragraph, Text];
|
||||
|
||||
@@ -88,26 +76,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin<string, typeof Um
|
||||
this._editor = new Editor({
|
||||
element: element,
|
||||
editable: !this.readonly,
|
||||
extensions: [
|
||||
...this.#requiredExtensions,
|
||||
Blockquote,
|
||||
Bold,
|
||||
BulletList,
|
||||
Code,
|
||||
CodeBlock,
|
||||
Heading,
|
||||
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'],
|
||||
}),
|
||||
Underline,
|
||||
...extensions,
|
||||
],
|
||||
extensions: [...this.#requiredExtensions, ...extensions],
|
||||
content: this.value,
|
||||
onUpdate: ({ editor }) => {
|
||||
this.value = editor.getHTML();
|
||||
@@ -142,6 +111,14 @@ export class UmbInputTiptapElement extends UmbFormControlMixin<string, typeof Um
|
||||
}
|
||||
}
|
||||
|
||||
.tiptap {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
white-space: pre-wrap;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#editor {
|
||||
overflow: auto;
|
||||
border-radius: var(--uui-border-radius);
|
||||
@@ -155,50 +132,42 @@ export class UmbInputTiptapElement extends UmbFormControlMixin<string, typeof Um
|
||||
width: 100%;
|
||||
min-height: 400px;
|
||||
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);
|
||||
overflow-x: auto;
|
||||
}
|
||||
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);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
#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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,144 +1,15 @@
|
||||
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 { 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';
|
||||
|
||||
@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;
|
||||
|
||||
@state()
|
||||
actions: Array<UmbTiptapToolbarButton> = [
|
||||
// TODO: I don't think we need a paragraph button. It's the default state.
|
||||
// {
|
||||
// name: 'paragraph',
|
||||
// icon: html`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">
|
||||
// <path fill="none" d="M0 0h24v24H0z" />
|
||||
// <path d="M12 6v15h-2v-5a6 6 0 1 1 0-12h10v2h-3v15h-2V6h-3zm-2 0a4 4 0 1 0 0 8V6z" fill="currentColor" />
|
||||
// </svg>`,
|
||||
// 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;
|
||||
@@ -146,35 +17,14 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
#editor?: Editor;
|
||||
|
||||
#onUpdate = () => {
|
||||
this.requestUpdate();
|
||||
};
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
${this.actions.map(
|
||||
(action) => html`
|
||||
<button
|
||||
class=${action.isActive?.(this.editor) ? 'active' : ''}
|
||||
title=${action.name}
|
||||
@click=${() => action.command(this.editor)}>
|
||||
${when(
|
||||
typeof action.icon === 'string',
|
||||
() => html`<umb-icon name=${action.icon}></umb-icon>`,
|
||||
() => action.icon,
|
||||
)}
|
||||
</button>
|
||||
`,
|
||||
)}
|
||||
<umb-extension-with-api-slot
|
||||
type="tiptapExtension"
|
||||
.filter=${(ext: ManifestTiptapExtension) => !!ext.kind || !!ext.element}
|
||||
@@ -187,10 +37,13 @@ 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;
|
||||
grid-template-columns: repeat(auto-fill, minmax(24px, 1fr));
|
||||
gap: 4px;
|
||||
position: sticky;
|
||||
top: -25px;
|
||||
left: 0px;
|
||||
@@ -202,42 +55,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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`<uui-popover-container></uui-popover-container>`;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
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';
|
||||
|
||||
const elementName = 'umb-tiptap-toolbar-button';
|
||||
|
||||
@customElement(elementName)
|
||||
export class UmbTiptapToolbarButtonElement extends UmbLitElement {
|
||||
public api?: UmbTiptapToolbarElementApi;
|
||||
public editor?: Editor;
|
||||
public manifest?: ManifestTiptapExtensionButtonKind;
|
||||
|
||||
@state()
|
||||
private _isActive = false;
|
||||
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
if (this.editor) {
|
||||
this.editor.on('selectionUpdate', this.#onEditorUpdate);
|
||||
this.editor.on('update', this.#onEditorUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
override disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
|
||||
if (this.editor) {
|
||||
this.editor.off('selectionUpdate', this.#onEditorUpdate);
|
||||
this.editor.off('update', this.#onEditorUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
#onEditorUpdate = () => {
|
||||
if (this.api && this.editor && this.manifest) {
|
||||
this._isActive = this.api.isActive(this.editor);
|
||||
}
|
||||
};
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<uui-button
|
||||
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,
|
||||
() => html`<umb-icon name=${this.manifest!.meta.icon}></umb-icon>`,
|
||||
() => html`<span>${this.manifest?.meta.label}</span>`,
|
||||
)}
|
||||
</uui-button>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
export { UmbTiptapToolbarButtonElement as element };
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
[elementName]: UmbTiptapToolbarButtonElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { UmbTiptapToolbarElementApiBase } 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 {
|
||||
getTiptapExtensions = () => [Blockquote];
|
||||
|
||||
override execute(editor?: Editor) {
|
||||
editor?.chain().focus().toggleBlockquote().run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { UmbTiptapToolbarElementApiBase } 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 {
|
||||
getTiptapExtensions = () => [Bold];
|
||||
|
||||
override execute(editor?: Editor) {
|
||||
editor?.chain().focus().toggleBold().run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { UmbTiptapToolbarElementApiBase } 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 {
|
||||
getTiptapExtensions = () => [Code, CodeBlock];
|
||||
|
||||
override execute(editor?: Editor) {
|
||||
// editor.chain().focus().toggleCode().run();
|
||||
editor?.chain().focus().toggleCodeBlock().run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
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;
|
||||
}
|
||||
|
||||
override execute(editor?: Editor) {
|
||||
editor?.chain().focus().toggleHeading({ level: 1 }).run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
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;
|
||||
}
|
||||
|
||||
override execute(editor?: Editor) {
|
||||
editor?.chain().focus().toggleHeading({ level: 2 }).run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
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;
|
||||
}
|
||||
|
||||
override execute(editor?: Editor) {
|
||||
editor?.chain().focus().toggleHeading({ level: 3 }).run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { UmbTiptapToolbarElementApiBase } 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 {
|
||||
getTiptapExtensions = () => [HorizontalRule];
|
||||
|
||||
override execute(editor?: Editor) {
|
||||
editor?.chain().focus().setHorizontalRule().run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { UmbTiptapToolbarElementApiBase } from '../types.js';
|
||||
import { UmbImage } from '@umbraco-cms/backoffice/external/tiptap';
|
||||
|
||||
export default class UmbTiptapImageExtensionApi extends UmbTiptapToolbarElementApiBase {
|
||||
getTiptapExtensions() {
|
||||
return [UmbImage.configure({ inline: true })];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { UmbTiptapToolbarElementApiBase } 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 {
|
||||
getTiptapExtensions = () => [Italic];
|
||||
|
||||
override execute(editor?: Editor) {
|
||||
editor?.chain().focus().toggleItalic().run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
import type { ManifestTiptapExtension, ManifestTiptapExtensionButtonKind } from '../tiptap-extension.js';
|
||||
|
||||
export const manifests: Array<ManifestTiptapExtension | 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',
|
||||
},
|
||||
},
|
||||
{
|
||||
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',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,11 @@
|
||||
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 {
|
||||
getTiptapExtensions = () => [OrderedList, ListItem];
|
||||
|
||||
override execute(editor?: Editor) {
|
||||
editor?.chain().focus().toggleOrderedList().run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { UmbTiptapToolbarElementApiBase } 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 {
|
||||
getTiptapExtensions = () => [Strike];
|
||||
|
||||
override execute(editor?: Editor) {
|
||||
editor?.chain().focus().toggleStrike().run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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;
|
||||
}
|
||||
|
||||
override execute(editor?: Editor) {
|
||||
editor?.chain().focus().setTextAlign('center').run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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;
|
||||
}
|
||||
|
||||
override execute(editor?: Editor) {
|
||||
editor?.chain().focus().setTextAlign('justify').run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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;
|
||||
}
|
||||
|
||||
override execute(editor?: Editor) {
|
||||
editor?.chain().focus().setTextAlign('left').run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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;
|
||||
}
|
||||
|
||||
override execute(editor?: Editor) {
|
||||
editor?.chain().focus().setTextAlign('right').run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { UmbTiptapToolbarElementApiBase } 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 {
|
||||
getTiptapExtensions = () => [Underline];
|
||||
|
||||
override execute(editor?: Editor) {
|
||||
editor?.chain().focus().toggleUnderline().run();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +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 type { ManifestTypes, UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
const kinds: Array<UmbExtensionManifestKind> = [
|
||||
@@ -8,33 +9,63 @@ const kinds: Array<UmbExtensionManifestKind> = [
|
||||
matchKind: 'button',
|
||||
matchType: 'tiptapExtension',
|
||||
manifest: {
|
||||
element: () => import('./tiptap-toolbar-button.element.js'),
|
||||
element: () => import('../components/toolbar/tiptap-toolbar-button.element.js'),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const extensions: Array<ManifestTiptapExtension> = [
|
||||
const umbExtensions: Array<ManifestTiptapExtensionButtonKind> = [
|
||||
{
|
||||
type: 'tiptapExtension',
|
||||
alias: 'Umb.Tiptap.Image',
|
||||
name: 'Image Tiptap Extension',
|
||||
kind: 'button',
|
||||
alias: 'Umb.Tiptap.CodeEditor',
|
||||
name: 'Code Editor Tiptap Extension',
|
||||
api: () => import('./umb/code-editor.extension.js'),
|
||||
weight: 1000,
|
||||
api: () => import('./tiptap-image.extension.js'),
|
||||
meta: {},
|
||||
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',
|
||||
weight: 900,
|
||||
api: () => import('./tiptap-mediapicker.extension.js'),
|
||||
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<ManifestTiptapExtension> = [...core, ...umbExtensions];
|
||||
|
||||
export const manifests: Array<ManifestTypes | UmbExtensionManifestKind> = [...kinds, ...extensions];
|
||||
|
||||
@@ -8,8 +8,9 @@ export interface ManifestTiptapExtension<MetaType extends MetaTiptapExtension =
|
||||
meta: MetaType;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||
export interface MetaTiptapExtension {}
|
||||
export interface MetaTiptapExtension {
|
||||
alias: string;
|
||||
}
|
||||
|
||||
export interface ManifestTiptapExtensionButtonKind<
|
||||
MetaType extends MetaTiptapExtensionButtonKind = MetaTiptapExtensionButtonKind,
|
||||
@@ -19,13 +20,12 @@ export interface ManifestTiptapExtensionButtonKind<
|
||||
}
|
||||
|
||||
export interface MetaTiptapExtensionButtonKind extends MetaTiptapExtension {
|
||||
alias: string;
|
||||
icon: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface UmbExtensionManifestMap {
|
||||
tiptapExtension: ManifestTiptapExtension;
|
||||
tiptapExtension: ManifestTiptapExtension | ManifestTiptapExtensionButtonKind;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 })];
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
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`
|
||||
<uui-button compact label=${ifDefined(this.manifest?.meta.label)} @click=${() => this.api?.execute(this.editor)}>
|
||||
${when(
|
||||
this.manifest?.meta.icon,
|
||||
() => html`<umb-icon name=${this.manifest!.meta.icon}></umb-icon>`,
|
||||
() => html`<span>${this.manifest?.meta.label}</span>`,
|
||||
)}
|
||||
</uui-button>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
export { UmbTiptapToolbarButtonElement as element };
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
[elementName]: UmbTiptapToolbarButtonElement;
|
||||
}
|
||||
}
|
||||
@@ -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<Extension | Mark | Node>;
|
||||
}
|
||||
|
||||
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<Extension | Mark | Node>;
|
||||
}
|
||||
|
||||
export interface UmbTiptapToolbarButton {
|
||||
name: string;
|
||||
icon: string | TemplateResult;
|
||||
isActive: (editor?: Editor) => boolean | undefined;
|
||||
command: (editor?: Editor) => boolean | undefined | void | Promise<boolean> | Promise<undefined> | Promise<void>;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
import { UmbTiptapExtensionApi } from './types.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 default class UmbTiptapMediaPickerPlugin extends UmbTiptapExtensionApi {
|
||||
export default class UmbTiptapMediaPickerExtensionApi extends UmbTiptapToolbarElementApiBase {
|
||||
getTiptapExtensions() {
|
||||
return [
|
||||
Node.create({
|
||||
name: 'umbMediaPicker',
|
||||
name: 'umb-media',
|
||||
priority: 1000,
|
||||
group: 'block',
|
||||
marks: '',
|
||||
@@ -33,7 +33,9 @@ export default class UmbTiptapMediaPickerPlugin extends UmbTiptapExtensionApi {
|
||||
];
|
||||
}
|
||||
|
||||
//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);
|
||||
@@ -0,0 +1,27 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user