@jcroger/tiptap-simple-editor 1.0.0

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/index.css ADDED
@@ -0,0 +1 @@
1
+ @layer ste-base{:root{--primary-color-filled: #228be6}}.tiptap.ProseMirror a{color:LinkText;text-decoration:underline}.tiptap.ProseMirror h1:not(:first-child),.tiptap.ProseMirror h2:not(:first-child),.tiptap.ProseMirror h3:not(:first-child),.tiptap.ProseMirror h4:not(:first-child),.tiptap.ProseMirror h5:not(:first-child),.tiptap.ProseMirror h6:not(:first-child),.tiptap.ProseMirror p:not(:first-child):not(td p):not(th p){margin-top:1.2rem}.tiptap.ProseMirror h1{font-size:48px;font-weight:400;line-height:1.125}.tiptap.ProseMirror h2{font-size:32px;font-weight:400;line-height:1.125}.tiptap.ProseMirror h3{font-size:22px;font-weight:400;line-height:1.125}.tiptap.ProseMirror h4{font-size:18px;font-weight:400;line-height:1.125}.tiptap.ProseMirror h5{font-size:14px;font-weight:400;line-height:1.125}.tiptap.ProseMirror h6{font-size:14px;font-weight:700;letter-spacing:1.4px;line-height:1.125;text-transform:uppercase}.tiptap.ProseMirror blockquote{border-left:3px solid var(--mantine-color-gray-4);padding-left:1rem;color:var(--mantine-color-gray-7);font-style:italic}.tiptap.ProseMirror img{max-width:100%;height:auto;display:block}.tiptap.ProseMirror p>img{display:inline-block}.tiptap.ProseMirror>img:not([data-type=emoji] img){margin:2rem 0;outline:.125rem solid transparent;border-radius:var(--mantine-radius-xs, .25rem)}.tiptap.ProseMirror img:not([data-type=emoji] img).ProseMirror-selectednode{outline-color:var(--primary-color-filled)}.tiptap.ProseMirror .tiptap-thread:has(>img){margin:2rem 0}.tiptap.ProseMirror .tiptap-thread:has(>img) img{outline:.125rem solid transparent;border-radius:var(--tt-radius-xs, .25rem)}.tiptap.ProseMirror .tiptap-thread img{margin:0}.tiptap.ProseMirror .tiptap-image-upload{margin:2rem 0}.tiptap.ProseMirror .tiptap-image-upload input[type=file]{display:none}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-dropzone{position:relative;width:3.125rem;height:3.75rem;display:inline-flex;align-items:flex-start;justify-content:center;-webkit-user-select:none;user-select:none}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-icon-container{position:absolute;width:1.75rem;height:1.75rem;bottom:0;right:0;background-color:var(--primary-color-filled);border-radius:.75rem;display:flex;align-items:center;justify-content:center}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-icon{width:.875rem;height:.875rem;color:#fff}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-dropzone-rect-primary{color:var(--mantine-color-gray-2);position:absolute}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-dropzone-rect-secondary{position:absolute;top:0;right:.25rem;bottom:0;color:var(--mantine-color-gray-3)}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-text{color:var(--mantine-color-gray-7);font-weight:500;font-size:.875rem;line-height:normal}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-text em{font-style:normal;text-decoration:underline}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-subtext{color:var(--mantine-color-gray-7);font-weight:600;line-height:normal;font-size:.75rem}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-drag-area{padding:2rem 1.5rem;border:1.5px dashed var(--mantine-color-gray-3);border-radius:var(--mantine-radius-default, .5rem);text-align:center;cursor:pointer;position:relative;overflow:hidden;transition:all .2s ease}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-drag-area:hover{border-color:var(--mantine-color-gray-4)}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-drag-area.drag-active{border-color:var(--mantine-color-gray-5);background-color:rgba(var(--primary-color-filled, 0, 123, 255),.05)}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-drag-area.drag-over{border-color:var(--primary-color-filled);background-color:#ffffff1a}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-content{display:flex;align-items:center;justify-content:center;flex-direction:column;gap:.25rem;-webkit-user-select:none;user-select:none}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-previews{display:flex;flex-direction:column;gap:.75rem}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-header{display:flex;align-items:center;justify-content:space-between;padding:.5rem 0;border-bottom:1px solid var(--mantine-color-gray-3);margin-bottom:.5rem}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-header span{font-size:.875rem;font-weight:500;color:var(--mantine-color-gray-7)}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-preview{border:1px solid var(--mantine-color-gray-3);border-radius:var(--mantine-radius-default, .5rem);overflow:hidden}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-preview .tiptap-image-upload-preview-content{padding:.75rem 1rem;display:flex;align-items:center;justify-content:space-between}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-preview .tiptap-image-upload-progress-track{height:4px;background-color:var(--mantine-color-gray-2)}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-preview .tiptap-image-upload-progress-track .tiptap-image-upload-progress-bar{height:100%;background-color:var(--primary-color-filled);transition:width .3s ease-out}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-preview .tiptap-image-upload-file-info{display:flex;align-items:center;gap:.75rem;min-width:0}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-preview .tiptap-image-upload-file-info .tiptap-image-upload-file-icon{flex-shrink:0;padding:.5rem;background-color:var(--primary-color-filled);border-radius:var(--mantine-radius-lg, .75rem)}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-preview .tiptap-image-upload-file-info .tiptap-image-upload-file-icon svg{width:.875rem;height:.875rem;color:#fff}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-preview .tiptap-image-upload-details{display:flex;flex-direction:column;gap:.125rem;min-width:0}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-preview .tiptap-image-upload-details .tiptap-image-upload-text{font-size:.875rem;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-preview .tiptap-image-upload-details .tiptap-image-upload-subtext{font-size:.75rem;color:var(--mantine-color-gray-6)}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-preview .tiptap-image-upload-actions{display:flex;align-items:center;gap:.5rem}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-preview .tiptap-image-upload-actions .tiptap-image-upload-progress-text{font-size:.75rem;color:var(--primary-color-filled);font-weight:600}@media(max-width:480px){.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-drag-area{padding:1.5rem 1rem}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-header{flex-direction:column;align-items:flex-start;gap:.5rem}.tiptap.ProseMirror .tiptap-image-upload .tiptap-image-upload-preview-content{padding:.75rem}}.tiptap.ProseMirror .ProseMirror-focused .ProseMirror-selectednode .tiptap-image-upload-drag-area{border-color:var(--primary-color-filled)}
package/dist/index.mjs ADDED
@@ -0,0 +1,1181 @@
1
+ import { jsx as t, jsxs as p, Fragment as X } from "react/jsx-runtime";
2
+ import { useRef as _, useState as A, useEffect as M, useCallback as O } from "react";
3
+ import { Popover as k, Group as G, TextInput as ce, ActionIcon as b, Divider as H, Stack as V, Button as Z, CloseIcon as Ie, useCombobox as Se, Combobox as $, Menu as z, Grid as B, ThemeIcon as ie, ColorSwatch as ne } from "@mantine/core";
4
+ import { FontAwesomeIcon as x } from "@fortawesome/react-fontawesome";
5
+ import { faCheck as de, faArrowUpRightFromSquare as ue, faTrash as K, faAlignLeft as J, faAlignCenter as ge, faAlignRight as he, faArrowsLeftRight as Ee, faLink as pe, faFile as Ne, faIndent as Te, faOutdent as Me, faImage as ze, faAlignJustify as De, faChevronDown as He, faListOl as me, faQuoteLeft as fe, faListUl as ve, faStrikethrough as Ue, faHighlighter as Be, faA as re } from "@fortawesome/free-solid-svg-icons";
6
+ import { NodeViewWrapper as be, ReactNodeViewRenderer as Ce, Node as Oe, mergeAttributes as Fe, useCurrentEditor as We, useEditorState as ye, useEditor as $e, EditorContent as Re } from "@tiptap/react";
7
+ import { TextStyle as Pe } from "@tiptap/extension-text-style";
8
+ import { Color as _e } from "@tiptap/extension-color";
9
+ import { Highlight as Ve } from "@tiptap/extension-highlight";
10
+ import qe from "@tiptap/starter-kit";
11
+ import je from "@tiptap/extension-underline";
12
+ import Xe from "@tiptap/extension-link";
13
+ import Ge from "@tiptap/extension-text-align";
14
+ import Ze from "@tiptap/extension-heading";
15
+ import { Selection as Ke, TextSelection as Je, NodeSelection as Qe } from "@tiptap/pm/state";
16
+ import { Image as Ye } from "@tiptap/extension-image";
17
+ import { Extension as et } from "@tiptap/core";
18
+ import { useDisclosure as tt } from "@mantine/hooks";
19
+ import { useHotkeys as it } from "react-hotkeys-hook";
20
+ const R = {
21
+ MIN_WIDTH: 10,
22
+ MAX_WIDTH: 100,
23
+ MIN_HEIGHT_PX: 70
24
+ }, nt = [25, 50, 75, 100], rt = [
25
+ { value: "left", icon: J, label: "Aligner à gauche" },
26
+ { value: "center", icon: ge, label: "Aligner au centre" },
27
+ { value: "right", icon: he, label: "Aligner à droite" }
28
+ ], at = (e) => {
29
+ const i = {
30
+ left: { marginLeft: 0, marginRight: "auto" },
31
+ right: { marginLeft: "auto", marginRight: 0 },
32
+ center: { marginLeft: "auto", marginRight: "auto" }
33
+ };
34
+ return i[e] || i.center;
35
+ }, ae = (e, i) => {
36
+ if (!(e != null && e.naturalWidth) || !(e != null && e.naturalHeight)) return R.MIN_WIDTH;
37
+ const l = e.naturalWidth / e.naturalHeight, r = R.MIN_HEIGHT_PX * l;
38
+ return Math.max(R.MIN_WIDTH, r / i * 100);
39
+ }, le = ({ direction: e, onMouseDown: i }) => /* @__PURE__ */ t(
40
+ "div",
41
+ {
42
+ className: `absolute top-1/2 -translate-y-1/2 w-[6px] h-[48px] bg-[var(--primary-color-filled)] cursor-ew-resize rounded-full z-10 ${e === "left" ? "left-[4px]" : "right-[4px]"}`,
43
+ onMouseDown: i
44
+ }
45
+ ), lt = ({ toolbarId: e, alignment: i, imageWidth: l, href: r, onAlignChange: c, onSizeChange: o, onToggleLink: s, onDelete: d }) => {
46
+ const [m, h] = A(!1);
47
+ return /* @__PURE__ */ p(G, { gap: "xs", wrap: "nowrap", children: [
48
+ rt.map((a) => /* @__PURE__ */ t(
49
+ b,
50
+ {
51
+ size: "xs",
52
+ radius: "sm",
53
+ variant: i === a.value ? "light" : "subtle",
54
+ color: i === a.value ? void 0 : "gray",
55
+ onClick: () => c(a.value),
56
+ title: a.label,
57
+ children: /* @__PURE__ */ t(x, { icon: a.icon })
58
+ },
59
+ a.value
60
+ )),
61
+ /* @__PURE__ */ p(k, { opened: m, position: "bottom", shadow: "md", offset: 8, withArrow: !1, closeOnClickOutside: !1, children: [
62
+ /* @__PURE__ */ t(k.Target, { children: /* @__PURE__ */ t(
63
+ b,
64
+ {
65
+ size: "xs",
66
+ radius: "sm",
67
+ variant: m ? "light" : "subtle",
68
+ color: m ? void 0 : "gray",
69
+ onClick: () => h((a) => !a),
70
+ title: "Taille",
71
+ children: /* @__PURE__ */ t(x, { icon: Ee })
72
+ }
73
+ ) }),
74
+ /* @__PURE__ */ t(k.Dropdown, { p: "xs", children: /* @__PURE__ */ t("div", { "data-image-toolbar": e, children: /* @__PURE__ */ t(V, { gap: "4px", wrap: "nowrap", children: nt.map((a) => /* @__PURE__ */ t(
75
+ b,
76
+ {
77
+ radius: "sm",
78
+ variant: l !== null && Math.round(l) === a ? "light" : "subtle",
79
+ color: l !== null && Math.round(l) === a ? void 0 : "gray",
80
+ onClick: () => {
81
+ o(a), h(!1);
82
+ },
83
+ title: `${a}%`,
84
+ style: { minWidth: 52 },
85
+ children: /* @__PURE__ */ p("span", { children: [
86
+ a,
87
+ "%"
88
+ ] })
89
+ },
90
+ a
91
+ )) }) }) })
92
+ ] }),
93
+ /* @__PURE__ */ t(H, { my: "0", mx: "xs", orientation: "vertical" }),
94
+ /* @__PURE__ */ t(
95
+ b,
96
+ {
97
+ size: "xs",
98
+ radius: "sm",
99
+ variant: r ? "light" : "subtle",
100
+ color: r ? void 0 : "gray",
101
+ onClick: s,
102
+ title: "Lien",
103
+ children: /* @__PURE__ */ t(x, { icon: pe })
104
+ }
105
+ ),
106
+ /* @__PURE__ */ t(H, { my: "0", mx: "xs", orientation: "vertical" }),
107
+ /* @__PURE__ */ t(b, { size: "xs", radius: "sm", variant: "subtle", color: "red", onClick: d, title: "Supprimer l'image", children: /* @__PURE__ */ t(x, { icon: K }) })
108
+ ] });
109
+ }, ot = ({ toolbarId: e, href: i, onHrefChange: l, onClose: r }) => {
110
+ const [c, o] = A(i || ""), s = () => {
111
+ l(c.trim() || null), r();
112
+ };
113
+ return /* @__PURE__ */ t("div", { "data-image-toolbar": e, children: /* @__PURE__ */ p(G, { gap: "xs", wrap: "nowrap", children: [
114
+ /* @__PURE__ */ t("div", { className: "w-[220px]", children: /* @__PURE__ */ t(
115
+ ce,
116
+ {
117
+ size: "xs",
118
+ placeholder: "https://...",
119
+ value: c,
120
+ onChange: (m) => o(m.currentTarget.value),
121
+ onKeyDown: (m) => m.key === "Enter" && s(),
122
+ autoFocus: !0
123
+ }
124
+ ) }),
125
+ /* @__PURE__ */ t(b, { type: "button", size: "xs", radius: "sm", variant: "subtle", color: "gray", onClick: s, title: "Valider", children: /* @__PURE__ */ t(x, { icon: de }) }),
126
+ /* @__PURE__ */ t(H, { my: "0", orientation: "vertical" }),
127
+ /* @__PURE__ */ t(
128
+ b,
129
+ {
130
+ type: "button",
131
+ size: "xs",
132
+ radius: "sm",
133
+ variant: "subtle",
134
+ color: "gray",
135
+ onClick: () => c && window.open(c, "_blank"),
136
+ title: "Ouvrir dans un nouvel onglet",
137
+ disabled: !c,
138
+ children: /* @__PURE__ */ t(x, { icon: ue })
139
+ }
140
+ ),
141
+ /* @__PURE__ */ t(
142
+ b,
143
+ {
144
+ type: "button",
145
+ size: "xs",
146
+ radius: "sm",
147
+ variant: "subtle",
148
+ color: "red",
149
+ onClick: () => {
150
+ l(null), r();
151
+ },
152
+ title: "Supprimer le lien",
153
+ disabled: !i,
154
+ children: /* @__PURE__ */ t(x, { icon: K })
155
+ }
156
+ )
157
+ ] }) });
158
+ }, st = ({ editor: e, node: i, getPos: l }) => {
159
+ const r = _(null), c = _(null), o = _(null), [s, d] = A(!1), [m, h] = A(!1), [a, f] = A(i.attrs.width || null), [u, g] = A(0), [y, C] = A(!1), [w] = A(() => `image-toolbar-${Math.random().toString(36).substring(2, 9)}`), L = i.attrs.alignment || "center";
160
+ M(() => {
161
+ if (!r.current || !u || a !== null) return;
162
+ const v = () => {
163
+ let E = r.current.naturalWidth / u * 100;
164
+ E = Math.min(E, R.MAX_WIDTH);
165
+ const U = ae(r.current, u);
166
+ E = Math.max(E, U), f(E), e.chain().updateAttributes({ width: E }).run();
167
+ };
168
+ if (r.current.complete)
169
+ v();
170
+ else
171
+ return r.current.addEventListener("load", v), () => {
172
+ var S;
173
+ return (S = r.current) == null ? void 0 : S.removeEventListener("load", v);
174
+ };
175
+ }, [u, a, e]), M(() => {
176
+ const v = () => {
177
+ c.current && g(c.current.offsetWidth);
178
+ };
179
+ v();
180
+ const S = new ResizeObserver(v);
181
+ return c.current && S.observe(c.current), () => S.disconnect();
182
+ }, []), M(() => {
183
+ const v = (S) => {
184
+ var F, W;
185
+ const E = S.target, U = ((F = r.current) == null ? void 0 : F.contains(E)) || ((W = o.current) == null ? void 0 : W.contains(E)), q = !!E.closest(`[data-image-toolbar="${w}"]`);
186
+ U ? (d(!0), e.chain().focus().run()) : d(!!q);
187
+ };
188
+ return document.addEventListener("click", v), () => document.removeEventListener("click", v);
189
+ }, [e, w]), M(() => {
190
+ s || h(!1);
191
+ }, [s]);
192
+ const D = O(
193
+ (v, S) => {
194
+ if (S.preventDefault(), S.stopPropagation(), !r.current || !c.current) return;
195
+ C(!0);
196
+ const E = S.clientX, U = r.current.offsetWidth, q = ae(r.current, u);
197
+ let F = a;
198
+ const W = (ke) => {
199
+ const ee = ke.clientX - E, te = L === "center" ? ee * 2 : ee, Le = (v === "left" ? U - te : U + te) / u * 100, P = Math.max(q, Math.min(R.MAX_WIDTH, Le));
200
+ P !== (a || 0) && (f(P), e.chain().updateAttributes({ width: P }).run(), F = P);
201
+ }, Y = () => {
202
+ C(!1), document.removeEventListener("mousemove", W), document.removeEventListener("mouseup", Y), e.commands.updateAttributes("image", { width: F });
203
+ };
204
+ document.addEventListener("mousemove", W), document.addEventListener("mouseup", Y);
205
+ },
206
+ [L, a, u, e]
207
+ ), n = O(
208
+ (v) => {
209
+ e.commands.updateAttributes("image", {
210
+ alignment: v,
211
+ ...a && { width: a }
212
+ });
213
+ },
214
+ [a, e]
215
+ ), I = O(
216
+ (v) => {
217
+ f(v), e.commands.updateAttributes("image", { width: v });
218
+ },
219
+ [e]
220
+ ), N = O(
221
+ (v) => {
222
+ e.commands.updateAttributes("image", { href: v });
223
+ },
224
+ [e]
225
+ ), T = O(() => {
226
+ const v = l();
227
+ e.view.dispatch(e.state.tr.delete(v, v + i.nodeSize));
228
+ }, [e, l, i.nodeSize]), Ae = a ? `${a}%` : "auto";
229
+ return /* @__PURE__ */ t("div", { ref: c, className: "relative block w-full my-2.5 select-none", children: /* @__PURE__ */ t("div", { style: { width: Ae, ...at(L) }, children: /* @__PURE__ */ p(k, { opened: s, position: "top", shadow: "md", offset: 8, withArrow: !1, closeOnClickOutside: !1, children: [
230
+ /* @__PURE__ */ t(k.Target, { children: /* @__PURE__ */ t("div", { children: /* @__PURE__ */ p(k, { opened: m, position: "bottom", shadow: "md", offset: 8, withArrow: !1, closeOnClickOutside: !1, children: [
231
+ /* @__PURE__ */ t(k.Target, { children: /* @__PURE__ */ p("div", { ref: o, className: "relative block", children: [
232
+ /* @__PURE__ */ t(
233
+ "img",
234
+ {
235
+ ref: r,
236
+ src: i.attrs.src,
237
+ alt: i.attrs.alt || "",
238
+ draggable: !1,
239
+ onDragStart: (v) => v.preventDefault(),
240
+ className: `w-full h-auto block rounded transition-shadow cursor-pointer ${s ? "shadow-[0_0_0_2px_var(--primary-color-filled)]" : ""}`
241
+ }
242
+ ),
243
+ s && /* @__PURE__ */ p(X, { children: [
244
+ /* @__PURE__ */ t(le, { direction: "left", onMouseDown: (v) => D("left", v) }),
245
+ /* @__PURE__ */ t(le, { direction: "right", onMouseDown: (v) => D("right", v) })
246
+ ] })
247
+ ] }) }),
248
+ /* @__PURE__ */ t(k.Dropdown, { p: "xs", children: /* @__PURE__ */ t(
249
+ ot,
250
+ {
251
+ toolbarId: w,
252
+ href: i.attrs.href,
253
+ onHrefChange: N,
254
+ onClose: () => h(!1)
255
+ }
256
+ ) })
257
+ ] }) }) }),
258
+ /* @__PURE__ */ t(k.Dropdown, { p: "xs", children: /* @__PURE__ */ t("div", { "data-image-toolbar": w, children: /* @__PURE__ */ t(
259
+ lt,
260
+ {
261
+ toolbarId: w,
262
+ alignment: L,
263
+ imageWidth: a,
264
+ href: i.attrs.href,
265
+ onAlignChange: n,
266
+ onSizeChange: I,
267
+ onToggleLink: () => h((v) => !v),
268
+ onDelete: T
269
+ }
270
+ ) }) })
271
+ ] }) }) });
272
+ };
273
+ function ct(e) {
274
+ const { editor: i, node: l, getPos: r } = e;
275
+ return /* @__PURE__ */ t(be, { children: /* @__PURE__ */ t(st, { editor: i, node: l, getPos: r }) });
276
+ }
277
+ const dt = Ye.extend({
278
+ addAttributes() {
279
+ return {
280
+ src: { default: null },
281
+ alt: { default: null },
282
+ title: { default: null },
283
+ width: {
284
+ default: null,
285
+ parseHTML: (e) => e.getAttribute("data-width"),
286
+ renderHTML: (e) => e.width ? { "data-width": e.width } : {}
287
+ },
288
+ alignment: {
289
+ default: "center",
290
+ parseHTML: (e) => e.getAttribute("data-alignment") || "center",
291
+ renderHTML: (e) => ({ "data-alignment": e.alignment })
292
+ },
293
+ href: {
294
+ default: null,
295
+ parseHTML: (e) => {
296
+ const i = e.parentElement;
297
+ return (i == null ? void 0 : i.tagName) === "A" ? i.getAttribute("href") : null;
298
+ },
299
+ renderHTML: () => ({})
300
+ }
301
+ };
302
+ },
303
+ renderHTML({ node: e, HTMLAttributes: i }) {
304
+ const { width: l, alignment: r, href: c } = e.attrs, o = [];
305
+ l && o.push(`width: ${l}%`), r === "center" ? o.push("display: block", "margin-left: auto", "margin-right: auto") : r === "right" ? o.push("display: block", "margin-left: auto", "margin-right: 0") : r === "left" && o.push("display: block", "margin-left: 0", "margin-right: auto");
306
+ const s = { ...i, class: "tiptap-img" };
307
+ o.length && (s.style = o.join("; "));
308
+ const d = ["img", s];
309
+ return c ? ["a", { href: c, target: "_blank", rel: "noopener noreferrer" }, d] : d;
310
+ },
311
+ addNodeView() {
312
+ return Ce(ct);
313
+ }
314
+ }), ut = et.create({
315
+ name: "indent",
316
+ addGlobalAttributes() {
317
+ return [
318
+ {
319
+ types: ["paragraph", "heading"],
320
+ attributes: {
321
+ indent: {
322
+ default: 0,
323
+ renderHTML: (e) => ({ style: `padding-left: ${(e.indent || 0) * 40}px` }),
324
+ parseHTML: (e) => ({
325
+ indent: parseInt(e.style.paddingLeft) / 40 || 0
326
+ })
327
+ }
328
+ }
329
+ }
330
+ ];
331
+ },
332
+ addCommands() {
333
+ return {
334
+ indent: () => ({ editor: e, commands: i }) => {
335
+ const l = e.getAttributes(e.state.selection.$head.parent.type.name).indent || 0;
336
+ return i.updateAttributes(
337
+ e.state.selection.$head.parent.type.name,
338
+ { indent: l + 1 }
339
+ );
340
+ },
341
+ outdent: () => ({ editor: e, commands: i }) => {
342
+ const l = e.getAttributes(e.state.selection.$head.parent.type.name).indent || 0;
343
+ return i.updateAttributes(
344
+ e.state.selection.$head.parent.type.name,
345
+ { indent: Math.max(0, l - 1) }
346
+ );
347
+ }
348
+ };
349
+ },
350
+ addKeyboardShortcuts() {
351
+ return {
352
+ Tab: () => this.editor.commands.indent(),
353
+ "Shift-Tab": () => this.editor.commands.outdent()
354
+ };
355
+ }
356
+ }), j = 8 * 1024 * 1024;
357
+ function gt(e) {
358
+ return typeof e == "number" && e >= 0;
359
+ }
360
+ function xe(e, i) {
361
+ return e ? (Array.isArray(i) ? i : [i]).some(
362
+ (r) => e.extensionManager.extensions.some((c) => c.name === r)
363
+ ) : !1;
364
+ }
365
+ function ht(e) {
366
+ const { state: i, view: l } = e, { doc: r, selection: c } = i, o = Ke.findFrom(c.$to, 1, !0);
367
+ if (o)
368
+ return l.dispatch(i.tr.setSelection(o).scrollIntoView()), !0;
369
+ const s = i.schema.nodes.paragraph;
370
+ if (!s) return !1;
371
+ const d = r.content.size, m = s.create();
372
+ let h = i.tr.insert(d, m);
373
+ const a = h.doc.resolve(d + 1);
374
+ return h = h.setSelection(Je.near(a)).scrollIntoView(), l.dispatch(h), !0;
375
+ }
376
+ function pt(e) {
377
+ const [i, l] = A([]), r = async (d) => {
378
+ var a, f, u;
379
+ if (d.size > e.maxSize)
380
+ return (a = e.onError) == null || a.call(e, new Error(`File size exceeds maximum allowed (${e.maxSize / 1024 / 1024}MB)`)), null;
381
+ const m = new AbortController(), h = crypto.randomUUID();
382
+ l((g) => [...g, { id: h, file: d, progress: 0, status: "uploading", abortController: m }]);
383
+ try {
384
+ if (!e.upload) throw new Error("Upload function is not defined");
385
+ const g = await e.upload(
386
+ d,
387
+ (y) => {
388
+ l(
389
+ (C) => C.map((w) => w.id === h ? { ...w, progress: y.progress } : w)
390
+ );
391
+ },
392
+ m.signal
393
+ );
394
+ if (!g) throw new Error("Upload failed: No URL returned");
395
+ return m.signal.aborted ? null : (l(
396
+ (y) => y.map((C) => C.id === h ? { ...C, status: "success", url: g, progress: 100 } : C)
397
+ ), (f = e.onSuccess) == null || f.call(e, g), g);
398
+ } catch (g) {
399
+ return m.signal.aborted || (l(
400
+ (y) => y.map((C) => C.id === h ? { ...C, status: "error", progress: 0 } : C)
401
+ ), (u = e.onError) == null || u.call(e, g instanceof Error ? g : new Error("Upload failed"))), null;
402
+ }
403
+ };
404
+ return { fileItems: i, uploadFiles: async (d) => {
405
+ var h, a;
406
+ return !d || d.length === 0 ? ((h = e.onError) == null || h.call(e, new Error("No files to upload")), []) : e.limit && d.length > e.limit ? ((a = e.onError) == null || a.call(e, new Error(`Maximum ${e.limit} file${e.limit === 1 ? "" : "s"} allowed`)), []) : (await Promise.all(d.map((f) => r(f)))).filter((f) => f !== null);
407
+ }, removeFileItem: (d) => {
408
+ l((m) => {
409
+ const h = m.find((a) => a.id === d);
410
+ return h != null && h.abortController && h.abortController.abort(), h != null && h.url && URL.revokeObjectURL(h.url), m.filter((a) => a.id !== d);
411
+ });
412
+ }, clearAllFiles: () => {
413
+ i.forEach((d) => {
414
+ d.abortController && d.abortController.abort(), d.url && URL.revokeObjectURL(d.url);
415
+ }), l([]);
416
+ } };
417
+ }
418
+ const we = () => /* @__PURE__ */ p("svg", { width: "24", height: "24", viewBox: "0 0 24 24", className: "tiptap-image-upload-icon", fill: "currentColor", xmlns: "http://www.w3.org/2000/svg", children: [
419
+ /* @__PURE__ */ t("path", { d: "M11.1953 4.41771C10.3478 4.08499 9.43578 3.94949 8.5282 4.02147C7.62062 4.09345 6.74133 4.37102 5.95691 4.83316C5.1725 5.2953 4.50354 5.92989 4.00071 6.68886C3.49788 7.44783 3.17436 8.31128 3.05465 9.2138C2.93495 10.1163 3.0222 11.0343 3.3098 11.8981C3.5974 12.7619 4.07781 13.5489 4.71463 14.1995C5.10094 14.5942 5.09414 15.2274 4.69945 15.6137C4.30476 16 3.67163 15.9932 3.28532 15.5985C2.43622 14.731 1.79568 13.6816 1.41221 12.5299C1.02875 11.3781 0.91241 10.1542 1.07201 8.95084C1.23162 7.74748 1.66298 6.59621 2.33343 5.58425C3.00387 4.57229 3.89581 3.72617 4.9417 3.10998C5.98758 2.4938 7.15998 2.1237 8.37008 2.02773C9.58018 1.93176 10.7963 2.11243 11.9262 2.55605C13.0561 2.99968 14.0703 3.69462 14.8919 4.58825C15.5423 5.29573 16.0585 6.11304 16.4177 7.00002H17.4999C18.6799 6.99991 19.8288 7.37933 20.7766 8.08222C21.7245 8.78515 22.4212 9.7743 22.7637 10.9036C23.1062 12.0328 23.0765 13.2423 22.6788 14.3534C22.2812 15.4644 21.5367 16.4181 20.5554 17.0736C20.0962 17.3803 19.4752 17.2567 19.1684 16.7975C18.8617 16.3382 18.9853 15.7172 19.4445 15.4105C20.069 14.9934 20.5427 14.3865 20.7958 13.6794C21.0488 12.9724 21.0678 12.2027 20.8498 11.4841C20.6318 10.7655 20.1885 10.136 19.5853 9.6887C18.9821 9.24138 18.251 8.99993 17.5001 9.00002H15.71C15.2679 9.00002 14.8783 8.70973 14.7518 8.28611C14.4913 7.41374 14.0357 6.61208 13.4195 5.94186C12.8034 5.27164 12.0427 4.75043 11.1953 4.41771Z", fill: "currentColor" }),
420
+ /* @__PURE__ */ t("path", { d: "M11 14.4142V21C11 21.5523 11.4477 22 12 22C12.5523 22 13 21.5523 13 21V14.4142L15.2929 16.7071C15.6834 17.0976 16.3166 17.0976 16.7071 16.7071C17.0976 16.3166 17.0976 15.6834 16.7071 15.2929L12.7078 11.2936C12.7054 11.2912 12.703 11.2888 12.7005 11.2864C12.5208 11.1099 12.2746 11.0008 12.003 11L12 11L11.997 11C11.8625 11.0004 11.7343 11.0273 11.6172 11.0759C11.502 11.1236 11.3938 11.1937 11.2995 11.2864C11.297 11.2888 11.2946 11.2912 11.2922 11.2936L7.29289 15.2929C6.90237 15.6834 6.90237 16.3166 7.29289 16.7071C7.68342 17.0976 8.31658 17.0976 8.70711 16.7071L11 14.4142Z", fill: "currentColor" })
421
+ ] }), mt = () => /* @__PURE__ */ t("svg", { width: "43", height: "57", viewBox: "0 0 43 57", fill: "currentColor", className: "tiptap-image-upload-dropzone-rect-primary", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ t("path", { d: "M0.75 10.75C0.75 5.64137 4.89137 1.5 10 1.5H32.3431C33.2051 1.5 34.0317 1.84241 34.6412 2.4519L40.2981 8.10876C40.9076 8.71825 41.25 9.5449 41.25 10.4069V46.75C41.25 51.8586 37.1086 56 32 56H10C4.89137 56 0.75 51.8586 0.75 46.75V10.75Z", fill: "currentColor", fillOpacity: "0.11", stroke: "currentColor", strokeWidth: "1.5" }) }), ft = () => /* @__PURE__ */ t("svg", { width: "10", height: "10", className: "tiptap-image-upload-dropzone-rect-secondary", viewBox: "0 0 10 10", fill: "currentColor", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ t("path", { d: "M0 0.75H0.343146C1.40401 0.75 2.42143 1.17143 3.17157 1.92157L8.82843 7.57843C9.57857 8.32857 10 9.34599 10 10.4069V10.75H4C1.79086 10.75 0 8.95914 0 6.75V0.75Z", fill: "currentColor" }) }), vt = ({ onFile: e, children: i }) => {
422
+ const [l, r] = A(!1), [c, o] = A(!1);
423
+ return /* @__PURE__ */ t(
424
+ "div",
425
+ {
426
+ className: `tiptap-image-upload-drag-area ${c ? "drag-active" : ""} ${l ? "drag-over" : ""}`,
427
+ onDragEnter: (a) => {
428
+ a.preventDefault(), a.stopPropagation(), o(!0);
429
+ },
430
+ onDragLeave: (a) => {
431
+ a.preventDefault(), a.stopPropagation(), a.currentTarget.contains(a.relatedTarget) || (o(!1), r(!1));
432
+ },
433
+ onDragOver: (a) => {
434
+ a.preventDefault(), a.stopPropagation(), r(!0);
435
+ },
436
+ onDrop: (a) => {
437
+ a.preventDefault(), a.stopPropagation(), o(!1), r(!1);
438
+ const f = Array.from(a.dataTransfer.files);
439
+ f.length > 0 && e(f);
440
+ },
441
+ children: i
442
+ }
443
+ );
444
+ }, bt = ({ fileItem: e, onRemove: i }) => {
445
+ const l = (r) => {
446
+ if (r === 0) return "0 Bytes";
447
+ const c = 1024, o = ["Bytes", "KB", "MB", "GB"], s = Math.floor(Math.log(r) / Math.log(c));
448
+ return `${parseFloat((r / Math.pow(c, s)).toFixed(2))} ${o[s]}`;
449
+ };
450
+ return /* @__PURE__ */ p("div", { className: "tiptap-image-upload-preview", children: [
451
+ /* @__PURE__ */ p("div", { className: "tiptap-image-upload-preview-content", children: [
452
+ /* @__PURE__ */ p("div", { className: "tiptap-image-upload-file-info", children: [
453
+ /* @__PURE__ */ t("div", { className: "tiptap-image-upload-file-icon", children: /* @__PURE__ */ t(we, {}) }),
454
+ /* @__PURE__ */ p("div", { className: "tiptap-image-upload-details", children: [
455
+ /* @__PURE__ */ t("span", { className: "tiptap-image-upload-text", children: e.file.name }),
456
+ /* @__PURE__ */ t("span", { className: "tiptap-image-upload-subtext", children: l(e.file.size) })
457
+ ] })
458
+ ] }),
459
+ /* @__PURE__ */ p("div", { className: "tiptap-image-upload-actions", children: [
460
+ e.status === "uploading" && /* @__PURE__ */ p("span", { className: "tiptap-image-upload-progress-text", children: [
461
+ e.progress,
462
+ "%"
463
+ ] }),
464
+ /* @__PURE__ */ t(Z, { type: "button", variant: "subtle", onClick: (r) => {
465
+ r.stopPropagation(), i();
466
+ }, children: /* @__PURE__ */ t(Ie, { className: "tiptap-button-icon" }) })
467
+ ] })
468
+ ] }),
469
+ e.status === "uploading" && /* @__PURE__ */ t("div", { className: "tiptap-image-upload-progress-track", children: /* @__PURE__ */ t("div", { className: "tiptap-image-upload-progress-bar", style: { width: `${e.progress}%` } }) })
470
+ ] });
471
+ }, Ct = ({ maxSize: e, limit: i }) => /* @__PURE__ */ p(X, { children: [
472
+ /* @__PURE__ */ p("div", { className: "tiptap-image-upload-dropzone", children: [
473
+ /* @__PURE__ */ t(mt, {}),
474
+ /* @__PURE__ */ t(ft, {}),
475
+ /* @__PURE__ */ t("div", { className: "tiptap-image-upload-icon-container", children: /* @__PURE__ */ t(we, {}) })
476
+ ] }),
477
+ /* @__PURE__ */ p("div", { className: "tiptap-image-upload-content", children: [
478
+ /* @__PURE__ */ p("span", { className: "tiptap-image-upload-text", children: [
479
+ /* @__PURE__ */ t("em", { children: "Cliquez pour télécharger" }),
480
+ " ou glisser-déposer vos images ici"
481
+ ] }),
482
+ /* @__PURE__ */ p("span", { className: "tiptap-image-upload-subtext", children: [
483
+ "Maximum ",
484
+ i,
485
+ " fichier",
486
+ i === 1 ? "" : "s",
487
+ ", ",
488
+ e / 1024 / 1024,
489
+ "Mo",
490
+ i === 1 ? ". " : " chacun."
491
+ ] })
492
+ ] })
493
+ ] }), yt = (e) => {
494
+ const { accept: i, limit: l, maxSize: r } = e.node.attrs, c = _(null), o = e.extension, { fileItems: s, uploadFiles: d, removeFileItem: m, clearAllFiles: h } = pt({
495
+ maxSize: r,
496
+ limit: l,
497
+ accept: i,
498
+ upload: o.options.upload,
499
+ onSuccess: o.options.onSuccess,
500
+ onError: o.options.onError
501
+ }), a = async (y) => {
502
+ const C = await d(y);
503
+ if (C.length > 0) {
504
+ const w = e.getPos();
505
+ if (gt(w)) {
506
+ const L = C.map((D, n) => {
507
+ var N;
508
+ const I = ((N = y[n]) == null ? void 0 : N.name.replace(/\.[^/.]+$/, "")) || "unknown";
509
+ return {
510
+ type: o.options.type,
511
+ attrs: { ...o.options, src: D, alt: I, title: I }
512
+ };
513
+ });
514
+ e.editor.chain().focus().deleteRange({ from: w, to: w + e.node.nodeSize }).insertContentAt(w, L).run(), ht(e.editor);
515
+ }
516
+ }
517
+ }, f = (y) => {
518
+ var w, L;
519
+ const C = y.target.files;
520
+ if (!C || C.length === 0) {
521
+ (L = (w = o.options).onError) == null || L.call(w, new Error("No file selected"));
522
+ return;
523
+ }
524
+ a(Array.from(C));
525
+ }, u = () => {
526
+ c.current && s.length === 0 && (c.current.value = "", c.current.click());
527
+ }, g = s.length > 0;
528
+ return /* @__PURE__ */ p(be, { className: "tiptap-image-upload", tabIndex: 0, onClick: u, children: [
529
+ !g && /* @__PURE__ */ t(vt, { onFile: a, children: /* @__PURE__ */ t(Ct, { maxSize: r, limit: l }) }),
530
+ g && /* @__PURE__ */ p("div", { className: "tiptap-image-upload-previews", children: [
531
+ s.length > 1 && /* @__PURE__ */ p("div", { className: "tiptap-image-upload-header", children: [
532
+ /* @__PURE__ */ p("span", { children: [
533
+ "Uploading ",
534
+ s.length,
535
+ " files"
536
+ ] }),
537
+ /* @__PURE__ */ t(Z, { type: "button", variant: "subtle", onClick: (y) => {
538
+ y.stopPropagation(), h();
539
+ }, children: "Clear All" })
540
+ ] }),
541
+ s.map((y) => /* @__PURE__ */ t(
542
+ bt,
543
+ {
544
+ fileItem: y,
545
+ onRemove: () => m(y.id)
546
+ },
547
+ y.id
548
+ ))
549
+ ] }),
550
+ /* @__PURE__ */ t(
551
+ "input",
552
+ {
553
+ ref: c,
554
+ name: "file",
555
+ accept: i,
556
+ type: "file",
557
+ multiple: l > 1,
558
+ onChange: f,
559
+ onClick: (y) => y.stopPropagation()
560
+ }
561
+ )
562
+ ] });
563
+ }, xt = Oe.create({
564
+ name: "imageUpload",
565
+ group: "block",
566
+ draggable: !0,
567
+ selectable: !0,
568
+ atom: !0,
569
+ addOptions() {
570
+ return {
571
+ type: "image",
572
+ accept: "image/*",
573
+ limit: 1,
574
+ maxSize: 0,
575
+ upload: void 0,
576
+ onError: void 0,
577
+ onSuccess: void 0,
578
+ HTMLAttributes: {}
579
+ };
580
+ },
581
+ addAttributes() {
582
+ return {
583
+ accept: { default: this.options.accept },
584
+ limit: { default: this.options.limit },
585
+ maxSize: { default: this.options.maxSize }
586
+ };
587
+ },
588
+ parseHTML() {
589
+ return [{ tag: 'div[data-type="image-upload"]' }];
590
+ },
591
+ renderHTML({ HTMLAttributes: e }) {
592
+ return ["div", Fe({ "data-type": "image-upload" }, e)];
593
+ },
594
+ addNodeView() {
595
+ return Ce(yt);
596
+ },
597
+ addCommands() {
598
+ return {
599
+ setImageUploadNode: (e) => ({ commands: i }) => i.insertContent({ type: this.name, attrs: e })
600
+ };
601
+ },
602
+ addKeyboardShortcuts() {
603
+ return {
604
+ Enter: ({ editor: e }) => {
605
+ const { selection: i } = e.state, { nodeAfter: l } = i.$from;
606
+ if (l && l.type.name === "imageUpload" && e.isActive("imageUpload")) {
607
+ const r = e.view.nodeDOM(i.$from.pos);
608
+ if (r && r instanceof HTMLElement) {
609
+ const c = r.firstChild;
610
+ if (c && c instanceof HTMLElement)
611
+ return c.click(), !0;
612
+ }
613
+ }
614
+ return !1;
615
+ }
616
+ };
617
+ }
618
+ });
619
+ function wt({ editor: e, disabled: i = !1 }) {
620
+ const [l, { open: r, close: c, toggle: o }] = tt(!1), [s, d] = A("");
621
+ M(() => {
622
+ if (l) {
623
+ const f = e.getAttributes("link").href ?? "";
624
+ d(f);
625
+ }
626
+ }, [l, e]), M(() => {
627
+ if (!e) return;
628
+ const f = (u) => {
629
+ const g = u.target.closest("a");
630
+ g && (d(g.href ?? ""), r());
631
+ };
632
+ return e.view.dom.addEventListener("click", f, !0), () => e.view.dom.removeEventListener("click", f);
633
+ }, [e, r]);
634
+ const m = () => {
635
+ s && e.chain().focus().setLink({ href: s }).run(), c();
636
+ }, h = () => {
637
+ e.chain().focus().unsetLink().run(), c();
638
+ }, a = e.isActive("link");
639
+ return /* @__PURE__ */ p(k, { shadow: "md", children: [
640
+ /* @__PURE__ */ t(k.Target, { children: /* @__PURE__ */ t(
641
+ b,
642
+ {
643
+ type: "button",
644
+ onClick: o,
645
+ title: "Inserer un lien",
646
+ variant: a ? "light" : "subtle",
647
+ color: a ? void 0 : "gray",
648
+ radius: "sm",
649
+ size: "xs",
650
+ disabled: i,
651
+ children: /* @__PURE__ */ t(x, { icon: pe })
652
+ }
653
+ ) }),
654
+ /* @__PURE__ */ t(k.Dropdown, { children: /* @__PURE__ */ p(G, { gap: "xs", wrap: "nowrap", children: [
655
+ /* @__PURE__ */ t(
656
+ ce,
657
+ {
658
+ size: "xs",
659
+ placeholder: "https://...",
660
+ value: s,
661
+ onChange: (f) => d(f.currentTarget.value),
662
+ onKeyDown: (f) => f.key === "Enter" && m(),
663
+ style: { width: 220 },
664
+ autoFocus: !0
665
+ }
666
+ ),
667
+ /* @__PURE__ */ t(b, { type: "button", size: "xs", radius: "sm", variant: "subtle", color: "gray", onClick: m, title: "Valider", children: /* @__PURE__ */ t(x, { icon: de }) }),
668
+ /* @__PURE__ */ t(H, { my: "0", orientation: "vertical" }),
669
+ /* @__PURE__ */ t(
670
+ b,
671
+ {
672
+ type: "button",
673
+ size: "xs",
674
+ radius: "sm",
675
+ variant: "subtle",
676
+ color: "gray",
677
+ onClick: () => window.open(s, "_blank"),
678
+ title: "Ouvrir dans un nouvel onglet",
679
+ disabled: !s,
680
+ children: /* @__PURE__ */ t(x, { icon: ue })
681
+ }
682
+ ),
683
+ /* @__PURE__ */ t(
684
+ b,
685
+ {
686
+ type: "button",
687
+ size: "xs",
688
+ radius: "sm",
689
+ variant: "subtle",
690
+ color: "red",
691
+ onClick: h,
692
+ title: "Supprimer le lien",
693
+ disabled: !a,
694
+ children: /* @__PURE__ */ t(x, { icon: K })
695
+ }
696
+ )
697
+ ] }) })
698
+ ] });
699
+ }
700
+ function At({ editor: e, onDocUpload: i, disabled: l = !1 }) {
701
+ return i ? /* @__PURE__ */ p(X, { children: [
702
+ /* @__PURE__ */ t(
703
+ "input",
704
+ {
705
+ id: "ste-file-upload",
706
+ type: "file",
707
+ accept: ".pdf,.doc,.docx,.xls,.xlsx,.txt,.csv",
708
+ style: { display: "none" },
709
+ onChange: async (c) => {
710
+ var s;
711
+ const o = (s = c.target.files) == null ? void 0 : s[0];
712
+ if (o) {
713
+ try {
714
+ const d = await i(o);
715
+ if (!(d != null && d.url)) return;
716
+ const { url: m, name: h } = d, a = h || o.name, { from: f, to: u, empty: g } = e.state.selection;
717
+ g ? e.chain().focus().insertContent(a).setTextSelection({ from: f, to: f + a.length }).setLink({ href: m }).run() : e.chain().focus().setLink({ href: m }).run();
718
+ } catch (d) {
719
+ console.error("Doc upload failed:", d);
720
+ }
721
+ c.target.value = "";
722
+ }
723
+ }
724
+ }
725
+ ),
726
+ /* @__PURE__ */ t(
727
+ b,
728
+ {
729
+ type: "button",
730
+ variant: "subtle",
731
+ color: "gray",
732
+ radius: "sm",
733
+ size: "xs",
734
+ title: "Insérer un fichier",
735
+ onClick: () => document.getElementById("ste-file-upload").click(),
736
+ disabled: l,
737
+ children: /* @__PURE__ */ t(x, { icon: Ne })
738
+ }
739
+ )
740
+ ] }) : null;
741
+ }
742
+ function oe({ editor: e, indentType: i = "in", disabled: l = !1 }) {
743
+ return /* @__PURE__ */ t(
744
+ b,
745
+ {
746
+ type: "button",
747
+ onClick: () => {
748
+ e && (i === "in" ? e.chain().focus().indent().run() : e.chain().focus().outdent().run());
749
+ },
750
+ title: i === "in" ? "Augmenter l'indentation" : "Diminuer l'indentation",
751
+ variant: "subtle",
752
+ color: "gray",
753
+ radius: "sm",
754
+ size: "xs",
755
+ disabled: l,
756
+ children: /* @__PURE__ */ t(x, { icon: i === "in" ? Te : Me })
757
+ }
758
+ );
759
+ }
760
+ function kt(e = "max", i = 768) {
761
+ const [l, r] = A(!1);
762
+ return M(() => {
763
+ const c = e === "min" ? `(min-width: ${i}px)` : `(max-width: ${i - 1}px)`, o = window.matchMedia(c), s = (d) => r(d.matches);
764
+ return r(o.matches), o.addEventListener("change", s), () => o.removeEventListener("change", s);
765
+ }, [e, i]), l;
766
+ }
767
+ function Lt(e) {
768
+ const i = e.storage, l = i == null ? void 0 : i.pages;
769
+ return !l || !("activeEditor" in l) ? null : l.activeEditor ?? null;
770
+ }
771
+ function It(e) {
772
+ const { editor: i } = We(), l = e ?? i, [r, c] = A(null);
773
+ return M(() => {
774
+ if (!l) {
775
+ c(null);
776
+ return;
777
+ }
778
+ const s = () => c(Lt(l));
779
+ return s(), l.on("update", s), l.on("selectionUpdate", s), () => {
780
+ l.off("update", s), l.off("selectionUpdate", s);
781
+ };
782
+ }, [l]), M(() => {
783
+ if (!r) return;
784
+ const s = () => c(null);
785
+ return r.on("destroy", s), () => r.off("destroy", s);
786
+ }, [r]), ye({
787
+ editor: r ?? l,
788
+ selector(s) {
789
+ return s.editor ? { editor: s.editor } : { editor: null };
790
+ }
791
+ }) ?? { editor: null };
792
+ }
793
+ function Q(e) {
794
+ return !e || !e.isEditable || !xe(e, "imageUpload") ? !1 : e.can().insertContent({ type: "imageUpload" });
795
+ }
796
+ function St(e) {
797
+ if (!e || !e.isEditable || !Q(e)) return !1;
798
+ try {
799
+ return e.chain().focus().insertContent({ type: "imageUpload" }).run();
800
+ } catch {
801
+ return !1;
802
+ }
803
+ }
804
+ function Et(e, i) {
805
+ return !e || !e.isEditable ? !1 : i ? xe(e, "imageUpload") ? e.isActive("code") ? !0 : Q(e) : !1 : !0;
806
+ }
807
+ function Nt({ editor: e, hideWhenUnavailable: i = !1, onInserted: l }) {
808
+ const { editor: r } = It(e), c = kt(), [o, s] = A(!0), d = Q(r), m = (r == null ? void 0 : r.isActive("imageUpload")) ?? !1;
809
+ M(() => {
810
+ if (!r) return;
811
+ const a = () => s(Et(r, i));
812
+ return a(), r.on("selectionUpdate", a), () => r.off("selectionUpdate", a);
813
+ }, [r, i]);
814
+ const h = O(() => {
815
+ if (!r) return !1;
816
+ const a = St(r);
817
+ return a && (l == null || l()), a;
818
+ }, [r, l]);
819
+ return it("", (a) => {
820
+ a.preventDefault(), h();
821
+ }, {
822
+ enabled: o && d,
823
+ enableOnContentEditable: !c,
824
+ enableOnFormTags: !0
825
+ }), o ? /* @__PURE__ */ t(
826
+ b,
827
+ {
828
+ type: "button",
829
+ variant: m ? "light" : "subtle",
830
+ color: m ? void 0 : "gray",
831
+ disabled: !d,
832
+ "aria-label": "Ajouter une image",
833
+ "aria-pressed": m,
834
+ title: "Ajouter une image",
835
+ onClick: h,
836
+ radius: "sm",
837
+ size: "xs",
838
+ children: /* @__PURE__ */ t(x, { icon: ze })
839
+ }
840
+ ) : null;
841
+ }
842
+ const Tt = [
843
+ { value: "#e9ecef" },
844
+ { value: "#f03e3e" },
845
+ { value: "#e64980" },
846
+ { value: "#be4bdb" },
847
+ { value: "#7950f2" },
848
+ { value: "#228be6" },
849
+ { value: "#12b886" },
850
+ { value: "#82c91e" },
851
+ { value: "#fd7e14" }
852
+ ], Mt = [
853
+ { value: "#e9ecef" },
854
+ { value: "#f03e3e" },
855
+ { value: "#e64980" },
856
+ { value: "#be4bdb" },
857
+ { value: "#7950f2" },
858
+ { value: "#228be6" },
859
+ { value: "#12b886" },
860
+ { value: "#82c91e" },
861
+ { value: "#fd7e14" }
862
+ ], zt = [
863
+ { type: "heading", level: 1, label: "Titre 1", className: "text-2xl font-bold" },
864
+ { type: "heading", level: 2, label: "Titre 2", className: "text-xl font-bold" },
865
+ { type: "heading", level: 3, label: "Titre 3", className: "text-lg font-bold" },
866
+ { type: "heading", level: 4, label: "Titre 4", className: "text-base font-bold" },
867
+ { type: "heading", level: 5, label: "Titre 5", className: "text-sm font-bold" },
868
+ { type: "heading", level: 6, label: "Titre 6", className: "text-sm font-semibold uppercase" }
869
+ ], se = [
870
+ { value: "left", icon: J, label: "Aligner à gauche" },
871
+ { value: "center", icon: ge, label: "Aligner au centre" },
872
+ { value: "right", icon: he, label: "Aligner à droite" },
873
+ { value: "justify", icon: De, label: "Justifier" }
874
+ ], Dt = [
875
+ { type: "bulletList", icon: ve, label: "Liste à puces" },
876
+ { type: "orderedList", icon: me, label: "Liste numérotée" },
877
+ { type: "blockquote", icon: fe, label: "Citation" }
878
+ ];
879
+ function ei({ content: e, onChange: i, error: l, onImageUpload: r, onDocUpload: c }) {
880
+ var D;
881
+ const [o, s] = A(!1), d = r ? async (n, I, N) => {
882
+ if (n.size > j)
883
+ throw new Error(`File size exceeds maximum allowed (${j / (1024 * 1024)}MB)`);
884
+ return r(n, I, N);
885
+ } : void 0, m = (n) => {
886
+ u.isActive({ textAlign: n }) ? u.chain().focus().setTextAlign("left").run() : u.chain().focus().setTextAlign(n).run();
887
+ }, h = (n) => {
888
+ u.isActive("heading", { level: n }) ? u.chain().focus().setParagraph().run() : u.chain().focus().toggleHeading({ level: n }).run();
889
+ }, a = (n) => {
890
+ n === null ? u.chain().focus().unsetColor().run() : u.chain().focus().setColor(n).run();
891
+ }, f = (n) => {
892
+ n === null ? u.chain().focus().unsetHighlight().run() : u.chain().focus().setHighlight({ color: n }).run();
893
+ }, u = $e({
894
+ extensions: [
895
+ qe.configure({
896
+ bulletList: { HTMLAttributes: { class: "list-disc ml-3" } },
897
+ orderedList: { HTMLAttributes: { class: "list-decimal ml-3" } },
898
+ heading: !1,
899
+ codeBlock: !1,
900
+ link: !1,
901
+ underline: !1
902
+ }),
903
+ _e,
904
+ Ze.configure({ levels: [1, 2, 3, 4, 5, 6] }),
905
+ Ve.configure({ multicolor: !0 }),
906
+ dt,
907
+ ...d ? [
908
+ xt.configure({
909
+ accept: "image/*",
910
+ maxSize: j,
911
+ limit: 1,
912
+ upload: d,
913
+ onError: (n) => console.error("Upload failed:", n)
914
+ })
915
+ ] : [],
916
+ ut,
917
+ Xe.configure({ openOnClick: !1 }),
918
+ Ge.configure({ types: ["heading", "paragraph"] }),
919
+ Pe,
920
+ je
921
+ ],
922
+ content: e || "",
923
+ onUpdate({ editor: n }) {
924
+ i == null || i(n.getHTML());
925
+ },
926
+ onSelectionUpdate({ editor: n }) {
927
+ const { selection: I } = n.state;
928
+ s(I instanceof Qe && I.node.type.name === "image");
929
+ },
930
+ editorProps: {
931
+ handleClick(n, I, N) {
932
+ return N.target.closest("a") ? (N.preventDefault(), !0) : !1;
933
+ }
934
+ }
935
+ });
936
+ if (!u) return null;
937
+ const g = ye({
938
+ editor: u,
939
+ selector: (n) => ({
940
+ isBold: n.editor.isActive("bold"),
941
+ isItalic: n.editor.isActive("italic"),
942
+ isUnderline: n.editor.isActive("underline"),
943
+ isStrike: n.editor.isActive("strike"),
944
+ isLink: n.editor.isActive("link"),
945
+ isBulletList: n.editor.isActive("bulletList"),
946
+ isOrderedList: n.editor.isActive("orderedList"),
947
+ isBlockquote: n.editor.isActive("blockquote"),
948
+ isAlignLeft: n.editor.isActive({ textAlign: "left" }),
949
+ isAlignCenter: n.editor.isActive({ textAlign: "center" }),
950
+ isAlignRight: n.editor.isActive({ textAlign: "right" }),
951
+ isAlignJustify: n.editor.isActive({ textAlign: "justify" })
952
+ })
953
+ }), y = Se(), C = g.isAlignJustify ? "justify" : g.isAlignRight ? "right" : g.isAlignCenter ? "center" : "left", w = ((D = se.find((n) => n.value === C)) == null ? void 0 : D.icon) || J, L = [1, 2, 3, 4, 5, 6].find((n) => u.isActive("heading", { level: n }));
954
+ return /* @__PURE__ */ p("div", { className: "rounded-md border border-gray-300 bg-white overflow-hidden", children: [
955
+ /* @__PURE__ */ p("div", { className: "flex flex-wrap gap-1 p-2 border-b border-gray-200 bg-gray-50", children: [
956
+ /* @__PURE__ */ p(
957
+ $,
958
+ {
959
+ store: y,
960
+ onOptionSubmit: (n) => {
961
+ h(Number(n)), y.closeDropdown();
962
+ },
963
+ width: 110,
964
+ classNames: { option: "editor-heading-option" },
965
+ children: [
966
+ /* @__PURE__ */ t($.Target, { children: /* @__PURE__ */ t(
967
+ Z,
968
+ {
969
+ title: "Taille du texte",
970
+ rightSection: /* @__PURE__ */ t(x, { icon: He, size: "2xs" }),
971
+ onClick: () => y.toggleDropdown(),
972
+ variant: L ? "light" : "subtle",
973
+ color: L ? void 0 : "gray",
974
+ radius: "sm",
975
+ size: "xs",
976
+ disabled: o,
977
+ children: /* @__PURE__ */ p("strong", { children: [
978
+ "T",
979
+ /* @__PURE__ */ t("sub", { children: L })
980
+ ] })
981
+ }
982
+ ) }),
983
+ /* @__PURE__ */ t($.Dropdown, { className: "editor-menu", children: /* @__PURE__ */ t($.Options, { children: zt.map((n) => /* @__PURE__ */ t(
984
+ $.Option,
985
+ {
986
+ value: String(n.level),
987
+ bg: L === n.level ? "var(--mantine-primary-color-light)" : void 0,
988
+ children: /* @__PURE__ */ t("span", { className: n.className, children: n.label })
989
+ },
990
+ n.level
991
+ )) }) })
992
+ ]
993
+ }
994
+ ),
995
+ /* @__PURE__ */ p(z, { shadow: "md", children: [
996
+ /* @__PURE__ */ t(z.Target, { children: /* @__PURE__ */ t(
997
+ b,
998
+ {
999
+ type: "button",
1000
+ title: "Alignement",
1001
+ variant: C !== "left" ? "light" : "subtle",
1002
+ color: C !== "left" ? void 0 : "gray",
1003
+ radius: "sm",
1004
+ size: "xs",
1005
+ disabled: o,
1006
+ children: /* @__PURE__ */ t(x, { icon: w })
1007
+ }
1008
+ ) }),
1009
+ /* @__PURE__ */ t(z.Dropdown, { children: se.map((n) => /* @__PURE__ */ t(
1010
+ z.Item,
1011
+ {
1012
+ onClick: () => m(n.value),
1013
+ bg: C === n.value ? "var(--mantine-primary-color-light)" : void 0,
1014
+ title: n.label,
1015
+ disabled: o,
1016
+ children: /* @__PURE__ */ t(x, { icon: n.icon })
1017
+ },
1018
+ n.value
1019
+ )) })
1020
+ ] }),
1021
+ /* @__PURE__ */ p(z, { shadow: "md", children: [
1022
+ /* @__PURE__ */ t(z.Target, { children: /* @__PURE__ */ t(
1023
+ b,
1024
+ {
1025
+ type: "button",
1026
+ title: "Listes et citations",
1027
+ variant: g.isBulletList || g.isOrderedList || g.isBlockquote ? "light" : "subtle",
1028
+ color: g.isBulletList || g.isOrderedList || g.isBlockquote ? void 0 : "gray",
1029
+ radius: "sm",
1030
+ size: "xs",
1031
+ disabled: o,
1032
+ children: /* @__PURE__ */ t(x, { icon: g.isOrderedList ? me : g.isBlockquote ? fe : ve })
1033
+ }
1034
+ ) }),
1035
+ /* @__PURE__ */ t(z.Dropdown, { children: Dt.map((n) => {
1036
+ const I = g[`is${n.type.charAt(0).toUpperCase() + n.type.slice(1)}`], N = () => {
1037
+ const T = u.chain().focus();
1038
+ n.type === "bulletList" ? (g.isBlockquote && T.toggleBlockquote(), T.toggleBulletList().run()) : n.type === "orderedList" ? (g.isBlockquote && T.toggleBlockquote(), T.toggleOrderedList().run()) : n.type === "blockquote" && (g.isBulletList ? T.toggleBulletList() : g.isOrderedList && T.toggleOrderedList(), T.toggleBlockquote().run());
1039
+ };
1040
+ return /* @__PURE__ */ t(
1041
+ z.Item,
1042
+ {
1043
+ onClick: N,
1044
+ bg: I ? "var(--mantine-primary-color-light)" : void 0,
1045
+ title: n.label,
1046
+ disabled: o,
1047
+ leftSection: /* @__PURE__ */ t(x, { icon: n.icon }),
1048
+ children: n.label
1049
+ },
1050
+ n.type
1051
+ );
1052
+ }) })
1053
+ ] }),
1054
+ /* @__PURE__ */ t(H, { my: "0", mx: "xs", orientation: "vertical" }),
1055
+ /* @__PURE__ */ t(oe, { editor: u, indentType: "in", disabled: o }),
1056
+ /* @__PURE__ */ t(oe, { editor: u, indentType: "out", disabled: o }),
1057
+ /* @__PURE__ */ t(H, { my: "0", mx: "xs", orientation: "vertical" }),
1058
+ /* @__PURE__ */ t(
1059
+ b,
1060
+ {
1061
+ type: "button",
1062
+ onClick: () => u.chain().focus().toggleBold().run(),
1063
+ title: "Gras",
1064
+ variant: g.isBold ? "light" : "subtle",
1065
+ color: g.isBold ? void 0 : "gray",
1066
+ radius: "sm",
1067
+ size: "xs",
1068
+ disabled: o,
1069
+ children: /* @__PURE__ */ t("strong", { children: "B" })
1070
+ }
1071
+ ),
1072
+ /* @__PURE__ */ t(
1073
+ b,
1074
+ {
1075
+ type: "button",
1076
+ onClick: () => u.chain().focus().toggleItalic().run(),
1077
+ title: "Italique",
1078
+ variant: g.isItalic ? "light" : "subtle",
1079
+ color: g.isItalic ? void 0 : "gray",
1080
+ radius: "sm",
1081
+ size: "xs",
1082
+ disabled: o,
1083
+ children: /* @__PURE__ */ t("em", { children: "I" })
1084
+ }
1085
+ ),
1086
+ /* @__PURE__ */ t(
1087
+ b,
1088
+ {
1089
+ type: "button",
1090
+ onClick: () => u.chain().focus().toggleUnderline().run(),
1091
+ title: "Souligné",
1092
+ variant: g.isUnderline ? "light" : "subtle",
1093
+ color: g.isUnderline ? void 0 : "gray",
1094
+ radius: "sm",
1095
+ size: "xs",
1096
+ disabled: o,
1097
+ children: /* @__PURE__ */ t("u", { children: "S" })
1098
+ }
1099
+ ),
1100
+ /* @__PURE__ */ t(
1101
+ b,
1102
+ {
1103
+ type: "button",
1104
+ onClick: () => u.chain().focus().toggleStrike().run(),
1105
+ title: "Barré",
1106
+ variant: g.isStrike ? "light" : "subtle",
1107
+ color: g.isStrike ? void 0 : "gray",
1108
+ radius: "sm",
1109
+ size: "xs",
1110
+ disabled: o,
1111
+ children: /* @__PURE__ */ t(x, { icon: Ue })
1112
+ }
1113
+ ),
1114
+ /* @__PURE__ */ p(k, { shadow: "md", children: [
1115
+ /* @__PURE__ */ t(k.Target, { children: /* @__PURE__ */ t(
1116
+ b,
1117
+ {
1118
+ type: "button",
1119
+ title: "Couleur du texte",
1120
+ variant: u.getAttributes("textStyle").color ? "light" : "subtle",
1121
+ color: u.getAttributes("textStyle").color ? void 0 : "gray",
1122
+ radius: "sm",
1123
+ size: "xs",
1124
+ disabled: o,
1125
+ children: /* @__PURE__ */ t(x, { icon: Be })
1126
+ }
1127
+ ) }),
1128
+ /* @__PURE__ */ t(k.Dropdown, { children: /* @__PURE__ */ p(V, { gap: "lg", children: [
1129
+ /* @__PURE__ */ p(V, { w: 150, gap: "sm", children: [
1130
+ /* @__PURE__ */ t("p", { className: "text-sm font-semibold", children: "Couleur du texte" }),
1131
+ /* @__PURE__ */ p(B, { gap: "none", children: [
1132
+ /* @__PURE__ */ t(B.Col, { span: 3, align: "center", p: "0", children: /* @__PURE__ */ t(b, { variant: "subtle", color: "gray", onClick: () => a(null), size: "xs", disabled: o, radius: "sm", children: /* @__PURE__ */ t(ie, { variant: "outline", radius: "xl", size: "xs", color: "black", children: /* @__PURE__ */ t(x, { icon: re, size: "xs" }) }) }) }),
1133
+ Tt.map((n) => /* @__PURE__ */ t(B.Col, { span: 3, align: "center", p: "0", children: /* @__PURE__ */ t(
1134
+ b,
1135
+ {
1136
+ variant: u.getAttributes("textStyle").color === n.value ? "light" : "subtle",
1137
+ color: u.getAttributes("textStyle").color === n.value ? void 0 : "gray",
1138
+ onClick: () => a(n.value),
1139
+ size: "xs",
1140
+ disabled: o,
1141
+ radius: "sm",
1142
+ children: /* @__PURE__ */ t(ie, { variant: "outline", radius: "xl", size: "xs", color: n.value, children: /* @__PURE__ */ t(x, { icon: re, size: "xs" }) })
1143
+ }
1144
+ ) }, n.value))
1145
+ ] })
1146
+ ] }),
1147
+ /* @__PURE__ */ p(V, { w: 150, gap: "sm", children: [
1148
+ /* @__PURE__ */ t("p", { className: "text-sm font-semibold", children: "Couleur du fond" }),
1149
+ /* @__PURE__ */ p(B, { gap: "none", children: [
1150
+ /* @__PURE__ */ t(B.Col, { span: 3, align: "center", p: "0", children: /* @__PURE__ */ t(b, { variant: "subtle", color: "gray", onClick: () => f(null), size: "xs", disabled: o, radius: "sm", children: /* @__PURE__ */ t(ne, { size: "20", color: "white" }) }) }),
1151
+ Mt.map((n) => /* @__PURE__ */ t(B.Col, { span: 3, align: "center", p: "0", children: /* @__PURE__ */ t(
1152
+ b,
1153
+ {
1154
+ variant: u.getAttributes("highlight").color === n.value ? "light" : "subtle",
1155
+ color: u.getAttributes("highlight").color === n.value ? void 0 : "gray",
1156
+ onClick: () => f(n.value),
1157
+ size: "xs",
1158
+ disabled: o,
1159
+ radius: "sm",
1160
+ children: /* @__PURE__ */ t(ne, { size: "20", color: n.value })
1161
+ }
1162
+ ) }, n.value))
1163
+ ] })
1164
+ ] })
1165
+ ] }) })
1166
+ ] }),
1167
+ /* @__PURE__ */ t(H, { my: "0", mx: "xs", orientation: "vertical" }),
1168
+ /* @__PURE__ */ t(wt, { editor: u, disabled: o }),
1169
+ /* @__PURE__ */ t(At, { editor: u, onDocUpload: c, disabled: o }),
1170
+ d && /* @__PURE__ */ t(Nt, { editor: u, hideWhenUnavailable: !0 })
1171
+ ] }),
1172
+ /* @__PURE__ */ t("div", { className: "p-4 min-h-[400px]", children: /* @__PURE__ */ t(Re, { editor: u }) }),
1173
+ l && /* @__PURE__ */ t("div", { className: "px-4 pb-2 text-red-500 text-sm", children: l })
1174
+ ] });
1175
+ }
1176
+ export {
1177
+ dt as CustomImage,
1178
+ xt as ImageUploadNode,
1179
+ ut as Indent,
1180
+ ei as SimpleTiptapEditor
1181
+ };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@jcroger/tiptap-simple-editor",
3
+ "version": "1.0.0",
4
+ "description": "Rich text editor based on Tiptap and Mantine",
5
+ "module": "dist/index.js",
6
+ "main": "dist/index.js",
7
+ "exports": {
8
+ ".": "./dist/index.js",
9
+ "./styles": "./dist/style.css"
10
+ },
11
+ "files": ["dist"],
12
+ "scripts": {
13
+ "build": "vite build",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "peerDependencies": {
17
+ "react": ">=18",
18
+ "react-dom": ">=18",
19
+ "@mantine/core": ">=7",
20
+ "@mantine/hooks": ">=7",
21
+ "@tiptap/react": ">=2",
22
+ "@tiptap/pm": ">=2",
23
+ "@tiptap/starter-kit": ">=2",
24
+ "@tiptap/extension-color": ">=2",
25
+ "@tiptap/extension-highlight": ">=2",
26
+ "@tiptap/extension-image": ">=2",
27
+ "@tiptap/extension-link": ">=2",
28
+ "@tiptap/extension-text-align": ">=2",
29
+ "@tiptap/extension-text-style": ">=2",
30
+ "@tiptap/extension-underline": ">=2",
31
+ "@tiptap/extension-heading": ">=2",
32
+ "@tiptap/extension-code-block": ">=2",
33
+ "@fortawesome/react-fontawesome": ">=0.2",
34
+ "@fortawesome/free-solid-svg-icons": ">=6",
35
+ "react-hotkeys-hook": ">=4"
36
+ },
37
+ "devDependencies": {
38
+ "@vitejs/plugin-react": "^4",
39
+ "sass": "^1",
40
+ "vite": "^6"
41
+ }
42
+ }