payload-intl 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -0
- package/dist/components/MessageController.d.ts +14 -0
- package/dist/components/MessageController.js +64 -0
- package/dist/components/MessageController.js.map +1 -0
- package/dist/components/MessagesForm.d.ts +11 -0
- package/dist/components/MessagesForm.js +91 -0
- package/dist/components/MessagesForm.js.map +1 -0
- package/dist/components/actions/JsonImport.d.ts +1 -0
- package/dist/components/actions/Move.d.ts +3 -0
- package/dist/components/inputs/InputWrapper.d.ts +7 -0
- package/dist/components/inputs/InputWrapper.js +23 -0
- package/dist/components/inputs/InputWrapper.js.map +1 -0
- package/dist/components/inputs/MessageInput.d.ts +10 -0
- package/dist/components/inputs/MessageInput.js +52 -0
- package/dist/components/inputs/MessageInput.js.map +1 -0
- package/dist/components/inputs/RichTextInput.d.ts +8 -0
- package/dist/components/inputs/RichTextInput.js +81 -0
- package/dist/components/inputs/RichTextInput.js.map +1 -0
- package/dist/components/inputs/toolbar/AlignmentControls.d.ts +5 -0
- package/dist/components/inputs/toolbar/AlignmentControls.js +59 -0
- package/dist/components/inputs/toolbar/AlignmentControls.js.map +1 -0
- package/dist/components/inputs/toolbar/BlockElementSelect.d.ts +5 -0
- package/dist/components/inputs/toolbar/BlockElementSelect.js +129 -0
- package/dist/components/inputs/toolbar/BlockElementSelect.js.map +1 -0
- package/dist/components/inputs/toolbar/LinkEditor.d.ts +58 -0
- package/dist/components/inputs/toolbar/LinkEditor.js +230 -0
- package/dist/components/inputs/toolbar/LinkEditor.js.map +1 -0
- package/dist/components/inputs/toolbar/MarkControls.d.ts +5 -0
- package/dist/components/inputs/toolbar/MarkControls.js +47 -0
- package/dist/components/inputs/toolbar/MarkControls.js.map +1 -0
- package/dist/components/inputs/toolbar/RichTextToolbar.d.ts +6 -0
- package/dist/components/inputs/toolbar/RichTextToolbar.js +32 -0
- package/dist/components/inputs/toolbar/RichTextToolbar.js.map +1 -0
- package/dist/components/inputs/variables/VariableChip.d.ts +2 -0
- package/dist/components/inputs/variables/VariableChip.js +60 -0
- package/dist/components/inputs/variables/VariableChip.js.map +1 -0
- package/dist/components/inputs/variables/VariableSuggestion.d.ts +8 -0
- package/dist/components/inputs/variables/VariableSuggestion.js +40 -0
- package/dist/components/inputs/variables/VariableSuggestion.js.map +1 -0
- package/dist/components/inputs/variables/editors/DateVariableEditor.d.ts +9 -0
- package/dist/components/inputs/variables/editors/PluralVariableEditor.d.ts +9 -0
- package/dist/components/inputs/variables/editors/PluralVariableEditor.js +149 -0
- package/dist/components/inputs/variables/editors/PluralVariableEditor.js.map +1 -0
- package/dist/components/inputs/variables/editors/SelectVariableEditor.d.ts +6 -0
- package/dist/components/inputs/variables/editors/SelectVariableEditor.js +42 -0
- package/dist/components/inputs/variables/editors/SelectVariableEditor.js.map +1 -0
- package/dist/components/inputs/variables/editors/TagVariableEditor.d.ts +6 -0
- package/dist/components/inputs/variables/editors/TagVariableEditor.js +32 -0
- package/dist/components/inputs/variables/editors/TagVariableEditor.js.map +1 -0
- package/dist/components/inputs/variables/editors/TimeVariableEditor.d.ts +9 -0
- package/dist/components/inputs/variables/extension.d.ts +3 -0
- package/dist/components/inputs/variables/extension.js +93 -0
- package/dist/components/inputs/variables/extension.js.map +1 -0
- package/dist/components/inputs/variables/pickers/NumericVariablePicker.d.ts +6 -0
- package/dist/components/inputs/variables/pickers/NumericVariablePicker.js +49 -0
- package/dist/components/inputs/variables/pickers/NumericVariablePicker.js.map +1 -0
- package/dist/components/inputs/variables/pickers/TemporalElementEditor.d.ts +6 -0
- package/dist/components/layout/MessageField.d.ts +9 -0
- package/dist/components/layout/MessageField.js +46 -0
- package/dist/components/layout/MessageField.js.map +1 -0
- package/dist/components/layout/MessagesTabs.d.ts +8 -0
- package/dist/components/layout/MessagesTabs.js +43 -0
- package/dist/components/layout/MessagesTabs.js.map +1 -0
- package/dist/components/layout/MessagesTree.d.ts +9 -0
- package/dist/components/layout/MessagesTree.js +67 -0
- package/dist/components/layout/MessagesTree.js.map +1 -0
- package/dist/context/messages-form.d.ts +30 -0
- package/dist/endpoints/get-messages.d.ts +2 -0
- package/dist/endpoints/get-messages.js +13 -0
- package/dist/endpoints/get-messages.js.map +1 -0
- package/dist/endpoints/set-messages.d.ts +2 -0
- package/dist/endpoints/set-messages.js +47 -0
- package/dist/endpoints/set-messages.js.map +1 -0
- package/dist/exports/link.d.ts +7 -0
- package/dist/exports/link.js +20 -0
- package/dist/exports/link.js.map +1 -0
- package/dist/exports/rsc.d.ts +2 -0
- package/dist/exports/rsc.js +7 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/exports/view.d.ts +10 -0
- package/dist/exports/view.js +56 -0
- package/dist/exports/view.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/requests/fetchMessages.d.ts +3 -0
- package/dist/requests/fetchMessages.js +29 -0
- package/dist/requests/fetchMessages.js.map +1 -0
- package/dist/styles.css +1 -0
- package/dist/types.d.ts +48 -0
- package/dist/utils/cn.d.ts +2 -0
- package/dist/utils/config.d.ts +11 -0
- package/dist/utils/config.js +19 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/format.d.ts +3 -0
- package/dist/utils/guards.d.ts +4 -0
- package/dist/utils/icu-tranform.d.ts +30 -0
- package/dist/utils/sanitize.d.ts +15 -0
- package/dist/utils/schema.d.ts +9 -0
- package/dist/utils/validate.d.ts +5 -0
- package/package.json +131 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { IconH6 as s, IconH5 as u, IconH4 as d, IconH3 as h, IconH2 as m, IconH1 as b, IconLetterT as v, IconList as f, IconListNumbers as g, IconSeparatorHorizontal as k, IconBlockquote as p, IconCode as E } from "@tabler/icons-react";
|
|
2
|
+
import { useEditorState as H } from "@tiptap/react";
|
|
3
|
+
import { Select as t, Toolbar as I } from "radix-ui";
|
|
4
|
+
import { useMemo as L } from "react";
|
|
5
|
+
import { cn as c } from "@/utils/cn";
|
|
6
|
+
function x({ editor: a }) {
|
|
7
|
+
const o = L(() => {
|
|
8
|
+
const e = a?.extensionManager.extensions.find((l) => l.name === "heading")?.options.levels.map(
|
|
9
|
+
(l) => n.HEADINGS[l]
|
|
10
|
+
) ?? [];
|
|
11
|
+
return [
|
|
12
|
+
n.TEXT,
|
|
13
|
+
...e.length > 0 ? ["-", ...e] : [],
|
|
14
|
+
"-",
|
|
15
|
+
...n.LIST,
|
|
16
|
+
"-",
|
|
17
|
+
n.SEPARATOR,
|
|
18
|
+
n.BLOCKQUOTE,
|
|
19
|
+
n.CODE_BLOCK
|
|
20
|
+
];
|
|
21
|
+
}, [a]), r = H({
|
|
22
|
+
editor: a,
|
|
23
|
+
selector: (e) => e.editor?.isActive("heading", { level: 1 }) ? "h1" : e.editor?.isActive("heading", { level: 2 }) ? "h2" : e.editor?.isActive("heading", { level: 3 }) ? "h3" : e.editor?.isActive("heading", { level: 4 }) ? "h4" : e.editor?.isActive("heading", { level: 5 }) ? "h5" : e.editor?.isActive("heading", { level: 6 }) ? "h6" : e.editor?.isActive("bulletList") ? "bullet-list" : e.editor?.isActive("orderedList") ? "ordered-list" : e.editor?.isActive("blockquote") ? "blockquote" : e.editor?.isActive("codeBlock") ? "code-block" : "text"
|
|
24
|
+
}), i = (e) => {
|
|
25
|
+
if (a) {
|
|
26
|
+
switch (e !== "blockquote" && a.chain().focus().unsetBlockquote().run(), e) {
|
|
27
|
+
case "h1":
|
|
28
|
+
a.chain().focus().setHeading({ level: 1 }).run();
|
|
29
|
+
break;
|
|
30
|
+
case "h2":
|
|
31
|
+
a.chain().focus().setHeading({ level: 2 }).run();
|
|
32
|
+
break;
|
|
33
|
+
case "h3":
|
|
34
|
+
a.chain().focus().setHeading({ level: 3 }).run();
|
|
35
|
+
break;
|
|
36
|
+
case "h4":
|
|
37
|
+
a.chain().focus().setHeading({ level: 4 }).run();
|
|
38
|
+
break;
|
|
39
|
+
case "h5":
|
|
40
|
+
a.chain().focus().setHeading({ level: 5 }).run();
|
|
41
|
+
break;
|
|
42
|
+
case "h6":
|
|
43
|
+
a.chain().focus().setHeading({ level: 6 }).run();
|
|
44
|
+
break;
|
|
45
|
+
case "bullet-list":
|
|
46
|
+
a.chain().focus().toggleBulletList().run();
|
|
47
|
+
break;
|
|
48
|
+
case "ordered-list":
|
|
49
|
+
a.chain().focus().toggleOrderedList().run();
|
|
50
|
+
break;
|
|
51
|
+
case "task-list":
|
|
52
|
+
a.chain().focus().toggleTaskList().run();
|
|
53
|
+
break;
|
|
54
|
+
case "separator":
|
|
55
|
+
a.chain().focus().setHorizontalRule().run();
|
|
56
|
+
break;
|
|
57
|
+
case "blockquote":
|
|
58
|
+
a.chain().focus().setParagraph().run(), a.chain().focus().setBlockquote().run();
|
|
59
|
+
break;
|
|
60
|
+
case "code-block":
|
|
61
|
+
a.chain().focus().setCodeBlock().run();
|
|
62
|
+
break;
|
|
63
|
+
case "text":
|
|
64
|
+
a.chain().focus().setParagraph().run();
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
setTimeout(() => {
|
|
68
|
+
a.commands.focus();
|
|
69
|
+
}, 100);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
return /* @__PURE__ */ React.createElement(t.Root, { value: r ?? "text", onValueChange: i }, /* @__PURE__ */ React.createElement(t.Trigger, { asChild: !0 }, /* @__PURE__ */ React.createElement(I.Button, { className: "flex w-34 cursor-pointer items-center justify-center gap-2 rounded-sm border-none bg-transparent hover:bg-elevation-250 focus:outline" }, /* @__PURE__ */ React.createElement(t.Value, null))), /* @__PURE__ */ React.createElement(t.Portal, null, /* @__PURE__ */ React.createElement(
|
|
73
|
+
t.Content,
|
|
74
|
+
{
|
|
75
|
+
"data-slot": "select-content",
|
|
76
|
+
side: "bottom",
|
|
77
|
+
className: "relative z-50 w-40 translate-y-2 rounded-md border border-border bg-elevation-100 shadow-md slide-in-from-top-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95"
|
|
78
|
+
},
|
|
79
|
+
/* @__PURE__ */ React.createElement(t.Viewport, { className: c("p-1") }, o.map((e, l) => e === "-" ? /* @__PURE__ */ React.createElement(
|
|
80
|
+
t.Separator,
|
|
81
|
+
{
|
|
82
|
+
key: l,
|
|
83
|
+
className: "my-1 h-px bg-border"
|
|
84
|
+
}
|
|
85
|
+
) : /* @__PURE__ */ React.createElement(
|
|
86
|
+
t.Item,
|
|
87
|
+
{
|
|
88
|
+
key: e.value,
|
|
89
|
+
"data-slot": "select-item",
|
|
90
|
+
className: c(
|
|
91
|
+
"flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1 hover:bg-elevation-250 hover:outline-none data-[state=checked]:pointer-events-none data-[state=checked]:bg-elevation-800 data-[state=checked]:text-elevation-0"
|
|
92
|
+
),
|
|
93
|
+
value: e.value
|
|
94
|
+
},
|
|
95
|
+
/* @__PURE__ */ React.createElement(t.Icon, { asChild: !0 }, /* @__PURE__ */ React.createElement(e.icon, { className: "size-4" })),
|
|
96
|
+
/* @__PURE__ */ React.createElement(t.ItemText, null, e.label)
|
|
97
|
+
)))
|
|
98
|
+
)));
|
|
99
|
+
}
|
|
100
|
+
const n = {
|
|
101
|
+
TEXT: { value: "text", label: "Text", icon: v },
|
|
102
|
+
HEADINGS: {
|
|
103
|
+
1: { value: "h1", label: "Heading 1", icon: b },
|
|
104
|
+
2: { value: "h2", label: "Heading 2", icon: m },
|
|
105
|
+
3: { value: "h3", label: "Heading 3", icon: h },
|
|
106
|
+
4: { value: "h4", label: "Heading 4", icon: d },
|
|
107
|
+
5: { value: "h5", label: "Heading 5", icon: u },
|
|
108
|
+
6: { value: "h6", label: "Heading 6", icon: s }
|
|
109
|
+
},
|
|
110
|
+
LIST: [
|
|
111
|
+
{ value: "bullet-list", label: "Bullet List", icon: f },
|
|
112
|
+
{ value: "ordered-list", label: "Ordered List", icon: g }
|
|
113
|
+
],
|
|
114
|
+
SEPARATOR: {
|
|
115
|
+
value: "separator",
|
|
116
|
+
label: "Separator",
|
|
117
|
+
icon: k
|
|
118
|
+
},
|
|
119
|
+
BLOCKQUOTE: {
|
|
120
|
+
value: "blockquote",
|
|
121
|
+
label: "Blockquote",
|
|
122
|
+
icon: p
|
|
123
|
+
},
|
|
124
|
+
CODE_BLOCK: { value: "code-block", label: "Code Block", icon: E }
|
|
125
|
+
};
|
|
126
|
+
export {
|
|
127
|
+
x as BlockElementSelect
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=BlockElementSelect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BlockElementSelect.js","sources":["../../../../src/components/inputs/toolbar/BlockElementSelect.tsx"],"sourcesContent":["import type { IconProps } from \"@tabler/icons-react\";\nimport type { Editor } from \"@tiptap/react\";\nimport {\n IconBlockquote,\n IconCode,\n IconH1,\n IconH2,\n IconH3,\n IconH4,\n IconH5,\n IconH6,\n IconLetterT,\n IconList,\n IconListNumbers,\n IconSeparatorHorizontal,\n} from \"@tabler/icons-react\";\nimport { useEditorState } from \"@tiptap/react\";\nimport { Select, Toolbar } from \"radix-ui\";\nimport { useMemo } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nexport interface BlockElementSelectProps {\n // TODO add config\n editor: Editor | null;\n}\nexport function BlockElementSelect({ editor }: BlockElementSelectProps) {\n const items = useMemo(() => {\n const headings =\n editor?.extensionManager.extensions\n .find((extension) => extension.name === \"heading\")\n ?.options.levels.map(\n (level: 1 | 2 | 3 | 4 | 5 | 6) => BLOCK_ELEMENTS.HEADINGS[level],\n ) ?? [];\n\n return [\n BLOCK_ELEMENTS.TEXT,\n ...(headings.length > 0 ? [\"-\", ...headings] : []),\n \"-\",\n ...BLOCK_ELEMENTS.LIST,\n \"-\",\n BLOCK_ELEMENTS.SEPARATOR,\n BLOCK_ELEMENTS.BLOCKQUOTE,\n BLOCK_ELEMENTS.CODE_BLOCK,\n ];\n }, [editor]);\n\n const value = useEditorState<BlockElementType>({\n editor,\n selector: (state) => {\n if (state.editor?.isActive(\"heading\", { level: 1 })) return \"h1\";\n if (state.editor?.isActive(\"heading\", { level: 2 })) return \"h2\";\n if (state.editor?.isActive(\"heading\", { level: 3 })) return \"h3\";\n if (state.editor?.isActive(\"heading\", { level: 4 })) return \"h4\";\n if (state.editor?.isActive(\"heading\", { level: 5 })) return \"h5\";\n if (state.editor?.isActive(\"heading\", { level: 6 })) return \"h6\";\n if (state.editor?.isActive(\"bulletList\")) return \"bullet-list\";\n if (state.editor?.isActive(\"orderedList\")) return \"ordered-list\";\n if (state.editor?.isActive(\"blockquote\")) return \"blockquote\";\n if (state.editor?.isActive(\"codeBlock\")) return \"code-block\";\n return \"text\";\n },\n });\n const handleChange = (value: string) => {\n if (!editor) return;\n\n if (value !== \"blockquote\") {\n editor.chain().focus().unsetBlockquote().run();\n }\n\n switch (value as BlockElementType) {\n case \"h1\":\n editor.chain().focus().setHeading({ level: 1 }).run();\n break;\n case \"h2\":\n editor.chain().focus().setHeading({ level: 2 }).run();\n break;\n case \"h3\":\n editor.chain().focus().setHeading({ level: 3 }).run();\n break;\n case \"h4\":\n editor.chain().focus().setHeading({ level: 4 }).run();\n break;\n case \"h5\":\n editor.chain().focus().setHeading({ level: 5 }).run();\n break;\n case \"h6\":\n editor.chain().focus().setHeading({ level: 6 }).run();\n break;\n case \"bullet-list\":\n editor.chain().focus().toggleBulletList().run();\n break;\n case \"ordered-list\":\n editor.chain().focus().toggleOrderedList().run();\n break;\n case \"task-list\":\n editor.chain().focus().toggleTaskList().run();\n break;\n case \"separator\":\n editor.chain().focus().setHorizontalRule().run();\n break;\n case \"blockquote\":\n editor.chain().focus().setParagraph().run();\n editor.chain().focus().setBlockquote().run();\n break;\n case \"code-block\":\n editor.chain().focus().setCodeBlock().run();\n break;\n case \"text\":\n editor.chain().focus().setParagraph().run();\n break;\n }\n // TODO bring back the focus to the active text\n setTimeout(() => {\n editor.commands.focus();\n }, 100);\n };\n\n return (\n <Select.Root value={value ?? \"text\"} onValueChange={handleChange}>\n <Select.Trigger asChild>\n <Toolbar.Button className=\"flex w-34 cursor-pointer items-center justify-center gap-2 rounded-sm border-none bg-transparent hover:bg-elevation-250 focus:outline\">\n <Select.Value />\n </Toolbar.Button>\n </Select.Trigger>\n <Select.Portal>\n <Select.Content\n data-slot=\"select-content\"\n side=\"bottom\"\n className=\"relative z-50 w-40 translate-y-2 rounded-md border border-border bg-elevation-100 shadow-md slide-in-from-top-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\"\n >\n <Select.Viewport className={cn(\"p-1\")}>\n {items.map((option, index) => {\n if (option === \"-\")\n return (\n <Select.Separator\n key={index}\n className=\"my-1 h-px bg-border\"\n />\n );\n\n return (\n <Select.Item\n key={option.value}\n data-slot=\"select-item\"\n className={cn(\n \"flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1 hover:bg-elevation-250 hover:outline-none data-[state=checked]:pointer-events-none data-[state=checked]:bg-elevation-800 data-[state=checked]:text-elevation-0\",\n )}\n value={option.value}\n >\n <Select.Icon asChild>\n <option.icon className=\"size-4\" />\n </Select.Icon>\n <Select.ItemText>{option.label}</Select.ItemText>\n </Select.Item>\n );\n })}\n </Select.Viewport>\n </Select.Content>\n </Select.Portal>\n </Select.Root>\n );\n}\n\n// MARK: Options\n\ntype BlockElementType =\n | \"text\"\n | \"h1\"\n | \"h2\"\n | \"h3\"\n | \"h4\"\n | \"h5\"\n | \"h6\"\n | \"bullet-list\"\n | \"ordered-list\"\n | \"task-list\"\n | \"blockquote\"\n | \"separator\"\n | \"code-block\";\n\ninterface BlockElementOption {\n value: BlockElementType;\n label: string;\n icon: React.FunctionComponent<IconProps>;\n}\n\nconst BLOCK_ELEMENTS = {\n TEXT: { value: \"text\", label: \"Text\", icon: IconLetterT },\n HEADINGS: {\n 1: { value: \"h1\", label: \"Heading 1\", icon: IconH1 },\n 2: { value: \"h2\", label: \"Heading 2\", icon: IconH2 },\n 3: { value: \"h3\", label: \"Heading 3\", icon: IconH3 },\n 4: { value: \"h4\", label: \"Heading 4\", icon: IconH4 },\n 5: { value: \"h5\", label: \"Heading 5\", icon: IconH5 },\n 6: { value: \"h6\", label: \"Heading 6\", icon: IconH6 },\n },\n LIST: [\n { value: \"bullet-list\", label: \"Bullet List\", icon: IconList },\n { value: \"ordered-list\", label: \"Ordered List\", icon: IconListNumbers },\n ],\n SEPARATOR: {\n value: \"separator\",\n label: \"Separator\",\n icon: IconSeparatorHorizontal,\n },\n BLOCKQUOTE: {\n value: \"blockquote\",\n label: \"Blockquote\",\n icon: IconBlockquote,\n },\n CODE_BLOCK: { value: \"code-block\", label: \"Code Block\", icon: IconCode },\n} satisfies Record<\n string,\n | BlockElementOption\n | BlockElementOption[]\n | Record<string | number, BlockElementOption>\n>;\n"],"names":["BlockElementSelect","editor","items","useMemo","headings","extension","level","BLOCK_ELEMENTS","value","useEditorState","state","handleChange","Select","Toolbar","cn","option","index","IconLetterT","IconH1","IconH2","IconH3","IconH4","IconH5","IconH6","IconList","IconListNumbers","IconSeparatorHorizontal","IconBlockquote","IconCode"],"mappings":";;;;;AA0BO,SAASA,EAAmB,EAAE,QAAAC,KAAmC;AACtE,QAAMC,IAAQC,EAAQ,MAAM;AAC1B,UAAMC,IACJH,GAAQ,iBAAiB,WACtB,KAAK,CAACI,MAAcA,EAAU,SAAS,SAAS,GAC/C,QAAQ,OAAO;AAAA,MACf,CAACC,MAAiCC,EAAe,SAASD,CAAK;AAAA,IAAA,KAC5D,CAAA;AAET,WAAO;AAAA,MACLC,EAAe;AAAA,MACf,GAAIH,EAAS,SAAS,IAAI,CAAC,KAAK,GAAGA,CAAQ,IAAI,CAAA;AAAA,MAC/C;AAAA,MACA,GAAGG,EAAe;AAAA,MAClB;AAAA,MACAA,EAAe;AAAA,MACfA,EAAe;AAAA,MACfA,EAAe;AAAA,IAAA;AAAA,EAEnB,GAAG,CAACN,CAAM,CAAC,GAELO,IAAQC,EAAiC;AAAA,IAC7C,QAAAR;AAAA,IACA,UAAU,CAACS,MACLA,EAAM,QAAQ,SAAS,WAAW,EAAE,OAAO,GAAG,IAAU,OACxDA,EAAM,QAAQ,SAAS,WAAW,EAAE,OAAO,GAAG,IAAU,OACxDA,EAAM,QAAQ,SAAS,WAAW,EAAE,OAAO,GAAG,IAAU,OACxDA,EAAM,QAAQ,SAAS,WAAW,EAAE,OAAO,GAAG,IAAU,OACxDA,EAAM,QAAQ,SAAS,WAAW,EAAE,OAAO,GAAG,IAAU,OACxDA,EAAM,QAAQ,SAAS,WAAW,EAAE,OAAO,GAAG,IAAU,OACxDA,EAAM,QAAQ,SAAS,YAAY,IAAU,gBAC7CA,EAAM,QAAQ,SAAS,aAAa,IAAU,iBAC9CA,EAAM,QAAQ,SAAS,YAAY,IAAU,eAC7CA,EAAM,QAAQ,SAAS,WAAW,IAAU,eACzC;AAAA,EACT,CACD,GACKC,IAAe,CAACH,MAAkB;AACtC,QAAKP,GAML;AAAA,cAJIO,MAAU,gBACZP,EAAO,QAAQ,MAAA,EAAQ,gBAAA,EAAkB,IAAA,GAGnCO,GAAAA;AAAAA,QACN,KAAK;AACH,UAAAP,EAAO,QAAQ,QAAQ,WAAW,EAAE,OAAO,GAAG,EAAE,IAAA;AAChD;AAAA,QACF,KAAK;AACH,UAAAA,EAAO,QAAQ,QAAQ,WAAW,EAAE,OAAO,GAAG,EAAE,IAAA;AAChD;AAAA,QACF,KAAK;AACH,UAAAA,EAAO,QAAQ,QAAQ,WAAW,EAAE,OAAO,GAAG,EAAE,IAAA;AAChD;AAAA,QACF,KAAK;AACH,UAAAA,EAAO,QAAQ,QAAQ,WAAW,EAAE,OAAO,GAAG,EAAE,IAAA;AAChD;AAAA,QACF,KAAK;AACH,UAAAA,EAAO,QAAQ,QAAQ,WAAW,EAAE,OAAO,GAAG,EAAE,IAAA;AAChD;AAAA,QACF,KAAK;AACH,UAAAA,EAAO,QAAQ,QAAQ,WAAW,EAAE,OAAO,GAAG,EAAE,IAAA;AAChD;AAAA,QACF,KAAK;AACH,UAAAA,EAAO,QAAQ,MAAA,EAAQ,iBAAA,EAAmB,IAAA;AAC1C;AAAA,QACF,KAAK;AACH,UAAAA,EAAO,QAAQ,MAAA,EAAQ,kBAAA,EAAoB,IAAA;AAC3C;AAAA,QACF,KAAK;AACH,UAAAA,EAAO,QAAQ,MAAA,EAAQ,eAAA,EAAiB,IAAA;AACxC;AAAA,QACF,KAAK;AACH,UAAAA,EAAO,QAAQ,MAAA,EAAQ,kBAAA,EAAoB,IAAA;AAC3C;AAAA,QACF,KAAK;AACH,UAAAA,EAAO,QAAQ,MAAA,EAAQ,aAAA,EAAe,IAAA,GACtCA,EAAO,QAAQ,MAAA,EAAQ,cAAA,EAAgB,IAAA;AACvC;AAAA,QACF,KAAK;AACH,UAAAA,EAAO,QAAQ,MAAA,EAAQ,aAAA,EAAe,IAAA;AACtC;AAAA,QACF,KAAK;AACH,UAAAA,EAAO,QAAQ,MAAA,EAAQ,aAAA,EAAe,IAAA;AACtC;AAAA,MAAA;AAGJ,iBAAW,MAAM;AACf,QAAAA,EAAO,SAAS,MAAA;AAAA,MAClB,GAAG,GAAG;AAAA;AAAA,EACR;AAEA,SACE,sBAAA,cAACW,EAAO,MAAP,EAAY,OAAOJ,KAAS,QAAQ,eAAeG,KAClD,sBAAA,cAACC,EAAO,SAAP,EAAe,SAAO,GAAA,GACrB,sBAAA,cAACC,EAAQ,QAAR,EAAe,WAAU,wIAAA,uCACvBD,EAAO,OAAP,IAAa,CAChB,CACF,GACA,sBAAA,cAACA,EAAO,QAAP,MACC,sBAAA;AAAA,IAACA,EAAO;AAAA,IAAP;AAAA,MACC,aAAU;AAAA,MACV,MAAK;AAAA,MACL,WAAU;AAAA,IAAA;AAAA,IAEV,sBAAA,cAACA,EAAO,UAAP,EAAgB,WAAWE,EAAG,KAAK,EAAA,GACjCZ,EAAM,IAAI,CAACa,GAAQC,MACdD,MAAW,MAEX,sBAAA;AAAA,MAACH,EAAO;AAAA,MAAP;AAAA,QACC,KAAKI;AAAA,QACL,WAAU;AAAA,MAAA;AAAA,IAAA,IAKd,sBAAA;AAAA,MAACJ,EAAO;AAAA,MAAP;AAAA,QACC,KAAKG,EAAO;AAAA,QACZ,aAAU;AAAA,QACV,WAAWD;AAAA,UACT;AAAA,QAAA;AAAA,QAEF,OAAOC,EAAO;AAAA,MAAA;AAAA,MAEd,sBAAA,cAACH,EAAO,MAAP,EAAY,SAAO,GAAA,GAClB,sBAAA,cAACG,EAAO,MAAP,EAAY,WAAU,SAAA,CAAS,CAClC;AAAA,MACA,sBAAA,cAACH,EAAO,UAAP,MAAiBG,EAAO,KAAM;AAAA,IAAA,CAGpC,CACH;AAAA,EAAA,CAEJ,CACF;AAEJ;AAyBA,MAAMR,IAAiB;AAAA,EACrB,MAAM,EAAE,OAAO,QAAQ,OAAO,QAAQ,MAAMU,EAAA;AAAA,EAC5C,UAAU;AAAA,IACR,GAAG,EAAE,OAAO,MAAM,OAAO,aAAa,MAAMC,EAAA;AAAA,IAC5C,GAAG,EAAE,OAAO,MAAM,OAAO,aAAa,MAAMC,EAAA;AAAA,IAC5C,GAAG,EAAE,OAAO,MAAM,OAAO,aAAa,MAAMC,EAAA;AAAA,IAC5C,GAAG,EAAE,OAAO,MAAM,OAAO,aAAa,MAAMC,EAAA;AAAA,IAC5C,GAAG,EAAE,OAAO,MAAM,OAAO,aAAa,MAAMC,EAAA;AAAA,IAC5C,GAAG,EAAE,OAAO,MAAM,OAAO,aAAa,MAAMC,EAAA;AAAA,EAAO;AAAA,EAErD,MAAM;AAAA,IACJ,EAAE,OAAO,eAAe,OAAO,eAAe,MAAMC,EAAA;AAAA,IACpD,EAAE,OAAO,gBAAgB,OAAO,gBAAgB,MAAMC,EAAA;AAAA,EAAgB;AAAA,EAExE,WAAW;AAAA,IACT,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAMC;AAAA,EAAA;AAAA,EAER,YAAY;AAAA,IACV,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAMC;AAAA,EAAA;AAAA,EAER,YAAY,EAAE,OAAO,cAAc,OAAO,cAAc,MAAMC,EAAA;AAChE;"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Editor } from '@tiptap/react';
|
|
2
|
+
export interface LinkEditorProps extends Omit<React.HTMLAttributes<HTMLButtonElement>, "type" | "children">, UseLinkPopoverConfig {
|
|
3
|
+
/**
|
|
4
|
+
* Callback for when the popover opens or closes.
|
|
5
|
+
*/
|
|
6
|
+
onOpenChange?: (isOpen: boolean) => void;
|
|
7
|
+
/**
|
|
8
|
+
* Whether to automatically open the popover when a link is active.
|
|
9
|
+
* @default true
|
|
10
|
+
*/
|
|
11
|
+
autoOpenOnLinkActive?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function LinkEditor({ editor, hideWhenUnavailable, onSetLink, onOpenChange, autoOpenOnLinkActive, onClick, className, ...buttonProps }: LinkEditorProps): import("react").JSX.Element;
|
|
14
|
+
/**
|
|
15
|
+
* Configuration for the link popover functionality
|
|
16
|
+
*/
|
|
17
|
+
export interface UseLinkPopoverConfig {
|
|
18
|
+
/**
|
|
19
|
+
* The Tiptap editor instance.
|
|
20
|
+
*/
|
|
21
|
+
editor?: Editor | null;
|
|
22
|
+
/**
|
|
23
|
+
* Whether to hide the link popover when not available.
|
|
24
|
+
* @default false
|
|
25
|
+
*/
|
|
26
|
+
hideWhenUnavailable?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Callback function called when the link is set.
|
|
29
|
+
*/
|
|
30
|
+
onSetLink?: () => void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Checks if a mark exists in the editor schema
|
|
34
|
+
*/
|
|
35
|
+
export declare const isMarkInSchema: (markName: string, editor: Editor | null) => boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Configuration for the link handler functionality
|
|
38
|
+
*/
|
|
39
|
+
export interface LinkHandlerProps {
|
|
40
|
+
/**
|
|
41
|
+
* The Tiptap editor instance.
|
|
42
|
+
*/
|
|
43
|
+
editor: Editor | null;
|
|
44
|
+
/**
|
|
45
|
+
* Callback function called when the link is set.
|
|
46
|
+
*/
|
|
47
|
+
onSetLink?: () => void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Custom hook for handling link operations in a Tiptap editor
|
|
51
|
+
*/
|
|
52
|
+
export declare function useLinkHandler({ editor, onSetLink }: LinkHandlerProps): {
|
|
53
|
+
url: string;
|
|
54
|
+
setUrl: import('react').Dispatch<import('react').SetStateAction<string | null>>;
|
|
55
|
+
setLink: () => void;
|
|
56
|
+
removeLink: () => void;
|
|
57
|
+
openLink: (target?: string, features?: string) => void;
|
|
58
|
+
};
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { IconLink as A, IconCornerDownLeft as I, IconExternalLink as N, IconLinkOff as P } from "@tabler/icons-react";
|
|
2
|
+
import { useEditorState as W } from "@tiptap/react";
|
|
3
|
+
import { Popover as k, Toolbar as p } from "radix-ui";
|
|
4
|
+
import { useState as v, useCallback as d, useEffect as h } from "react";
|
|
5
|
+
import { cn as B } from "@/utils/cn";
|
|
6
|
+
function q({
|
|
7
|
+
editor: e,
|
|
8
|
+
hideWhenUnavailable: t = !1,
|
|
9
|
+
onSetLink: n,
|
|
10
|
+
onOpenChange: a,
|
|
11
|
+
autoOpenOnLinkActive: c = !0,
|
|
12
|
+
onClick: u,
|
|
13
|
+
className: f,
|
|
14
|
+
...r
|
|
15
|
+
}) {
|
|
16
|
+
const [o, i] = v(!0), { canSet: l, isActive: m, url: b, setUrl: w, setLink: E, removeLink: g, openLink: y } = M({
|
|
17
|
+
editor: e,
|
|
18
|
+
hideWhenUnavailable: t,
|
|
19
|
+
onSetLink: n
|
|
20
|
+
}), S = d(
|
|
21
|
+
(s) => {
|
|
22
|
+
i(s), a?.(s);
|
|
23
|
+
},
|
|
24
|
+
[a]
|
|
25
|
+
), R = d(() => {
|
|
26
|
+
E(), i(!1);
|
|
27
|
+
}, [E]), U = d(() => {
|
|
28
|
+
g(), i(!1);
|
|
29
|
+
}, [g]), C = d(
|
|
30
|
+
(s) => {
|
|
31
|
+
u?.(s), !s.defaultPrevented && i(!o);
|
|
32
|
+
},
|
|
33
|
+
[u, o]
|
|
34
|
+
), z = (s) => {
|
|
35
|
+
s.key === "Enter" && (s.preventDefault(), R());
|
|
36
|
+
};
|
|
37
|
+
return h(() => {
|
|
38
|
+
c && m && i(!0);
|
|
39
|
+
}, [c, m]), /* @__PURE__ */ React.createElement(k.Root, { open: o, onOpenChange: S }, /* @__PURE__ */ React.createElement(k.Trigger, { asChild: !0 }, /* @__PURE__ */ React.createElement(
|
|
40
|
+
p.Button,
|
|
41
|
+
{
|
|
42
|
+
className: B(f, {
|
|
43
|
+
"bg-elevation-600 text-elevation-0": m
|
|
44
|
+
}),
|
|
45
|
+
disabled: !l,
|
|
46
|
+
onClick: C,
|
|
47
|
+
"aria-label": "Link",
|
|
48
|
+
...r
|
|
49
|
+
},
|
|
50
|
+
/* @__PURE__ */ React.createElement(A, { size: 16 })
|
|
51
|
+
)), /* @__PURE__ */ React.createElement(
|
|
52
|
+
k.Content,
|
|
53
|
+
{
|
|
54
|
+
className: "z-50 rounded-md border border-border bg-elevation-100 p-2 shadow-md",
|
|
55
|
+
sideOffset: 8,
|
|
56
|
+
align: "end"
|
|
57
|
+
},
|
|
58
|
+
/* @__PURE__ */ React.createElement(p.Root, { className: "flex items-center gap-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center overflow-hidden rounded-md border border-border focus-within:border-elevation-600" }, /* @__PURE__ */ React.createElement(
|
|
59
|
+
"input",
|
|
60
|
+
{
|
|
61
|
+
type: "url",
|
|
62
|
+
placeholder: "Paste a link...",
|
|
63
|
+
autoComplete: "off",
|
|
64
|
+
autoCorrect: "off",
|
|
65
|
+
autoCapitalize: "off",
|
|
66
|
+
value: b,
|
|
67
|
+
onChange: (s) => w(s.target.value),
|
|
68
|
+
onKeyDown: z,
|
|
69
|
+
className: "h-10 flex-1 border-none bg-input px-2 py-1 focus:outline-none",
|
|
70
|
+
"aria-label": "Link URL",
|
|
71
|
+
style: { minWidth: "200px" }
|
|
72
|
+
}
|
|
73
|
+
), /* @__PURE__ */ React.createElement(
|
|
74
|
+
p.Button,
|
|
75
|
+
{
|
|
76
|
+
type: "button",
|
|
77
|
+
title: "Apply link",
|
|
78
|
+
disabled: !b && !m,
|
|
79
|
+
className: "flex size-10 items-center justify-center !rounded-none border-none bg-elevation-800 p-0 text-elevation-0 hover:bg-elevation-600 disabled:bg-transparent disabled:text-elevation-250",
|
|
80
|
+
onClick: R
|
|
81
|
+
},
|
|
82
|
+
/* @__PURE__ */ React.createElement(I, { size: 16 })
|
|
83
|
+
)), /* @__PURE__ */ React.createElement(p.Separator, { className: "mx-1 h-10 w-px bg-border" }), /* @__PURE__ */ React.createElement(
|
|
84
|
+
p.Button,
|
|
85
|
+
{
|
|
86
|
+
type: "button",
|
|
87
|
+
className: "flex size-10 items-center justify-center rounded-md border-none bg-transparent p-0 hover:bg-elevation-250",
|
|
88
|
+
title: "Open in new window",
|
|
89
|
+
disabled: !b && !m,
|
|
90
|
+
onClick: () => y()
|
|
91
|
+
},
|
|
92
|
+
/* @__PURE__ */ React.createElement(N, { size: 16 })
|
|
93
|
+
), /* @__PURE__ */ React.createElement(
|
|
94
|
+
p.Button,
|
|
95
|
+
{
|
|
96
|
+
type: "button",
|
|
97
|
+
title: "Remove link",
|
|
98
|
+
className: "flex size-10 items-center justify-center rounded-md border-none bg-transparent p-0 hover:bg-elevation-250",
|
|
99
|
+
disabled: !b && !m,
|
|
100
|
+
onClick: U
|
|
101
|
+
},
|
|
102
|
+
/* @__PURE__ */ React.createElement(P, { size: 16 })
|
|
103
|
+
))
|
|
104
|
+
));
|
|
105
|
+
}
|
|
106
|
+
function M(e) {
|
|
107
|
+
const { editor: t, hideWhenUnavailable: n = !1, onSetLink: a } = e || {}, { isVisible: c, canSet: u, isActive: f } = L({
|
|
108
|
+
editor: t || null,
|
|
109
|
+
hideWhenUnavailable: n
|
|
110
|
+
}), r = D({
|
|
111
|
+
editor: t || null,
|
|
112
|
+
onSetLink: a
|
|
113
|
+
});
|
|
114
|
+
return {
|
|
115
|
+
isVisible: c,
|
|
116
|
+
canSet: u,
|
|
117
|
+
isActive: f,
|
|
118
|
+
...r
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function L(e) {
|
|
122
|
+
const { editor: t, hideWhenUnavailable: n = !1 } = e, a = x(t), c = W({
|
|
123
|
+
editor: t,
|
|
124
|
+
selector: (r) => r.editor?.isEditable && r?.editor?.isActive("link")
|
|
125
|
+
}) ?? !1, [u, f] = v(!1);
|
|
126
|
+
return h(() => {
|
|
127
|
+
if (!t) return;
|
|
128
|
+
const r = () => {
|
|
129
|
+
f(
|
|
130
|
+
T({
|
|
131
|
+
editor: t,
|
|
132
|
+
hideWhenUnavailable: n
|
|
133
|
+
})
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
return r(), t.on("selectionUpdate", r), () => {
|
|
137
|
+
t.off("selectionUpdate", r);
|
|
138
|
+
};
|
|
139
|
+
}, [t, n]), {
|
|
140
|
+
isVisible: u,
|
|
141
|
+
canSet: a,
|
|
142
|
+
isActive: c
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function x(e) {
|
|
146
|
+
return !e || !e.isEditable ? !1 : e.can().setMark("link");
|
|
147
|
+
}
|
|
148
|
+
function T(e) {
|
|
149
|
+
const { editor: t, hideWhenUnavailable: n } = e;
|
|
150
|
+
return !j("link", t) || !t ? !1 : n && !t.isActive("code") ? x(t) : !0;
|
|
151
|
+
}
|
|
152
|
+
const j = (e, t) => t?.schema ? t.schema.spec.marks.get(e) !== void 0 : !1;
|
|
153
|
+
function D({ editor: e, onSetLink: t }) {
|
|
154
|
+
const [n, a] = v(null), { isActive: c } = L({ editor: e, hideWhenUnavailable: !1 });
|
|
155
|
+
h(() => {
|
|
156
|
+
if (!e) return;
|
|
157
|
+
const { href: o } = e.getAttributes("link");
|
|
158
|
+
c && n === null && a(o || "");
|
|
159
|
+
}, [e, n, c]), h(() => {
|
|
160
|
+
if (!e) return;
|
|
161
|
+
const o = () => {
|
|
162
|
+
const { href: i } = e.getAttributes("link");
|
|
163
|
+
a(i || "");
|
|
164
|
+
};
|
|
165
|
+
return e.on("selectionUpdate", o), () => {
|
|
166
|
+
e.off("selectionUpdate", o);
|
|
167
|
+
};
|
|
168
|
+
}, [e]);
|
|
169
|
+
const u = d(() => {
|
|
170
|
+
if (!n || !e) return;
|
|
171
|
+
const { selection: o } = e.state, i = o.empty;
|
|
172
|
+
let l = e.chain().focus();
|
|
173
|
+
l = l.extendMarkRange("link").setLink({ href: n }), i && (l = l.insertContent({ type: "text", text: n })), l.run(), a(null), t?.();
|
|
174
|
+
}, [e, t, n]), f = d(() => {
|
|
175
|
+
e && (e.chain().focus().extendMarkRange("link").unsetLink().setMeta("preventAutolink", !0).run(), a(""));
|
|
176
|
+
}, [e]), r = d(
|
|
177
|
+
(o = "_blank", i = "noopener,noreferrer") => {
|
|
178
|
+
if (!n) return;
|
|
179
|
+
const l = O(n, window.location.href);
|
|
180
|
+
l !== "#" && window.open(l, o, i);
|
|
181
|
+
},
|
|
182
|
+
[n]
|
|
183
|
+
);
|
|
184
|
+
return {
|
|
185
|
+
url: n || "",
|
|
186
|
+
setUrl: a,
|
|
187
|
+
setLink: u,
|
|
188
|
+
removeLink: f,
|
|
189
|
+
openLink: r
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function O(e, t, n) {
|
|
193
|
+
try {
|
|
194
|
+
const a = new URL(e, t);
|
|
195
|
+
if (H(a.href, n))
|
|
196
|
+
return a.href;
|
|
197
|
+
} catch {
|
|
198
|
+
}
|
|
199
|
+
return "#";
|
|
200
|
+
}
|
|
201
|
+
function H(e, t) {
|
|
202
|
+
const n = [
|
|
203
|
+
"http",
|
|
204
|
+
"https",
|
|
205
|
+
"ftp",
|
|
206
|
+
"ftps",
|
|
207
|
+
"mailto",
|
|
208
|
+
"tel",
|
|
209
|
+
"callto",
|
|
210
|
+
"sms",
|
|
211
|
+
"cid",
|
|
212
|
+
"xmpp"
|
|
213
|
+
], a = (
|
|
214
|
+
// eslint-disable-next-line no-control-regex
|
|
215
|
+
/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g
|
|
216
|
+
);
|
|
217
|
+
return !e || e.replace(a, "").match(
|
|
218
|
+
new RegExp(
|
|
219
|
+
// eslint-disable-next-line no-useless-escape, regexp/no-obscure-range
|
|
220
|
+
`^(?:(?:${n.join("|")}):|[^a-z]|[a-z0-9+.-]+(?:[^a-z+.-:]|$))`,
|
|
221
|
+
"i"
|
|
222
|
+
)
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
export {
|
|
226
|
+
q as LinkEditor,
|
|
227
|
+
j as isMarkInSchema,
|
|
228
|
+
D as useLinkHandler
|
|
229
|
+
};
|
|
230
|
+
//# sourceMappingURL=LinkEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LinkEditor.js","sources":["../../../../src/components/inputs/toolbar/LinkEditor.tsx"],"sourcesContent":["import type { Editor } from \"@tiptap/react\";\nimport {\n IconCornerDownLeft,\n IconExternalLink,\n IconLink,\n IconLinkOff,\n} from \"@tabler/icons-react\";\nimport { useEditorState } from \"@tiptap/react\";\nimport { Popover, Toolbar } from \"radix-ui\";\nimport { useCallback, useEffect, useState } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nexport interface LinkEditorProps\n extends Omit<React.HTMLAttributes<HTMLButtonElement>, \"type\" | \"children\">,\n UseLinkPopoverConfig {\n /**\n * Callback for when the popover opens or closes.\n */\n onOpenChange?: (isOpen: boolean) => void;\n /**\n * Whether to automatically open the popover when a link is active.\n * @default true\n */\n autoOpenOnLinkActive?: boolean;\n}\n\nexport function LinkEditor({\n editor,\n hideWhenUnavailable = false,\n onSetLink,\n onOpenChange,\n autoOpenOnLinkActive = true,\n onClick,\n className,\n ...buttonProps\n}: LinkEditorProps) {\n const [isOpen, setIsOpen] = useState(true);\n\n const { canSet, isActive, url, setUrl, setLink, removeLink, openLink } =\n useLinkPopover({\n editor,\n hideWhenUnavailable,\n onSetLink,\n });\n\n const handleOnOpenChange = useCallback(\n (nextIsOpen: boolean) => {\n setIsOpen(nextIsOpen);\n onOpenChange?.(nextIsOpen);\n },\n [onOpenChange],\n );\n\n const handleSetLink = useCallback(() => {\n setLink();\n setIsOpen(false);\n }, [setLink]);\n\n const handleRemoveLink = useCallback(() => {\n removeLink();\n setIsOpen(false);\n }, [removeLink]);\n\n const handleClick = useCallback(\n (event: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n if (event.defaultPrevented) return;\n setIsOpen(!isOpen);\n },\n [onClick, isOpen],\n );\n\n const handleIputKeyDown = (event: React.KeyboardEvent) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n handleSetLink();\n }\n };\n\n useEffect(() => {\n if (autoOpenOnLinkActive && isActive) {\n setIsOpen(true);\n }\n }, [autoOpenOnLinkActive, isActive]);\n\n return (\n <Popover.Root open={isOpen} onOpenChange={handleOnOpenChange}>\n <Popover.Trigger asChild>\n <Toolbar.Button\n className={cn(className, {\n \"bg-elevation-600 text-elevation-0\": isActive,\n })}\n disabled={!canSet}\n onClick={handleClick}\n aria-label=\"Link\"\n {...buttonProps}\n >\n <IconLink size={16} />\n </Toolbar.Button>\n </Popover.Trigger>\n\n <Popover.Content\n className=\"z-50 rounded-md border border-border bg-elevation-100 p-2 shadow-md\"\n sideOffset={8}\n align=\"end\"\n >\n <Toolbar.Root className=\"flex items-center gap-2\">\n <div className=\"flex items-center overflow-hidden rounded-md border border-border focus-within:border-elevation-600\">\n <input\n type=\"url\"\n placeholder=\"Paste a link...\"\n autoComplete=\"off\"\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n onKeyDown={handleIputKeyDown}\n className=\"h-10 flex-1 border-none bg-input px-2 py-1 focus:outline-none\"\n aria-label=\"Link URL\"\n style={{ minWidth: \"200px\" }}\n />\n\n <Toolbar.Button\n type=\"button\"\n title=\"Apply link\"\n disabled={!url && !isActive}\n className=\"flex size-10 items-center justify-center !rounded-none border-none bg-elevation-800 p-0 text-elevation-0 hover:bg-elevation-600 disabled:bg-transparent disabled:text-elevation-250\"\n onClick={handleSetLink}\n >\n <IconCornerDownLeft size={16} />\n </Toolbar.Button>\n </div>\n\n <Toolbar.Separator className=\"mx-1 h-10 w-px bg-border\" />\n\n <Toolbar.Button\n type=\"button\"\n className=\"flex size-10 items-center justify-center rounded-md border-none bg-transparent p-0 hover:bg-elevation-250\"\n title=\"Open in new window\"\n disabled={!url && !isActive}\n onClick={() => openLink()}\n >\n <IconExternalLink size={16} />\n </Toolbar.Button>\n\n <Toolbar.Button\n type=\"button\"\n title=\"Remove link\"\n className=\"flex size-10 items-center justify-center rounded-md border-none bg-transparent p-0 hover:bg-elevation-250\"\n disabled={!url && !isActive}\n onClick={handleRemoveLink}\n >\n <IconLinkOff size={16} />\n </Toolbar.Button>\n </Toolbar.Root>\n </Popover.Content>\n </Popover.Root>\n );\n}\n\n// TODO this was mostly copied from the tiptap starter template, pretty sure it can be simplified\n\n/**\n * Configuration for the link popover functionality\n */\nexport interface UseLinkPopoverConfig {\n /**\n * The Tiptap editor instance.\n */\n editor?: Editor | null;\n /**\n * Whether to hide the link popover when not available.\n * @default false\n */\n hideWhenUnavailable?: boolean;\n /**\n * Callback function called when the link is set.\n */\n onSetLink?: () => void;\n}\n\n/**\n * Main hook that provides link popover functionality for Tiptap editor\n */\nfunction useLinkPopover(config?: UseLinkPopoverConfig) {\n const { editor, hideWhenUnavailable = false, onSetLink } = config || {};\n\n const { isVisible, canSet, isActive } = useLinkState({\n editor: editor || null,\n hideWhenUnavailable,\n });\n\n const linkHandler = useLinkHandler({\n editor: editor || null,\n onSetLink,\n });\n\n return {\n isVisible,\n canSet,\n isActive,\n ...linkHandler,\n };\n}\n\nfunction useLinkState(props: {\n editor: Editor | null;\n hideWhenUnavailable: boolean;\n}) {\n const { editor, hideWhenUnavailable = false } = props;\n\n const canSet = canSetLink(editor);\n const isActive =\n useEditorState({\n editor,\n selector: (state) =>\n state.editor?.isEditable && state?.editor?.isActive(\"link\"),\n }) ?? false;\n\n const [isVisible, setIsVisible] = useState(false);\n\n useEffect(() => {\n if (!editor) return;\n\n const handleSelectionUpdate = () => {\n setIsVisible(\n shouldShowLinkButton({\n editor,\n hideWhenUnavailable,\n }),\n );\n };\n\n handleSelectionUpdate();\n\n editor.on(\"selectionUpdate\", handleSelectionUpdate);\n\n return () => {\n editor.off(\"selectionUpdate\", handleSelectionUpdate);\n };\n }, [editor, hideWhenUnavailable]);\n\n return {\n isVisible,\n canSet,\n isActive,\n };\n}\n\n/**\n * Checks if a link can be set in the current editor state\n */\nfunction canSetLink(editor: Editor | null): boolean {\n if (!editor || !editor.isEditable) return false;\n return editor.can().setMark(\"link\");\n}\n\n/**\n * Determines if the link button should be shown\n */\nfunction shouldShowLinkButton(props: {\n editor: Editor | null;\n hideWhenUnavailable: boolean;\n}): boolean {\n const { editor, hideWhenUnavailable } = props;\n\n const linkInSchema = isMarkInSchema(\"link\", editor);\n\n if (!linkInSchema || !editor) {\n return false;\n }\n\n if (hideWhenUnavailable && !editor.isActive(\"code\")) {\n return canSetLink(editor);\n }\n\n return true;\n}\n\n/**\n * Checks if a mark exists in the editor schema\n */\nexport const isMarkInSchema = (\n markName: string,\n editor: Editor | null,\n): boolean => {\n if (!editor?.schema) return false;\n return editor.schema.spec.marks.get(markName) !== undefined;\n};\n\n/**\n * Configuration for the link handler functionality\n */\nexport interface LinkHandlerProps {\n /**\n * The Tiptap editor instance.\n */\n editor: Editor | null;\n /**\n * Callback function called when the link is set.\n */\n onSetLink?: () => void;\n}\n\n/**\n * Custom hook for handling link operations in a Tiptap editor\n */\nexport function useLinkHandler({ editor, onSetLink }: LinkHandlerProps) {\n const [url, setUrl] = useState<string | null>(null);\n const { isActive } = useLinkState({ editor, hideWhenUnavailable: false });\n\n useEffect(() => {\n if (!editor) return;\n\n // Get URL immediately on mount\n const { href } = editor.getAttributes(\"link\");\n\n if (isActive && url === null) {\n setUrl(href || \"\");\n }\n }, [editor, url, isActive]);\n\n useEffect(() => {\n if (!editor) return;\n\n const updateLinkState = () => {\n const { href } = editor.getAttributes(\"link\");\n setUrl(href || \"\");\n };\n\n editor.on(\"selectionUpdate\", updateLinkState);\n return () => {\n editor.off(\"selectionUpdate\", updateLinkState);\n };\n }, [editor]);\n\n const setLink = useCallback(() => {\n if (!url || !editor) return;\n\n const { selection } = editor.state;\n const isEmpty = selection.empty;\n\n let chain = editor.chain().focus();\n\n chain = chain.extendMarkRange(\"link\").setLink({ href: url });\n\n if (isEmpty) {\n chain = chain.insertContent({ type: \"text\", text: url });\n }\n\n chain.run();\n\n setUrl(null);\n\n onSetLink?.();\n }, [editor, onSetLink, url]);\n\n const removeLink = useCallback(() => {\n if (!editor) return;\n editor\n .chain()\n .focus()\n .extendMarkRange(\"link\")\n .unsetLink()\n .setMeta(\"preventAutolink\", true)\n .run();\n setUrl(\"\");\n }, [editor]);\n\n const openLink = useCallback(\n (target: string = \"_blank\", features: string = \"noopener,noreferrer\") => {\n if (!url) return;\n\n const safeUrl = sanitizeUrl(url, window.location.href);\n if (safeUrl !== \"#\") {\n window.open(safeUrl, target, features);\n }\n },\n [url],\n );\n\n return {\n url: url || \"\",\n setUrl,\n setLink,\n removeLink,\n openLink,\n };\n}\n\ntype ProtocolOptions = {\n /**\n * The protocol scheme to be registered.\n * @default '''\n * @example 'ftp'\n * @example 'git'\n */\n scheme: string;\n\n /**\n * If enabled, it allows optional slashes after the protocol.\n * @default false\n * @example true\n */\n optionalSlashes?: boolean;\n};\n\ntype ProtocolConfig = Array<ProtocolOptions | string>;\n\nfunction sanitizeUrl(\n inputUrl: string,\n baseUrl: string,\n protocols?: ProtocolConfig,\n): string {\n try {\n const url = new URL(inputUrl, baseUrl);\n\n if (isAllowedUri(url.href, protocols)) {\n return url.href;\n }\n } catch {\n // If URL creation fails, it's considered invalid\n }\n return \"#\";\n}\n\nfunction isAllowedUri(uri: string | undefined, protocols?: ProtocolConfig) {\n const allowedProtocols: string[] = [\n \"http\",\n \"https\",\n \"ftp\",\n \"ftps\",\n \"mailto\",\n \"tel\",\n \"callto\",\n \"sms\",\n \"cid\",\n \"xmpp\",\n ];\n\n if (protocols) {\n protocols.forEach((protocol) => {\n const nextProtocol =\n typeof protocol === \"string\" ? protocol : protocol.scheme;\n\n if (nextProtocol) {\n allowedProtocols.push(nextProtocol);\n }\n });\n }\n\n const ATTR_WHITESPACE =\n // eslint-disable-next-line no-control-regex\n /[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]/g;\n\n return (\n !uri ||\n uri.replace(ATTR_WHITESPACE, \"\").match(\n new RegExp(\n // eslint-disable-next-line no-useless-escape, regexp/no-obscure-range\n `^(?:(?:${allowedProtocols.join(\"|\")}):|[^a-z]|[a-z0-9+.\\-]+(?:[^a-z+.\\-:]|$))`,\n \"i\",\n ),\n )\n );\n}\n"],"names":["LinkEditor","editor","hideWhenUnavailable","onSetLink","onOpenChange","autoOpenOnLinkActive","onClick","className","buttonProps","isOpen","setIsOpen","useState","canSet","isActive","url","setUrl","setLink","removeLink","openLink","useLinkPopover","handleOnOpenChange","useCallback","nextIsOpen","handleSetLink","handleRemoveLink","handleClick","event","handleIputKeyDown","useEffect","Popover","Toolbar","cn","IconLink","e","IconCornerDownLeft","IconExternalLink","IconLinkOff","config","isVisible","useLinkState","linkHandler","useLinkHandler","props","canSetLink","useEditorState","state","setIsVisible","handleSelectionUpdate","shouldShowLinkButton","isMarkInSchema","markName","href","updateLinkState","selection","isEmpty","chain","target","features","safeUrl","sanitizeUrl","inputUrl","baseUrl","protocols","isAllowedUri","uri","allowedProtocols","ATTR_WHITESPACE"],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { IconBold as r, IconItalic as c, IconStrikethrough as u, IconUnderline as s, IconHighlight as h } from "@tabler/icons-react";
|
|
2
|
+
import { useEditorState as g } from "@tiptap/react";
|
|
3
|
+
import { Toolbar as t } from "radix-ui";
|
|
4
|
+
function b({ editor: l }) {
|
|
5
|
+
const n = g({
|
|
6
|
+
editor: l,
|
|
7
|
+
selector: (e) => {
|
|
8
|
+
const i = new Array();
|
|
9
|
+
return e.editor?.isActive("bold") && i.push("bold"), e.editor?.isActive("italic") && i.push("italic"), e.editor?.isActive("strike") && i.push("strikethrough"), e.editor?.isActive("underline") && i.push("underline"), e.editor?.isActive("highlight") && i.push("highlight"), i;
|
|
10
|
+
}
|
|
11
|
+
}), o = (e) => {
|
|
12
|
+
if (l)
|
|
13
|
+
for (const i of a)
|
|
14
|
+
e.includes(i.value) ? l.chain().focus().setMark(i.value).run() : l.chain().focus().unsetMark(i.value).run();
|
|
15
|
+
};
|
|
16
|
+
return /* @__PURE__ */ React.createElement(
|
|
17
|
+
t.ToggleGroup,
|
|
18
|
+
{
|
|
19
|
+
type: "multiple",
|
|
20
|
+
className: "flex items-center gap-0.5",
|
|
21
|
+
"aria-label": "Text formatting",
|
|
22
|
+
value: n ?? [],
|
|
23
|
+
onValueChange: o
|
|
24
|
+
},
|
|
25
|
+
a.map((e) => /* @__PURE__ */ React.createElement(
|
|
26
|
+
t.ToggleItem,
|
|
27
|
+
{
|
|
28
|
+
key: e.value,
|
|
29
|
+
className: "inline-flex h-7 flex-shrink-0 flex-grow-0 basis-auto cursor-pointer items-center justify-center rounded border-none bg-transparent px-2 leading-none hover:bg-elevation-250 focus:relative focus:outline data-[state=on]:bg-elevation-600 data-[state=on]:text-elevation-0",
|
|
30
|
+
value: e.value,
|
|
31
|
+
"aria-label": e.label
|
|
32
|
+
},
|
|
33
|
+
/* @__PURE__ */ React.createElement(e.icon, { size: 16 })
|
|
34
|
+
))
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
const a = [
|
|
38
|
+
{ value: "bold", label: "Bold", icon: r },
|
|
39
|
+
{ value: "italic", label: "Italic", icon: c },
|
|
40
|
+
{ value: "strike", label: "Strikethrough", icon: u },
|
|
41
|
+
{ value: "underline", label: "Underline", icon: s },
|
|
42
|
+
{ value: "highlight", label: "Highlight", icon: h }
|
|
43
|
+
];
|
|
44
|
+
export {
|
|
45
|
+
b as MarkControls
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=MarkControls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MarkControls.js","sources":["../../../../src/components/inputs/toolbar/MarkControls.tsx"],"sourcesContent":["import type { IconProps } from \"@tabler/icons-react\";\nimport type { Editor } from \"@tiptap/react\";\nimport {\n IconBold,\n IconHighlight,\n IconItalic,\n IconStrikethrough,\n IconUnderline,\n} from \"@tabler/icons-react\";\nimport { useEditorState } from \"@tiptap/react\";\nimport { Toolbar } from \"radix-ui\";\n\nexport interface MarkControlsProps {\n editor: Editor | null;\n}\n\nexport function MarkControls({ editor }: MarkControlsProps) {\n const value = useEditorState<string[]>({\n editor,\n selector: (state) => {\n const selected = new Array<string>();\n\n if (state.editor?.isActive(\"bold\")) selected.push(\"bold\");\n if (state.editor?.isActive(\"italic\")) selected.push(\"italic\");\n if (state.editor?.isActive(\"strike\")) selected.push(\"strikethrough\");\n if (state.editor?.isActive(\"underline\")) selected.push(\"underline\");\n if (state.editor?.isActive(\"highlight\")) selected.push(\"highlight\");\n return selected;\n },\n });\n\n const handleChange = (value: string[]) => {\n if (!editor) return;\n for (const option of FORMAT_OPTIONS) {\n if (value.includes(option.value)) {\n editor.chain().focus().setMark(option.value).run();\n } else {\n editor.chain().focus().unsetMark(option.value).run();\n }\n }\n };\n\n return (\n <Toolbar.ToggleGroup\n type=\"multiple\"\n className=\"flex items-center gap-0.5\"\n aria-label=\"Text formatting\"\n value={value ?? []}\n onValueChange={handleChange}\n >\n {FORMAT_OPTIONS.map((option) => (\n <Toolbar.ToggleItem\n key={option.value}\n className=\"inline-flex h-7 flex-shrink-0 flex-grow-0 basis-auto cursor-pointer items-center justify-center rounded border-none bg-transparent px-2 leading-none hover:bg-elevation-250 focus:relative focus:outline data-[state=on]:bg-elevation-600 data-[state=on]:text-elevation-0\"\n value={option.value}\n aria-label={option.label}\n >\n <option.icon size={16} />\n </Toolbar.ToggleItem>\n ))}\n </Toolbar.ToggleGroup>\n );\n}\n\n// MARK: Options\n\ntype FormatType = \"bold\" | \"italic\" | \"strike\" | \"underline\" | \"highlight\";\nconst FORMAT_OPTIONS: {\n value: FormatType;\n label: string;\n icon: React.FunctionComponent<IconProps>;\n}[] = [\n { value: \"bold\", label: \"Bold\", icon: IconBold },\n { value: \"italic\", label: \"Italic\", icon: IconItalic },\n { value: \"strike\", label: \"Strikethrough\", icon: IconStrikethrough },\n { value: \"underline\", label: \"Underline\", icon: IconUnderline },\n { value: \"highlight\", label: \"Highlight\", icon: IconHighlight },\n];\n"],"names":["MarkControls","editor","value","useEditorState","state","selected","handleChange","option","FORMAT_OPTIONS","Toolbar","IconBold","IconItalic","IconStrikethrough","IconUnderline","IconHighlight"],"mappings":";;;AAgBO,SAASA,EAAa,EAAE,QAAAC,KAA6B;AAC1D,QAAMC,IAAQC,EAAyB;AAAA,IACrC,QAAAF;AAAA,IACA,UAAU,CAACG,MAAU;AACnB,YAAMC,IAAW,IAAI,MAAA;AAErB,aAAID,EAAM,QAAQ,SAAS,MAAM,KAAGC,EAAS,KAAK,MAAM,GACpDD,EAAM,QAAQ,SAAS,QAAQ,KAAGC,EAAS,KAAK,QAAQ,GACxDD,EAAM,QAAQ,SAAS,QAAQ,KAAGC,EAAS,KAAK,eAAe,GAC/DD,EAAM,QAAQ,SAAS,WAAW,KAAGC,EAAS,KAAK,WAAW,GAC9DD,EAAM,QAAQ,SAAS,WAAW,KAAGC,EAAS,KAAK,WAAW,GAC3DA;AAAA,IACT;AAAA,EAAA,CACD,GAEKC,IAAe,CAACJ,MAAoB;AACxC,QAAKD;AACL,iBAAWM,KAAUC;AACnB,QAAIN,EAAM,SAASK,EAAO,KAAK,IAC7BN,EAAO,MAAA,EAAQ,MAAA,EAAQ,QAAQM,EAAO,KAAK,EAAE,IAAA,IAE7CN,EAAO,MAAA,EAAQ,MAAA,EAAQ,UAAUM,EAAO,KAAK,EAAE,IAAA;AAAA,EAGrD;AAEA,SACE,sBAAA;AAAA,IAACE,EAAQ;AAAA,IAAR;AAAA,MACC,MAAK;AAAA,MACL,WAAU;AAAA,MACV,cAAW;AAAA,MACX,OAAOP,KAAS,CAAA;AAAA,MAChB,eAAeI;AAAA,IAAA;AAAA,IAEdE,EAAe,IAAI,CAACD,MACnB,sBAAA;AAAA,MAACE,EAAQ;AAAA,MAAR;AAAA,QACC,KAAKF,EAAO;AAAA,QACZ,WAAU;AAAA,QACV,OAAOA,EAAO;AAAA,QACd,cAAYA,EAAO;AAAA,MAAA;AAAA,MAEnB,sBAAA,cAACA,EAAO,MAAP,EAAY,MAAM,GAAA,CAAI;AAAA,IAAA,CAE1B;AAAA,EAAA;AAGP;AAKA,MAAMC,IAIA;AAAA,EACJ,EAAE,OAAO,QAAQ,OAAO,QAAQ,MAAME,EAAA;AAAA,EACtC,EAAE,OAAO,UAAU,OAAO,UAAU,MAAMC,EAAA;AAAA,EAC1C,EAAE,OAAO,UAAU,OAAO,iBAAiB,MAAMC,EAAA;AAAA,EACjD,EAAE,OAAO,aAAa,OAAO,aAAa,MAAMC,EAAA;AAAA,EAChD,EAAE,OAAO,aAAa,OAAO,aAAa,MAAMC,EAAA;AAClD;"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Toolbar as t } from "radix-ui";
|
|
2
|
+
import { cn as a } from "@/utils/cn";
|
|
3
|
+
import { AlignmentControls as o } from "./AlignmentControls.js";
|
|
4
|
+
import { BlockElementSelect as n } from "./BlockElementSelect.js";
|
|
5
|
+
import { LinkEditor as l } from "./LinkEditor.js";
|
|
6
|
+
import { MarkControls as m } from "./MarkControls.js";
|
|
7
|
+
function x({ editor: e, className: r }) {
|
|
8
|
+
return /* @__PURE__ */ React.createElement(
|
|
9
|
+
t.Root,
|
|
10
|
+
{
|
|
11
|
+
className: a("flex rounded-md bg-elevation-100 px-2 py-1", r),
|
|
12
|
+
"aria-label": "Formatting options"
|
|
13
|
+
},
|
|
14
|
+
/* @__PURE__ */ React.createElement(n, { editor: e }),
|
|
15
|
+
/* @__PURE__ */ React.createElement(t.Separator, { className: "mx-2 w-px bg-border" }),
|
|
16
|
+
/* @__PURE__ */ React.createElement(m, { editor: e }),
|
|
17
|
+
/* @__PURE__ */ React.createElement(t.Separator, { className: "mx-2 w-px bg-border" }),
|
|
18
|
+
/* @__PURE__ */ React.createElement(o, { editor: e }),
|
|
19
|
+
/* @__PURE__ */ React.createElement(t.Separator, { className: "mx-2 w-px bg-border" }),
|
|
20
|
+
/* @__PURE__ */ React.createElement(
|
|
21
|
+
l,
|
|
22
|
+
{
|
|
23
|
+
editor: e,
|
|
24
|
+
className: "inline-flex h-7 flex-shrink-0 flex-grow-0 basis-auto cursor-pointer items-center justify-center rounded border-none bg-transparent px-2 leading-none hover:bg-elevation-250 focus:relative focus:outline"
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
x as RichTextToolbar
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=RichTextToolbar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RichTextToolbar.js","sources":["../../../../src/components/inputs/toolbar/RichTextToolbar.tsx"],"sourcesContent":["import type { Editor } from \"@tiptap/react\";\nimport { Toolbar } from \"radix-ui\";\n\nimport { cn } from \"@/utils/cn\";\n\nimport { AlignmentControls } from \"./AlignmentControls\";\nimport { BlockElementSelect } from \"./BlockElementSelect\";\nimport { LinkEditor } from \"./LinkEditor\";\nimport { MarkControls } from \"./MarkControls\";\n\nexport interface RichTextToolbarProps {\n editor: Editor | null;\n className?: string;\n}\n\n// TODO add horizontal rule\n\nexport function RichTextToolbar({ editor, className }: RichTextToolbarProps) {\n return (\n <Toolbar.Root\n className={cn(\"flex rounded-md bg-elevation-100 px-2 py-1\", className)}\n aria-label=\"Formatting options\"\n >\n <BlockElementSelect editor={editor} />\n\n <Toolbar.Separator className=\"mx-2 w-px bg-border\" />\n\n <MarkControls editor={editor} />\n\n <Toolbar.Separator className=\"mx-2 w-px bg-border\" />\n\n <AlignmentControls editor={editor} />\n\n <Toolbar.Separator className=\"mx-2 w-px bg-border\" />\n\n <LinkEditor\n editor={editor}\n className=\"inline-flex h-7 flex-shrink-0 flex-grow-0 basis-auto cursor-pointer items-center justify-center rounded border-none bg-transparent px-2 leading-none hover:bg-elevation-250 focus:relative focus:outline\"\n />\n </Toolbar.Root>\n );\n}\n"],"names":["RichTextToolbar","editor","className","Toolbar","cn","BlockElementSelect","MarkControls","AlignmentControls","LinkEditor"],"mappings":";;;;;;AAiBO,SAASA,EAAgB,EAAE,QAAAC,GAAQ,WAAAC,KAAmC;AAC3E,SACE,sBAAA;AAAA,IAACC,EAAQ;AAAA,IAAR;AAAA,MACC,WAAWC,EAAG,8CAA8CF,CAAS;AAAA,MACrE,cAAW;AAAA,IAAA;AAAA,IAEX,sBAAA,cAACG,KAAmB,QAAAJ,EAAA,CAAgB;AAAA,IAEpC,sBAAA,cAACE,EAAQ,WAAR,EAAkB,WAAU,uBAAsB;AAAA,IAEnD,sBAAA,cAACG,KAAa,QAAAL,EAAA,CAAgB;AAAA,IAE9B,sBAAA,cAACE,EAAQ,WAAR,EAAkB,WAAU,uBAAsB;AAAA,IAEnD,sBAAA,cAACI,KAAkB,QAAAN,EAAA,CAAgB;AAAA,IAEnC,sBAAA,cAACE,EAAQ,WAAR,EAAkB,WAAU,uBAAsB;AAAA,IAEnD,sBAAA;AAAA,MAACK;AAAA,MAAA;AAAA,QACC,QAAAP;AAAA,QACA,WAAU;AAAA,MAAA;AAAA,IAAA;AAAA,EACZ;AAGN;"}
|