UNPKG

reactjs-tiptap-editor

Version:

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

370 lines (369 loc) 13.7 kB
import { E as A, a as O } from "./index-CXIIg9Sq.js"; import { a as D } from "./dom-dataset-2RXYq9wp.js"; import { S as P } from "./index-fKx6CYUM.js"; import B from "tippy.js"; import { jsx as g, jsxs as k } from "react/jsx-runtime"; import { forwardRef as $, useState as N, useRef as E, useImperativeHandle as z, useEffect as M, Fragment as T } from "react"; import { d as V, k as U, q, E as K, U as G, V as F, W } from "./RichTextEditor-iSPxjLdO.js"; import { u as j, m as h } from "./index-D-DR0FPY.js"; function Q(i, m) { var R, v; const [t, n] = N(0), [e, a] = N(0), r = E(null), { t: s } = j(), u = E([]); z(m, () => ({ onKeyDown: f })), M(() => { if (!r.current) return; const l = e * 1e3 + t, c = u.current[l]; c && c.scrollIntoView({ behavior: "smooth", block: "nearest" }); }, [t, e]); function f({ event: l }) { return l.key === "ArrowUp" ? (w(), !0) : l.key === "ArrowDown" ? (b(), !0) : l.key === "Enter" ? (L(), !0) : !1; } function w() { var d; if (i.items.length === 0) return !1; let l = t - 1, c = e; l < 0 && (c = e - 1, l = ((d = i.items[c]) == null ? void 0 : d.commands.length) - 1 || 0), c < 0 && (c = i.items.length - 1, l = i.items[c].commands.length - 1), n(l), a(c); } function b() { if (i.items.length === 0) return !1; const l = i.items[e].commands; let c = t + 1, d = e; l.length - 1 < c && (c = 0, d = e + 1), i.items.length - 1 < d && (d = 0), n(c), a(d); } function L() { if (i.items.length === 0 || e === -1 || t === -1) return !1; C(e, t); } function C(l, c) { const d = i.items[l].commands[c]; i.command(d); } function H(l, c) { C(l, c); } function I(l, c, d) { u.current[l * 1e3 + c] = d; } return /* @__PURE__ */ g( "div", { className: "richtext-mb-8 richtext-max-h-[min(80vh,24rem)] richtext-flex-wrap richtext-overflow-auto richtext-rounded-lg !richtext-border !richtext-border-neutral-200 !richtext-bg-white richtext-p-1 !richtext-text-black richtext-shadow-sm dark:!richtext-border-neutral-800 dark:!richtext-bg-black", ref: r, children: (R = i == null ? void 0 : i.items) != null && R.length ? /* @__PURE__ */ g("div", { className: "richtext-grid richtext-min-w-48 richtext-grid-cols-1 richtext-gap-0.5", children: (v = i == null ? void 0 : i.items) == null ? void 0 : v.map((l, c) => /* @__PURE__ */ k(T, { children: [ /* @__PURE__ */ g("div", { className: "richtext-col-[1/-1] richtext-mx-2 richtext-mt-2 richtext-select-none richtext-text-[0.65rem] richtext-font-semibold richtext-uppercase richtext-tracking-wider !richtext-text-neutral-500 first:richtext-mt-0.5", children: l.title }), l.commands.map((d, y) => /* @__PURE__ */ k( "button", { onClick: () => H(c, y), ref: (S) => I(c, y, S), className: V("richtext-flex richtext-items-center richtext-gap-3 richtext-px-2 richtext-py-1.5 richtext-text-sm !richtext-text-neutral-800 dark:!richtext-text-neutral-200 richtext-text-left richtext-w-full richtext-rounded-sm richtext-outline-none richtext-transition-colors !richtext-bg-transparent hover:!richtext-bg-accent ", { "slash-command-active": e === c && t === y }), children: [ d.iconUrl && /* @__PURE__ */ g( "img", { alt: "", className: "richtext-size-6", src: d.iconUrl } ), d.iconName && /* @__PURE__ */ g( U, { className: "!richtext-mr-1 !richtext-text-lg", name: d.iconName } ), d.label ] }, `command-${y}` )) ] }, `slash-${l.title}`)) }) : /* @__PURE__ */ g("div", { className: "richtext-p-3", children: /* @__PURE__ */ g("span", { className: "richtext-text-xs richtext-text-gray-800 dark:richtext-text-gray-100", children: s("editor.slash.empty") }) }) } ); } const _ = $(Q), J = { setOpen: (i, m) => { q(K.UPLOAD_VIDEO(i), m); } }; function X(i, m) { const t = [ { name: "format", title: h.t("editor.slash.format"), commands: [] }, { name: "insert", title: h.t("editor.slash.insert"), commands: [] } ]; return i.forEach((n) => { n.name.toLowerCase() === "heading" && n.options.levels.forEach((e) => { t[0].commands.push({ name: `heading${e}`, //@ts-expect-error label: h.t(`editor.heading.h${e}.tooltip`), aliases: [`h${e}`, "bt", `bt${e}`], iconName: `Heading${e}`, action: ({ editor: a, range: r }) => { a.chain().focus().deleteRange(r).setHeading({ level: e }).run(); } }); }), n.name.toLowerCase() === "bulletlist" && t[0].commands.push({ name: "bulletList", label: h.t("editor.bulletlist.tooltip"), aliases: ["ul", "yxlb"], iconName: "List", action: ({ editor: e, range: a }) => { e.chain().focus().deleteRange(a).toggleBulletList().run(); } }), n.name.toLowerCase() === "orderedlist" && t[0].commands.push({ name: "numberedList", label: h.t("editor.orderedlist.tooltip"), aliases: ["ol", "yxlb"], iconName: "ListOrdered", action: ({ editor: e, range: a }) => { e.chain().focus().deleteRange(a).toggleOrderedList().run(); } }), n.name.toLowerCase() === "tasklist" && t[0].commands.push({ name: "taskList", label: h.t("editor.tasklist.tooltip"), iconName: "ListTodo", description: "Task list with todo items", aliases: ["todo"], action: ({ editor: e, range: a }) => { e.chain().focus().deleteRange(a).toggleTaskList().run(); } }), n.name.toLowerCase() === "blockquote" && t[0].commands.push({ name: "blockquote", label: h.t("editor.blockquote.tooltip"), description: "插入引入格式", aliases: ["yr"], iconName: "TextQuote", action: ({ editor: e, range: a }) => { e.chain().focus().deleteRange(a).setBlockquote().run(); } }), n.name.toLowerCase() === "codeblock" && t[0].commands.push({ name: "codeBlock", label: h.t("editor.codeblock.tooltip"), iconName: "Code2", description: "Code block with syntax highlighting", shouldBeHidden: (e) => e.isActive("columns"), action: ({ editor: e, range: a }) => { e.chain().focus().deleteRange(a).setCodeBlock().run(); } }), n.name.toLowerCase() === G.name && t[1].commands.push({ name: "image", label: h.t("editor.image.tooltip"), iconName: "ImageUp", description: "Insert a image", aliases: ["image", "tp", "tupian"], shouldBeHidden: (e) => e.isActive("columns"), action: ({ editor: e, range: a }) => { e.chain().focus().deleteRange(a).run(), F.setOpen(e.id, !0); } }), n.name.toLowerCase() === W.name && t[1].commands.push({ name: "video", label: h.t("editor.video.tooltip"), iconName: "Video", description: "Insert a video", aliases: ["video", "sp", "shipin"], shouldBeHidden: (e) => e.isActive("columns"), action: ({ editor: e, range: a }) => { e.chain().focus().deleteRange(a).run(), J.setOpen(e.id, !0); } }), n.name.toLowerCase() === "table" && t[1].commands.push({ name: "table", label: h.t("editor.table.tooltip"), iconName: "Table", description: "Insert a table", aliases: ["table", "bg", "biaoge", "biao"], shouldBeHidden: (e) => e.isActive("columns"), action: ({ editor: e, range: a }) => { e.chain().focus().deleteRange(a).insertTable({ rows: 3, cols: 3, withHeaderRow: !1 }).run(); } }), n.name.toLowerCase() === "horizontalrule" && t[1].commands.push({ name: "horizontalRule", label: h.t("editor.horizontalrule.tooltip"), iconName: "Minus", description: "Insert a horizontal divider", aliases: ["hr", "fgx", "fg"], action: ({ editor: e, range: a }) => { e.chain().focus().deleteRange(a).setHorizontalRule().run(); } }), n.name.toLowerCase() === "columns" && t[1].commands.push({ name: "columns", label: h.t("editor.columns.tooltip"), iconName: "Columns2", description: "Add two column content", action: ({ editor: e }) => { e.chain().focus().insertColumns({ cols: 2 }).run(); } }), m == null || m(n, t); }), t; } const x = "slashCommand"; let o; const oe = /* @__PURE__ */ A.create({ name: x, priority: 200, onCreate() { o = B("body", { interactive: !0, trigger: "manual", placement: "bottom-start", theme: "slash-command", maxWidth: "16rem", offset: [16, 8], popperOptions: { strategy: "fixed", modifiers: [ { name: "flip", enabled: !1 } ] } }); }, addProseMirrorPlugins() { return [ P({ editor: this.editor, char: "/", allowSpaces: !0, startOfLine: !0, pluginKey: new O(x), allow: ({ state: i, range: m }) => { var f, w, b; const t = i.doc.resolve(m.from), n = t.depth === 1, e = t.parent.type.name === "paragraph", a = ((f = t.parent.textContent) == null ? void 0 : f.charAt(0)) === "/", r = this.editor.isActive("column"), s = (b = t.parent.textContent) == null ? void 0 : b.slice( Math.max(0, (w = t.parent.textContent) == null ? void 0 : w.indexOf("/")) ), u = !(s != null && s.endsWith(" ")); return (n && e && a || r && e && a) && u; }, command: ({ editor: i, range: m, props: t }) => { const { view: n } = i; t.action({ editor: i, range: m }), n.focus(); }, items: ({ query: i, editor: m }) => X(m.extensionManager.extensions, this.options.renderGroupItem).map((r) => ({ ...r, commands: r.commands.filter((s) => { const u = s.label.toLowerCase().trim(), f = i.toLowerCase().trim(); if (s.aliases) { const w = s.aliases.map((C) => C.toLowerCase().trim()), b = u.match(f), L = w.some((C) => C.match(f)); return b || L; } return u.match(f); }).filter( (s) => s.shouldBeHidden ? !s.shouldBeHidden(this.editor) : !0 ) })).filter((r) => r.commands.length > 0).map((r) => ({ ...r, commands: r.commands.map((s) => ({ ...s, isEnabled: !0 })) })), render: () => { let i, m = null; return { onStart: (t) => { var a; i = new D(_, { props: t, editor: t.editor }); const { view: n } = t.editor, e = () => { if (!t.clientRect) return t.editor.storage[x].rect; const r = t.clientRect(); if (!r) return t.editor.storage[x].rect; let s = r.y; if (r.top + i.element.offsetHeight + 40 > window.innerHeight) { const u = r.top + i.element.offsetHeight - window.innerHeight + 40; s = r.y - u; } return new DOMRect(r.x, s, r.width, r.height); }; m = () => { o == null || o[0].setProps({ getReferenceClientRect: e }); }, (a = n.dom.parentElement) == null || a.addEventListener("scroll", m), o == null || o[0].setProps({ getReferenceClientRect: e, appendTo: () => document.body, content: i.element }), o == null || o[0].show(); }, onUpdate(t) { var r; i.updateProps(t); const { view: n } = t.editor, e = () => { if (!t.clientRect) return t.editor.storage[x].rect; const s = t.clientRect(); return s ? new DOMRect(s.x, s.y, s.width, s.height) : t.editor.storage[x].rect; }, a = () => { o == null || o[0].setProps({ getReferenceClientRect: e }); }; (r = n.dom.parentElement) == null || r.addEventListener("scroll", a), t.editor.storage[x].rect = t.clientRect ? e() : { width: 0, height: 0, left: 0, top: 0, right: 0, bottom: 0 }, o == null || o[0].setProps({ getReferenceClientRect: e }); }, onKeyDown(t) { var n; return t.event.key === "Escape" ? (o == null || o[0].hide(), !0) : (o != null && o[0].state.isShown || o == null || o[0].show(), (n = i.ref) == null ? void 0 : n.onKeyDown(t)); }, onExit(t) { var n; if (o == null || o[0].hide(), m) { const { view: e } = t.editor; (n = e.dom.parentElement) == null || n.removeEventListener("scroll", m); } i.destroy(); } }; } }) ]; }, addStorage() { return { rect: { width: 0, height: 0, left: 0, top: 0, right: 0, bottom: 0 } }; } }); export { oe as SlashCommand, oe as default };