UNPKG

reactjs-tiptap-editor

Version:

A modern WYSIWYG rich text editor based on tiptap and shadcn ui for React

430 lines (429 loc) 13.7 kB
import { h as B, m as q, k as V } from "./clsx-DaPvp9ji.js"; import { H as k, w as C, J as X, u as Y, d as D, A as Z, K as J, j as U, y as Q } from "./index-RcSPeQHn.js"; import { jsx as u, jsxs as z, Fragment as K } from "react/jsx-runtime"; import { useState as G, useMemo as P, useCallback as M, useEffect as $, useRef as ee } from "react"; import { I as j, i as W, j as te } from "./index-C07N8gA1.js"; import "./theme.js"; import { P as ne, a as re, b as ie } from "./popover-CtinPbiy.js"; const O = { TOP_LEFT: "tl", TOP_RIGHT: "tr", BOTTOM_LEFT: "bl", BOTTOM_RIGHT: "br" }; function ae(e) { var p, H; const [t, r] = G({ width: j, height: j }), [c, i] = G({ width: 0, height: 0 }), [n] = G([ O.TOP_LEFT, O.TOP_RIGHT, O.BOTTOM_LEFT, O.BOTTOM_RIGHT ]), [a, s] = G(!1), [l, v] = G({ x: 0, y: 0, w: 0, h: 0, dir: "" }), { align: b } = (p = e == null ? void 0 : e.node) == null ? void 0 : p.attrs, g = P(() => { var w; const { src: o, alt: m, width: f, height: x } = (w = e == null ? void 0 : e.node) == null ? void 0 : w.attrs, A = k(f) ? `${f}px` : f, d = k(x) ? `${x}px` : x; return { src: o || void 0, alt: m || void 0, style: { width: A || void 0, height: d || void 0 } }; }, [(H = e == null ? void 0 : e.node) == null ? void 0 : H.attrs]), y = P(() => { const { style: { width: o } } = g; return { width: o === "100%" ? o : void 0 }; }, [g]); function h(o) { i({ width: o.target.width, height: o.target.height }); } function _() { const { editor: o, getPos: m } = e; o.commands.setNodeSelection(m()); } const I = M( C(() => { const { editor: o } = e, { width: m } = getComputedStyle(o.view.dom); r((f) => ({ ...f, width: Number.parseInt(m, 10) })); }, W), [e == null ? void 0 : e.editor] ); function F(o, m) { o.preventDefault(), o.stopPropagation(); const f = c.width, x = c.height, A = f / x; let d = Number(e.node.attrs.width), w = Number(e.node.attrs.height); const T = t.width; d && !w ? (d = d > T ? T : d, w = Math.round(d / A)) : w && !d ? (d = Math.round(w * A), d = d > T ? T : d) : !d && !w ? (d = f > T ? T : f, w = Math.round(d / A)) : d = d > T ? T : d, s(!0), v({ x: o.clientX, y: o.clientY, w: d, h: w, dir: m }); } const N = M( C((o) => { if (o.preventDefault(), o.stopPropagation(), !a) return; const { x: m, w: f, dir: x } = l, A = (o.clientX - m) * (/l/.test(x) ? -1 : 1), d = X(f + A, te, t.width); e.updateAttributes({ width: d, height: null }); }, W), [a, l, t, e.updateAttributes] ), E = M( (o) => { o.preventDefault(), o.stopPropagation(), a && (v({ x: 0, y: 0, w: 0, h: 0, dir: "" }), s(!1), _()); }, [a, _] ), R = M(() => { document == null || document.addEventListener("mousemove", N, !0), document == null || document.addEventListener("mouseup", E, !0); }, [N, E]), S = M(() => { document == null || document.removeEventListener("mousemove", N, !0), document == null || document.removeEventListener("mouseup", E, !0); }, [N, E]); $(() => (a ? R() : S(), () => { S(); }), [a, R, S]); const L = P(() => new ResizeObserver(() => I()), [I]); return $(() => (L.observe(e.editor.view.dom), () => { L.disconnect(); }), [e.editor.view.dom, L]), /* @__PURE__ */ u( B, { className: "image-view", style: { ...y, width: "100%", textAlign: b }, children: /* @__PURE__ */ z( "div", { "data-drag-handle": !0, draggable: "true", style: y, className: `image-view__body ${e != null && e.selected ? "image-view__body--focused" : ""} ${a ? "image-view__body--resizing" : ""}`, children: [ /* @__PURE__ */ u( "img", { alt: g.alt, className: "image-view__body__image block", height: "auto", onClick: _, onLoad: h, src: g.src, style: g.style } ), (e == null ? void 0 : e.editor.view.editable) && ((e == null ? void 0 : e.selected) || a) && /* @__PURE__ */ u("div", { className: "image-resizer", children: n == null ? void 0 : n.map((o) => /* @__PURE__ */ u( "span", { className: `image-resizer__handler image-resizer__handler--${o}`, onMouseDown: (m) => F(m, o) }, `image-dir-${o}` )) }) ] } ) } ); } async function se(e) { var c; const r = await (await fetch(`https://api.giphy.com/v1/gifs/trending?q=&limit=15&api_key=${e}`)).json(); return (c = r == null ? void 0 : r.data) == null ? void 0 : c.map((i) => { var n, a, s; return { id: i == null ? void 0 : i.id, src: (n = i == null ? void 0 : i.images.original) == null ? void 0 : n.url, width: +((a = i == null ? void 0 : i.images.original) == null ? void 0 : a.width), height: +((s = i == null ? void 0 : i.images.original) == null ? void 0 : s.width) }; }); } async function oe(e, t) { var i; const c = await (await fetch(`https://api.giphy.com/v1/gifs/search?q=${e}&limit=15&api_key=${t}`)).json(); return (i = c == null ? void 0 : c.data) == null ? void 0 : i.map((n) => { var a, s, l; return { id: n == null ? void 0 : n.id, src: (a = n == null ? void 0 : n.images.original) == null ? void 0 : a.url, width: +((s = n == null ? void 0 : n.images.original) == null ? void 0 : s.width), height: +((l = n == null ? void 0 : n.images.original) == null ? void 0 : l.width) }; }); } async function ce(e) { var n, a; const t = await fetch(`https://tenor.googleapis.com/v2/trending_terms?key=${e}&limit=10`), r = await (t == null ? void 0 : t.json()), i = await (await fetch(`https://tenor.googleapis.com/v2/search?key=${e}&q=${(n = r == null ? void 0 : r.results) == null ? void 0 : n[0]}&limit=15`)).json(); return (a = i == null ? void 0 : i.results) == null ? void 0 : a.map((s) => { var l, v, b, g, y, h, _, I; return { id: s == null ? void 0 : s.id, src: (v = (l = s == null ? void 0 : s.media_formats) == null ? void 0 : l.gif) == null ? void 0 : v.url, width: (y = (g = (b = s == null ? void 0 : s.media_formats) == null ? void 0 : b.gif) == null ? void 0 : g.dims) == null ? void 0 : y[0], height: (I = (_ = (h = s == null ? void 0 : s.media_formats) == null ? void 0 : h.gif) == null ? void 0 : _.dims) == null ? void 0 : I[1] }; }); } async function de(e, t) { var i; const c = await (await fetch(`https://tenor.googleapis.com/v2/search?key=${t}&q=${e}&limit=15`)).json(); return (i = c == null ? void 0 : c.results) == null ? void 0 : i.map((n) => { var a, s, l, v, b, g, y, h; return { id: n == null ? void 0 : n.id, src: (s = (a = n == null ? void 0 : n.media_formats) == null ? void 0 : a.gif) == null ? void 0 : s.url, width: (b = (v = (l = n == null ? void 0 : n.media_formats) == null ? void 0 : l.gif) == null ? void 0 : v.dims) == null ? void 0 : b[0], height: (h = (y = (g = n == null ? void 0 : n.media_formats) == null ? void 0 : g.gif) == null ? void 0 : y.dims) == null ? void 0 : h[1] }; }); } function le(e, t) { return { searchTrending: async () => t ? e === "giphy" ? se(t) : e === "tenor" ? ce(t) : [] : [], searchWord: async (i) => t ? e === "giphy" ? oe(i, t) : e === "tenor" ? de(i, t) : [] : [] }; } function he({ selectImage: e, apiKey: t, provider: r, children: c }) { const [i, n] = G(!1), [a, s] = G([]), { editorDisabled: l } = D(), v = ee(null), { searchTrending: b, searchWord: g } = le(r, t); $(() => { (async () => { const h = await b(); s(h); })(); }, []); const y = M( J(async (h) => { if (!h.target.value) { const I = await b(); s(I); return; } const _ = await g(h.target.value); s(_); }, 350), // Adjust the debounce delay as needed [] ); return /* @__PURE__ */ z( ne, { modal: !0, onOpenChange: n, open: i, children: [ /* @__PURE__ */ u( re, { asChild: !0, disabled: l, children: c } ), /* @__PURE__ */ u( ie, { align: "start", className: "richtext-size-full richtext-p-2", hideWhenDetached: !0, side: "bottom", children: t ? /* @__PURE__ */ z(K, { children: [ /* @__PURE__ */ u("div", { className: "richtext-mb-[10px] richtext-w-full", children: /* @__PURE__ */ u( U, { onChange: y, placeholder: "Search GIF", ref: v, type: "text" } ) }), /* @__PURE__ */ u("div", { className: "richtext-max-h-[280px] !richtext-max-w-[400px] richtext-overflow-y-auto", children: /* @__PURE__ */ u("div", { className: "richtext-grid richtext-grid-cols-2 richtext-gap-1 ", children: a != null && a.length ? a == null ? void 0 : a.map((h) => /* @__PURE__ */ u( "img", { alt: "", className: "richtext-cursor-pointer richtext-object-contain richtext-text-center", src: h.src, onClick: () => { e(h.src), n(!1); } }, h.id )) : /* @__PURE__ */ u("p", { children: "No GIFs found" }) }) }) ] }) : /* @__PURE__ */ u("div", { children: /* @__PURE__ */ u("p", { children: "Missing Giphy API Key" }) }) } ) ] } ); } function _e() { const e = Y(ue.name), { action: t, icon: r, tooltip: c, apiKey: i, provider: n } = (e == null ? void 0 : e.componentProps) ?? {}, { editorDisabled: a } = D(); return /* @__PURE__ */ u( he, { apiKey: i, provider: n, selectImage: (l) => { a || t && t(l); }, children: /* @__PURE__ */ u( Z, { disabled: a, icon: r, tooltip: c } ) } ); } const ue = /* @__PURE__ */ Q.extend({ name: "imageGif", // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-expect-error addOptions() { var e; return { ...(e = this.parent) == null ? void 0 : e.call(this), inline: !1, content: "", marks: "", group: "block", API_KEY: "", provider: "giphy", draggable: !1, selectable: !0, atom: !0, button: ({ editor: t, extension: r, t: c }) => { var a, s; const i = ((a = r == null ? void 0 : r.options) == null ? void 0 : a.provider) || "", n = ((s = r == null ? void 0 : r.options) == null ? void 0 : s.API_KEY) || ""; return { componentProps: { action: (l) => { t.chain().focus().setImageGif({ src: l }).run(); }, isActive: () => !1, disabled: !1, icon: "GifIcon", tooltip: c("editor.imageGif.tooltip"), apiKey: n, provider: i } }; } }; }, addAttributes() { var e; return { ...(e = this.parent) == null ? void 0 : e.call(this), width: { default: null, parseHTML: (t) => { const r = t.style.width || t.getAttribute("width") || "10"; return r === void 0 ? null : Number.parseInt(`${r}`, 10); }, renderHTML: (t) => ({ width: t.width }) }, align: { default: "center", parseHTML: (t) => t.getAttribute("align"), renderHTML: (t) => ({ align: t.align }) } }; }, addNodeView() { return V(ae); }, addCommands() { var e; return { ...(e = this.parent) == null ? void 0 : e.call(this), setImageGif: (t) => ({ commands: r }) => r.insertContent({ type: this.name, attrs: t }), updateImageGif: (t) => ({ commands: r }) => r.updateAttributes(this.name, t), setAlignImageGif: (t) => ({ commands: r }) => r.updateAttributes(this.name, { align: t }) }; }, renderHTML({ HTMLAttributes: e }) { const { align: t } = e; return [ "div", // Parent element { style: t ? `text-align: ${t};` : "", class: "imageGIf" }, [ "img", q( // Always render the `height="auto"` { height: "auto" }, this.options.HTMLAttributes, e ) ] ]; }, parseHTML() { return [ { tag: "div[class=imageGIf]", getAttrs: (e) => { const t = e.querySelector("img"), r = t == null ? void 0 : t.getAttribute("width"); return { src: t == null ? void 0 : t.getAttribute("src"), alt: t == null ? void 0 : t.getAttribute("alt"), title: t == null ? void 0 : t.getAttribute("title"), width: r ? Number.parseInt(r, 10) : null, align: (t == null ? void 0 : t.getAttribute("align")) || e.style.textAlign || null }; } } ]; } }); export { ue as ImageGif, _e as RichTextImageGif };