payload-intl 0.0.2 → 0.0.4
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/dist/components/MessageController.js +29 -28
- package/dist/components/MessageController.js.map +1 -1
- package/dist/components/MessagesForm.js +92 -76
- package/dist/components/MessagesForm.js.map +1 -1
- package/dist/components/actions/JsonImport.d.ts +1 -1
- package/dist/components/actions/JsonImport.js +63 -0
- package/dist/components/actions/JsonImport.js.map +1 -0
- package/dist/components/actions/Move.d.ts +1 -1
- package/dist/components/inputs/InputWrapper.d.ts +1 -1
- package/dist/components/inputs/InputWrapper.js +24 -18
- package/dist/components/inputs/InputWrapper.js.map +1 -1
- package/dist/components/inputs/MessageInput.d.ts +1 -1
- package/dist/components/inputs/MessageInput.js +27 -41
- package/dist/components/inputs/MessageInput.js.map +1 -1
- package/dist/components/inputs/RichTextInput.js +62 -58
- package/dist/components/inputs/RichTextInput.js.map +1 -1
- package/dist/components/inputs/toolbar/AlignmentControls.d.ts +1 -1
- package/dist/components/inputs/toolbar/AlignmentControls.js +47 -44
- package/dist/components/inputs/toolbar/AlignmentControls.js.map +1 -1
- package/dist/components/inputs/toolbar/BlockElementSelect.d.ts +1 -1
- package/dist/components/inputs/toolbar/BlockElementSelect.js +60 -54
- package/dist/components/inputs/toolbar/BlockElementSelect.js.map +1 -1
- package/dist/components/inputs/toolbar/LinkEditor.d.ts +1 -1
- package/dist/components/inputs/toolbar/LinkEditor.js +182 -170
- package/dist/components/inputs/toolbar/LinkEditor.js.map +1 -1
- package/dist/components/inputs/toolbar/MarkControls.d.ts +1 -1
- package/dist/components/inputs/toolbar/MarkControls.js +29 -28
- package/dist/components/inputs/toolbar/MarkControls.js.map +1 -1
- package/dist/components/inputs/toolbar/RichTextToolbar.d.ts +1 -1
- package/dist/components/inputs/toolbar/RichTextToolbar.js +29 -26
- package/dist/components/inputs/toolbar/RichTextToolbar.js.map +1 -1
- package/dist/components/inputs/variables/VariableChip.d.ts +1 -1
- package/dist/components/inputs/variables/VariableChip.js +55 -49
- package/dist/components/inputs/variables/VariableChip.js.map +1 -1
- package/dist/components/inputs/variables/VariableSuggestion.d.ts +1 -1
- package/dist/components/inputs/variables/VariableSuggestion.js +24 -23
- package/dist/components/inputs/variables/VariableSuggestion.js.map +1 -1
- package/dist/components/inputs/variables/editors/DateVariableEditor.d.ts +1 -1
- package/dist/components/inputs/variables/editors/PluralVariableEditor.d.ts +1 -1
- package/dist/components/inputs/variables/editors/PluralVariableEditor.js +151 -122
- package/dist/components/inputs/variables/editors/PluralVariableEditor.js.map +1 -1
- package/dist/components/inputs/variables/editors/SelectVariableEditor.d.ts +1 -1
- package/dist/components/inputs/variables/editors/SelectVariableEditor.js +33 -29
- package/dist/components/inputs/variables/editors/SelectVariableEditor.js.map +1 -1
- package/dist/components/inputs/variables/editors/TagVariableEditor.d.ts +1 -1
- package/dist/components/inputs/variables/editors/TagVariableEditor.js +13 -12
- package/dist/components/inputs/variables/editors/TagVariableEditor.js.map +1 -1
- package/dist/components/inputs/variables/editors/TimeVariableEditor.d.ts +1 -1
- package/dist/components/inputs/variables/pickers/NumericVariablePicker.d.ts +1 -1
- package/dist/components/inputs/variables/pickers/NumericVariablePicker.js +40 -36
- package/dist/components/inputs/variables/pickers/NumericVariablePicker.js.map +1 -1
- package/dist/components/inputs/variables/pickers/TemporalElementEditor.d.ts +1 -1
- package/dist/components/layout/MessageField.js +42 -38
- package/dist/components/layout/MessageField.js.map +1 -1
- package/dist/components/layout/MessagesTabs.js +31 -30
- package/dist/components/layout/MessagesTabs.js.map +1 -1
- package/dist/components/layout/MessagesTree.js +52 -51
- package/dist/components/layout/MessagesTree.js.map +1 -1
- package/dist/context/messages-form.d.ts +1 -1
- package/dist/context/messages-form.js +12 -11
- package/dist/context/messages-form.js.map +1 -1
- package/dist/exports/link.d.ts +1 -1
- package/dist/exports/link.js +11 -10
- package/dist/exports/link.js.map +1 -1
- package/dist/exports/view.d.ts +1 -1
- package/dist/exports/view.js +43 -40
- package/dist/exports/view.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/package.json +1 -2
|
@@ -1,205 +1,217 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
1
|
+
import { jsxs as v, jsx as l } from "react/jsx-runtime";
|
|
2
|
+
import { IconLink as N, IconCornerDownLeft as j, IconExternalLink as P, IconLinkOff as W } from "@tabler/icons-react";
|
|
3
|
+
import { useEditorState as B } from "@tiptap/react";
|
|
4
|
+
import { Popover as g, Toolbar as m } from "radix-ui";
|
|
5
|
+
import { useState as L, useCallback as p, useEffect as k } from "react";
|
|
6
|
+
import { cn as M } from "../../../utils/cn.js";
|
|
7
|
+
function Q({
|
|
7
8
|
editor: e,
|
|
8
|
-
hideWhenUnavailable:
|
|
9
|
-
onSetLink:
|
|
10
|
-
onOpenChange:
|
|
11
|
-
autoOpenOnLinkActive:
|
|
12
|
-
onClick:
|
|
13
|
-
className:
|
|
14
|
-
...
|
|
9
|
+
hideWhenUnavailable: n = !1,
|
|
10
|
+
onSetLink: t,
|
|
11
|
+
onOpenChange: i,
|
|
12
|
+
autoOpenOnLinkActive: u = !0,
|
|
13
|
+
onClick: f,
|
|
14
|
+
className: d,
|
|
15
|
+
...o
|
|
15
16
|
}) {
|
|
16
|
-
const [
|
|
17
|
+
const [r, s] = L(!0), { canSet: a, isActive: h, url: b, setUrl: C, setLink: x, removeLink: w, openLink: z } = T({
|
|
17
18
|
editor: e,
|
|
18
|
-
hideWhenUnavailable:
|
|
19
|
-
onSetLink:
|
|
20
|
-
}),
|
|
21
|
-
(
|
|
22
|
-
|
|
19
|
+
hideWhenUnavailable: n,
|
|
20
|
+
onSetLink: t
|
|
21
|
+
}), A = p(
|
|
22
|
+
(c) => {
|
|
23
|
+
s(c), i?.(c);
|
|
23
24
|
},
|
|
24
|
-
[
|
|
25
|
-
),
|
|
26
|
-
|
|
27
|
-
}, [
|
|
28
|
-
|
|
29
|
-
}, [
|
|
30
|
-
(
|
|
31
|
-
|
|
25
|
+
[i]
|
|
26
|
+
), y = p(() => {
|
|
27
|
+
x(), s(!1);
|
|
28
|
+
}, [x]), E = p(() => {
|
|
29
|
+
w(), s(!1);
|
|
30
|
+
}, [w]), I = p(
|
|
31
|
+
(c) => {
|
|
32
|
+
f?.(c), !c.defaultPrevented && s(!r);
|
|
32
33
|
},
|
|
33
|
-
[
|
|
34
|
-
),
|
|
35
|
-
|
|
34
|
+
[f, r]
|
|
35
|
+
), R = (c) => {
|
|
36
|
+
c.key === "Enter" && (c.preventDefault(), y());
|
|
36
37
|
};
|
|
37
|
-
return
|
|
38
|
-
|
|
39
|
-
}, [
|
|
40
|
-
|
|
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",
|
|
38
|
+
return k(() => {
|
|
39
|
+
u && h && s(!0);
|
|
40
|
+
}, [u, h]), /* @__PURE__ */ v(g.Root, { open: r, onOpenChange: A, children: [
|
|
41
|
+
/* @__PURE__ */ l(g.Trigger, { asChild: !0, children: /* @__PURE__ */ l(
|
|
42
|
+
m.Button,
|
|
60
43
|
{
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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" }
|
|
44
|
+
className: M(d, {
|
|
45
|
+
"bg-elevation-600 text-elevation-0": h
|
|
46
|
+
}),
|
|
47
|
+
disabled: !a,
|
|
48
|
+
onClick: I,
|
|
49
|
+
"aria-label": "Link",
|
|
50
|
+
...o,
|
|
51
|
+
children: /* @__PURE__ */ l(N, { size: 16 })
|
|
72
52
|
}
|
|
73
|
-
),
|
|
74
|
-
|
|
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,
|
|
53
|
+
) }),
|
|
54
|
+
/* @__PURE__ */ l(
|
|
55
|
+
g.Content,
|
|
95
56
|
{
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
57
|
+
className: "z-50 rounded-md border border-border bg-elevation-100 p-2 shadow-md",
|
|
58
|
+
sideOffset: 8,
|
|
59
|
+
align: "end",
|
|
60
|
+
children: /* @__PURE__ */ v(m.Root, { className: "flex items-center gap-2", children: [
|
|
61
|
+
/* @__PURE__ */ v("div", { className: "flex items-center overflow-hidden rounded-md border border-border focus-within:border-elevation-600", children: [
|
|
62
|
+
/* @__PURE__ */ l(
|
|
63
|
+
"input",
|
|
64
|
+
{
|
|
65
|
+
type: "url",
|
|
66
|
+
placeholder: "Paste a link...",
|
|
67
|
+
autoComplete: "off",
|
|
68
|
+
autoCorrect: "off",
|
|
69
|
+
autoCapitalize: "off",
|
|
70
|
+
value: b,
|
|
71
|
+
onChange: (c) => C(c.target.value),
|
|
72
|
+
onKeyDown: R,
|
|
73
|
+
className: "h-10 flex-1 border-none bg-input px-2 py-1 focus:outline-none",
|
|
74
|
+
"aria-label": "Link URL",
|
|
75
|
+
style: { minWidth: "200px" }
|
|
76
|
+
}
|
|
77
|
+
),
|
|
78
|
+
/* @__PURE__ */ l(
|
|
79
|
+
m.Button,
|
|
80
|
+
{
|
|
81
|
+
type: "button",
|
|
82
|
+
title: "Apply link",
|
|
83
|
+
disabled: !b && !h,
|
|
84
|
+
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",
|
|
85
|
+
onClick: y,
|
|
86
|
+
children: /* @__PURE__ */ l(j, { size: 16 })
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
] }),
|
|
90
|
+
/* @__PURE__ */ l(m.Separator, { className: "mx-1 h-10 w-px bg-border" }),
|
|
91
|
+
/* @__PURE__ */ l(
|
|
92
|
+
m.Button,
|
|
93
|
+
{
|
|
94
|
+
type: "button",
|
|
95
|
+
className: "flex size-10 items-center justify-center rounded-md border-none bg-transparent p-0 hover:bg-elevation-250",
|
|
96
|
+
title: "Open in new window",
|
|
97
|
+
disabled: !b && !h,
|
|
98
|
+
onClick: () => z(),
|
|
99
|
+
children: /* @__PURE__ */ l(P, { size: 16 })
|
|
100
|
+
}
|
|
101
|
+
),
|
|
102
|
+
/* @__PURE__ */ l(
|
|
103
|
+
m.Button,
|
|
104
|
+
{
|
|
105
|
+
type: "button",
|
|
106
|
+
title: "Remove link",
|
|
107
|
+
className: "flex size-10 items-center justify-center rounded-md border-none bg-transparent p-0 hover:bg-elevation-250",
|
|
108
|
+
disabled: !b && !h,
|
|
109
|
+
onClick: E,
|
|
110
|
+
children: /* @__PURE__ */ l(W, { size: 16 })
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
] })
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
] });
|
|
105
117
|
}
|
|
106
|
-
function
|
|
107
|
-
const { editor:
|
|
108
|
-
editor:
|
|
109
|
-
hideWhenUnavailable:
|
|
110
|
-
}),
|
|
111
|
-
editor:
|
|
112
|
-
onSetLink:
|
|
118
|
+
function T(e) {
|
|
119
|
+
const { editor: n, hideWhenUnavailable: t = !1, onSetLink: i } = e || {}, { isVisible: u, canSet: f, isActive: d } = S({
|
|
120
|
+
editor: n || null,
|
|
121
|
+
hideWhenUnavailable: t
|
|
122
|
+
}), o = H({
|
|
123
|
+
editor: n || null,
|
|
124
|
+
onSetLink: i
|
|
113
125
|
});
|
|
114
126
|
return {
|
|
115
|
-
isVisible:
|
|
116
|
-
canSet:
|
|
117
|
-
isActive:
|
|
118
|
-
...
|
|
127
|
+
isVisible: u,
|
|
128
|
+
canSet: f,
|
|
129
|
+
isActive: d,
|
|
130
|
+
...o
|
|
119
131
|
};
|
|
120
132
|
}
|
|
121
|
-
function
|
|
122
|
-
const { editor:
|
|
123
|
-
editor:
|
|
124
|
-
selector: (
|
|
125
|
-
}) ?? !1, [
|
|
126
|
-
return
|
|
127
|
-
if (!
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
editor:
|
|
132
|
-
hideWhenUnavailable:
|
|
133
|
+
function S(e) {
|
|
134
|
+
const { editor: n, hideWhenUnavailable: t = !1 } = e, i = U(n), u = B({
|
|
135
|
+
editor: n,
|
|
136
|
+
selector: (o) => o.editor?.isEditable && o?.editor?.isActive("link")
|
|
137
|
+
}) ?? !1, [f, d] = L(!1);
|
|
138
|
+
return k(() => {
|
|
139
|
+
if (!n) return;
|
|
140
|
+
const o = () => {
|
|
141
|
+
d(
|
|
142
|
+
D({
|
|
143
|
+
editor: n,
|
|
144
|
+
hideWhenUnavailable: t
|
|
133
145
|
})
|
|
134
146
|
);
|
|
135
147
|
};
|
|
136
|
-
return
|
|
137
|
-
|
|
148
|
+
return o(), n.on("selectionUpdate", o), () => {
|
|
149
|
+
n.off("selectionUpdate", o);
|
|
138
150
|
};
|
|
139
|
-
}, [
|
|
140
|
-
isVisible:
|
|
141
|
-
canSet:
|
|
142
|
-
isActive:
|
|
151
|
+
}, [n, t]), {
|
|
152
|
+
isVisible: f,
|
|
153
|
+
canSet: i,
|
|
154
|
+
isActive: u
|
|
143
155
|
};
|
|
144
156
|
}
|
|
145
|
-
function
|
|
157
|
+
function U(e) {
|
|
146
158
|
return !e || !e.isEditable ? !1 : e.can().setMark("link");
|
|
147
159
|
}
|
|
148
|
-
function
|
|
149
|
-
const { editor:
|
|
150
|
-
return !
|
|
160
|
+
function D(e) {
|
|
161
|
+
const { editor: n, hideWhenUnavailable: t } = e;
|
|
162
|
+
return !O("link", n) || !n ? !1 : t && !n.isActive("code") ? U(n) : !0;
|
|
151
163
|
}
|
|
152
|
-
const
|
|
153
|
-
function
|
|
154
|
-
const [
|
|
155
|
-
|
|
164
|
+
const O = (e, n) => n?.schema ? n.schema.spec.marks.get(e) !== void 0 : !1;
|
|
165
|
+
function H({ editor: e, onSetLink: n }) {
|
|
166
|
+
const [t, i] = L(null), { isActive: u } = S({ editor: e, hideWhenUnavailable: !1 });
|
|
167
|
+
k(() => {
|
|
156
168
|
if (!e) return;
|
|
157
|
-
const { href:
|
|
158
|
-
|
|
159
|
-
}, [e,
|
|
169
|
+
const { href: r } = e.getAttributes("link");
|
|
170
|
+
u && t === null && i(r || "");
|
|
171
|
+
}, [e, t, u]), k(() => {
|
|
160
172
|
if (!e) return;
|
|
161
|
-
const
|
|
162
|
-
const { href:
|
|
163
|
-
|
|
173
|
+
const r = () => {
|
|
174
|
+
const { href: s } = e.getAttributes("link");
|
|
175
|
+
i(s || "");
|
|
164
176
|
};
|
|
165
|
-
return e.on("selectionUpdate",
|
|
166
|
-
e.off("selectionUpdate",
|
|
177
|
+
return e.on("selectionUpdate", r), () => {
|
|
178
|
+
e.off("selectionUpdate", r);
|
|
167
179
|
};
|
|
168
180
|
}, [e]);
|
|
169
|
-
const
|
|
170
|
-
if (!
|
|
171
|
-
const { selection:
|
|
172
|
-
let
|
|
173
|
-
|
|
174
|
-
}, [e,
|
|
175
|
-
e && (e.chain().focus().extendMarkRange("link").unsetLink().setMeta("preventAutolink", !0).run(),
|
|
176
|
-
}, [e]),
|
|
177
|
-
(
|
|
178
|
-
if (!
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
+
const f = p(() => {
|
|
182
|
+
if (!t || !e) return;
|
|
183
|
+
const { selection: r } = e.state, s = r.empty;
|
|
184
|
+
let a = e.chain().focus();
|
|
185
|
+
a = a.extendMarkRange("link").setLink({ href: t }), s && (a = a.insertContent({ type: "text", text: t })), a.run(), i(null), n?.();
|
|
186
|
+
}, [e, n, t]), d = p(() => {
|
|
187
|
+
e && (e.chain().focus().extendMarkRange("link").unsetLink().setMeta("preventAutolink", !0).run(), i(""));
|
|
188
|
+
}, [e]), o = p(
|
|
189
|
+
(r = "_blank", s = "noopener,noreferrer") => {
|
|
190
|
+
if (!t) return;
|
|
191
|
+
const a = V(t, window.location.href);
|
|
192
|
+
a !== "#" && window.open(a, r, s);
|
|
181
193
|
},
|
|
182
|
-
[
|
|
194
|
+
[t]
|
|
183
195
|
);
|
|
184
196
|
return {
|
|
185
|
-
url:
|
|
186
|
-
setUrl:
|
|
187
|
-
setLink:
|
|
188
|
-
removeLink:
|
|
189
|
-
openLink:
|
|
197
|
+
url: t || "",
|
|
198
|
+
setUrl: i,
|
|
199
|
+
setLink: f,
|
|
200
|
+
removeLink: d,
|
|
201
|
+
openLink: o
|
|
190
202
|
};
|
|
191
203
|
}
|
|
192
|
-
function
|
|
204
|
+
function V(e, n, t) {
|
|
193
205
|
try {
|
|
194
|
-
const
|
|
195
|
-
if (
|
|
196
|
-
return
|
|
206
|
+
const i = new URL(e, n);
|
|
207
|
+
if (K(i.href, t))
|
|
208
|
+
return i.href;
|
|
197
209
|
} catch {
|
|
198
210
|
}
|
|
199
211
|
return "#";
|
|
200
212
|
}
|
|
201
|
-
function
|
|
202
|
-
const
|
|
213
|
+
function K(e, n) {
|
|
214
|
+
const t = [
|
|
203
215
|
"http",
|
|
204
216
|
"https",
|
|
205
217
|
"ftp",
|
|
@@ -210,21 +222,21 @@ function H(e, t) {
|
|
|
210
222
|
"sms",
|
|
211
223
|
"cid",
|
|
212
224
|
"xmpp"
|
|
213
|
-
],
|
|
225
|
+
], i = (
|
|
214
226
|
// eslint-disable-next-line no-control-regex
|
|
215
227
|
/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g
|
|
216
228
|
);
|
|
217
|
-
return !e || e.replace(
|
|
229
|
+
return !e || e.replace(i, "").match(
|
|
218
230
|
new RegExp(
|
|
219
231
|
// eslint-disable-next-line no-useless-escape, regexp/no-obscure-range
|
|
220
|
-
`^(?:(?:${
|
|
232
|
+
`^(?:(?:${t.join("|")}):|[^a-z]|[a-z0-9+.-]+(?:[^a-z+.-:]|$))`,
|
|
221
233
|
"i"
|
|
222
234
|
)
|
|
223
235
|
);
|
|
224
236
|
}
|
|
225
237
|
export {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
238
|
+
Q as LinkEditor,
|
|
239
|
+
O as isMarkInSchema,
|
|
240
|
+
H as useLinkHandler
|
|
229
241
|
};
|
|
230
242
|
//# sourceMappingURL=LinkEditor.js.map
|
|
@@ -1 +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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"}
|
|
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","jsx","Toolbar","cn","IconLink","jsxs","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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"}
|
|
@@ -2,4 +2,4 @@ import { Editor } from '@tiptap/react';
|
|
|
2
2
|
export interface MarkControlsProps {
|
|
3
3
|
editor: Editor | null;
|
|
4
4
|
}
|
|
5
|
-
export declare function MarkControls({ editor }: MarkControlsProps): import("react").JSX.Element;
|
|
5
|
+
export declare function MarkControls({ editor }: MarkControlsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,47 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { jsx as t } from "react/jsx-runtime";
|
|
2
|
+
import { IconBold as u, IconItalic as c, IconStrikethrough as s, IconUnderline as h, IconHighlight as g } from "@tabler/icons-react";
|
|
3
|
+
import { useEditorState as d } from "@tiptap/react";
|
|
4
|
+
import { Toolbar as n } from "radix-ui";
|
|
5
|
+
function p({ editor: l }) {
|
|
6
|
+
const r = d({
|
|
6
7
|
editor: l,
|
|
7
8
|
selector: (e) => {
|
|
8
9
|
const i = new Array();
|
|
9
10
|
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
|
}
|
|
11
|
-
}),
|
|
12
|
+
}), a = (e) => {
|
|
12
13
|
if (l)
|
|
13
|
-
for (const i of
|
|
14
|
+
for (const i of o)
|
|
14
15
|
e.includes(i.value) ? l.chain().focus().setMark(i.value).run() : l.chain().focus().unsetMark(i.value).run();
|
|
15
16
|
};
|
|
16
|
-
return /* @__PURE__ */
|
|
17
|
-
|
|
17
|
+
return /* @__PURE__ */ t(
|
|
18
|
+
n.ToggleGroup,
|
|
18
19
|
{
|
|
19
20
|
type: "multiple",
|
|
20
21
|
className: "flex items-center gap-0.5",
|
|
21
22
|
"aria-label": "Text formatting",
|
|
22
|
-
value:
|
|
23
|
-
onValueChange:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
23
|
+
value: r ?? [],
|
|
24
|
+
onValueChange: a,
|
|
25
|
+
children: o.map((e) => /* @__PURE__ */ t(
|
|
26
|
+
n.ToggleItem,
|
|
27
|
+
{
|
|
28
|
+
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",
|
|
29
|
+
value: e.value,
|
|
30
|
+
"aria-label": e.label,
|
|
31
|
+
children: /* @__PURE__ */ t(e.icon, { size: 16 })
|
|
32
|
+
},
|
|
33
|
+
e.value
|
|
34
|
+
))
|
|
35
|
+
}
|
|
35
36
|
);
|
|
36
37
|
}
|
|
37
|
-
const
|
|
38
|
-
{ value: "bold", label: "Bold", icon:
|
|
38
|
+
const o = [
|
|
39
|
+
{ value: "bold", label: "Bold", icon: u },
|
|
39
40
|
{ value: "italic", label: "Italic", icon: c },
|
|
40
|
-
{ value: "strike", label: "Strikethrough", icon:
|
|
41
|
-
{ value: "underline", label: "Underline", icon:
|
|
42
|
-
{ value: "highlight", label: "Highlight", icon:
|
|
41
|
+
{ value: "strike", label: "Strikethrough", icon: s },
|
|
42
|
+
{ value: "underline", label: "Underline", icon: h },
|
|
43
|
+
{ value: "highlight", label: "Highlight", icon: g }
|
|
43
44
|
];
|
|
44
45
|
export {
|
|
45
|
-
|
|
46
|
+
p as MarkControls
|
|
46
47
|
};
|
|
47
48
|
//# sourceMappingURL=MarkControls.js.map
|
|
@@ -1 +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":"
|
|
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","jsx","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,gBAAAE;AAAA,IAACC,EAAQ;AAAA,IAAR;AAAA,MACC,MAAK;AAAA,MACL,WAAU;AAAA,MACV,cAAW;AAAA,MACX,OAAOR,KAAS,CAAA;AAAA,MAChB,eAAeI;AAAA,MAEd,UAAAE,EAAe,IAAI,CAACD,MACnB,gBAAAE;AAAA,QAACC,EAAQ;AAAA,QAAR;AAAA,UAEC,WAAU;AAAA,UACV,OAAOH,EAAO;AAAA,UACd,cAAYA,EAAO;AAAA,UAEnB,UAAA,gBAAAE,EAACF,EAAO,MAAP,EAAY,MAAM,GAAA,CAAI;AAAA,QAAA;AAAA,QALlBA,EAAO;AAAA,MAAA,CAOf;AAAA,IAAA;AAAA,EAAA;AAGP;AAKA,MAAMC,IAIA;AAAA,EACJ,EAAE,OAAO,QAAQ,OAAO,QAAQ,MAAMG,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;"}
|
|
@@ -3,4 +3,4 @@ export interface RichTextToolbarProps {
|
|
|
3
3
|
editor: Editor | null;
|
|
4
4
|
className?: string;
|
|
5
5
|
}
|
|
6
|
-
export declare function RichTextToolbar({ editor, className }: RichTextToolbarProps): import("react").JSX.Element;
|
|
6
|
+
export declare function RichTextToolbar({ editor, className }: RichTextToolbarProps): import("react/jsx-runtime").JSX.Element;
|