reactjs-tiptap-editor
Version:
A modern WYSIWYG rich text editor based on tiptap and shadcn ui for React
371 lines (370 loc) • 13.4 kB
JavaScript
import { E as Q } from "./clsx-DaPvp9ji.js";
import { Plugin as X, PluginKey as Y } from "@tiptap/pm/state";
import { DecorationSet as $, Decoration as Z } from "@tiptap/pm/view";
import { jsx as u, Fragment as _, jsxs as T } from "react/jsx-runtime";
import { useState as b, useEffect as k } from "react";
import { h as ee, u as te, e as se, A as re, L as B, j as V, B as N, I as q, C as ne } from "./index-RcSPeQHn.js";
import "./theme.js";
import { u as ce } from "./index-C07N8gA1.js";
import { P as ae, a as le, b as oe } from "./popover-CtinPbiy.js";
function Ce() {
const { t } = ce(), e = ee(), s = te(xe.name), {
icon: n = void 0,
tooltip: i = void 0,
shortcutKeys: l = void 0,
tooltipOptions: c = {},
action: h = void 0,
isActive: m = void 0
} = (s == null ? void 0 : s.componentProps) ?? {}, { disabled: o } = se(m), [f, d] = b(!1), [x, A] = b(""), [S, w] = b(""), [I, M] = b(!1), [W, j] = b(""), y = () => {
var r, a, g, p;
j(`${((a = (r = e == null ? void 0 : e.storage) == null ? void 0 : r.searchAndReplace) == null ? void 0 : a.resultIndex) + 1}/${(p = (g = e == null ? void 0 : e.storage) == null ? void 0 : g.searchAndReplace) == null ? void 0 : p.results.length}`);
};
k(() => {
e && y();
}, [e]);
const H = () => {
o || h && h();
}, L = (r = !1) => {
var a, g, p, R, v, F, P, O;
e && (r && ((g = (a = e == null ? void 0 : e.commands) == null ? void 0 : a.resetIndex) == null || g.call(a)), (R = (p = e == null ? void 0 : e.commands) == null ? void 0 : p.setSearchTerm) == null || R.call(p, x), (F = (v = e == null ? void 0 : e.commands) == null ? void 0 : v.setReplaceTerm) == null || F.call(v, S), (O = (P = e == null ? void 0 : e.commands) == null ? void 0 : P.setCaseSensitive) == null || O.call(P, I), y());
}, E = () => {
var R, v;
if (!e) return;
const { results: r, resultIndex: a } = e.storage.searchAndReplace, g = r[a];
if (!g) return;
(v = (R = e == null ? void 0 : e.commands) == null ? void 0 : R.setTextSelection) == null || v.call(R, g);
const { node: p } = e.view.domAtPos(
e.state.selection.anchor
);
p instanceof HTMLElement && p.scrollIntoView({ behavior: "smooth", block: "center" }), y();
};
k(() => {
x.trim() || D(), x.trim() && L(!0);
}, [x]), k(() => {
S.trim() && L();
}, [S]), k(() => {
L(!0);
}, [I]);
const U = () => {
var r, a;
(a = (r = e == null ? void 0 : e.commands) == null ? void 0 : r.replace) == null || a.call(r), E();
}, z = () => {
var r, a;
(a = (r = e == null ? void 0 : e.commands) == null ? void 0 : r.nextSearchResult) == null || a.call(r), E();
}, G = () => {
var r, a;
(a = (r = e == null ? void 0 : e.commands) == null ? void 0 : r.previousSearchResult) == null || a.call(r), E();
}, D = () => {
var r, a;
A(""), w(""), (a = (r = e == null ? void 0 : e.commands) == null ? void 0 : r.resetIndex) == null || a.call(r), y();
}, J = () => {
var r, a;
(a = (r = e == null ? void 0 : e.commands) == null ? void 0 : r.replaceAll) == null || a.call(r), j("0/0");
};
return s ? /* @__PURE__ */ T(
ae,
{
onOpenChange: d,
open: f,
children: [
/* @__PURE__ */ u(
le,
{
asChild: !0,
disabled: o,
children: /* @__PURE__ */ u(
re,
{
action: H,
disabled: o,
icon: n,
shortcutKeys: l,
tooltip: i,
tooltipOptions: c
}
)
}
),
/* @__PURE__ */ T(
oe,
{
align: "start",
className: "richtext-w-full",
hideWhenDetached: !0,
side: "bottom",
children: [
/* @__PURE__ */ T("div", { className: "richtext-mb-[6px] richtext-flex richtext-items-center richtext-justify-between", children: [
/* @__PURE__ */ u(B, { children: t("editor.search.dialog.text") }),
/* @__PURE__ */ u("span", { className: "richtext-font-semibold", children: W })
] }),
/* @__PURE__ */ T("div", { className: "richtext-mb-[10px] richtext-flex richtext-w-full richtext-max-w-sm richtext-items-center richtext-gap-1.5", children: [
/* @__PURE__ */ u(
V,
{
autoFocus: !0,
className: "richtext-w-full",
onChange: (r) => A(r.target.value),
placeholder: "Text",
required: !0,
type: "text",
value: x
}
),
/* @__PURE__ */ u(
N,
{
className: "richtext-flex-1",
onClick: G,
children: /* @__PURE__ */ u(q, { name: "ChevronUp" })
}
),
/* @__PURE__ */ u(
N,
{
className: "richtext-flex-1",
onClick: z,
children: /* @__PURE__ */ u(q, { name: "ChevronDown" })
}
),
/* @__PURE__ */ u(
N,
{
className: "richtext-flex-1",
onClick: D,
children: "Clear"
}
)
] }),
/* @__PURE__ */ u(B, { className: "richtext-mb-[6px]", children: t("editor.replace.dialog.text") }),
/* @__PURE__ */ u("div", { className: "richtext-mb-[5px] richtext-flex richtext-w-full richtext-max-w-sm richtext-items-center richtext-gap-1.5", children: /* @__PURE__ */ u("div", { className: "richtext-relative richtext-w-full richtext-max-w-sm richtext-items-center", children: /* @__PURE__ */ u(
V,
{
className: "richtext-w-80",
onChange: (r) => w(r.target.value),
placeholder: "Text",
required: !0,
type: "text",
value: S
}
) }) }),
/* @__PURE__ */ T("div", { className: "richtext-my-[10px] richtext-flex richtext-items-center richtext-gap-1", children: [
/* @__PURE__ */ u(
ne,
{
checked: I,
onCheckedChange: (r) => {
M(r), e.commands.setCaseSensitive(r);
}
}
),
/* @__PURE__ */ u(B, { children: t("editor.replace.caseSensitive") })
] }),
/* @__PURE__ */ T("div", { className: "richtext-flex richtext-items-center richtext-gap-[10px]", children: [
/* @__PURE__ */ u(
N,
{
className: "richtext-flex-1",
onClick: U,
children: t("editor.replace.dialog.text")
}
),
/* @__PURE__ */ u(
N,
{
className: "richtext-flex-1",
onClick: J,
children: t("editor.replaceAll.dialog.text")
}
)
] })
]
}
)
]
}
) : /* @__PURE__ */ u(_, {});
}
const C = (t, e) => e(t.tr);
function ie(t, e, s) {
return RegExp(
e ? t.replace(/[$()*+.?[\\\]^{|}]/g, String.raw`\$&`) : t,
s ? "gu" : "gui"
);
}
function ue(t, e, s, n) {
const i = [], l = [];
let c = [], h = 0;
if (!e)
return {
decorationsToReturn: $.empty,
results: []
};
t == null || t.descendants((m, o) => {
m.isText ? c[h] ? c[h] = {
text: c[h].text + m.text,
pos: c[h].pos
} : c[h] = {
text: `${m.text}`,
pos: o
} : h += 1;
}), c = c.filter(Boolean);
for (const m of c) {
const { text: o, pos: f } = m, d = Array.from(o.matchAll(e)).filter(
([x]) => x.trim()
);
for (const x of d) {
if (x[0] === "") break;
x.index !== void 0 && l.push({
from: f + x.index,
to: f + x.index + x[0].length
});
}
}
for (const [m, o] of l.entries()) {
const f = m === n ? `${s} ${s}-current` : s, d = Z.inline(o.from, o.to, {
class: f
});
i.push(d);
}
return {
decorationsToReturn: $.create(t, i),
results: l
};
}
function K(t, e, { state: s, dispatch: n }) {
if (!e[0]) return;
const { from: l, to: c } = e[0];
n && n(s.tr.insertText(t, l, c));
}
function he(t, e, s, n) {
const i = e + 1;
if (!n[i]) return null;
const { from: l, to: c } = n[e], h = c - l - t.length + s, { from: m, to: o } = n[i];
return n[i] = {
to: o - h,
from: m - h
}, [h, n];
}
function me(t, e, { tr: s, dispatch: n }) {
let i = 0, l = e.slice();
if (l.length !== 0) {
for (let c = 0; c < l.length; c += 1) {
const { from: h, to: m } = l[c];
s.insertText(t, h, m);
const o = he(
t,
c,
i,
l
);
o && (i = o[0], l = o[1]);
}
n(s);
}
}
const xe = Q.create({
name: "searchAndReplace",
addOptions() {
var t;
return {
...(t = this.parent) == null ? void 0 : t.call(this),
searchTerm: "",
replaceTerm: "",
results: [],
searchResultClass: "search-result",
searchResultCurrentClass: "search-result-current",
caseSensitive: !1,
disableRegex: !0,
onChange: () => {
},
button: ({ editor: e, t: s }) => ({
// component: RichTextSearchAndReplace,
componentProps: {
action: () => {
},
icon: "SearchAndReplace",
tooltip: s("editor.searchAndReplace.tooltip"),
isActive: () => !0,
editor: e
}
})
};
},
addStorage() {
return {
searchTerm: "",
replaceTerm: "",
results: [],
lastSearchTerm: "",
caseSensitive: !1,
lastCaseSensitive: !1,
resultIndex: 0,
lastResultIndex: 0
};
},
addCommands() {
return {
setSearchTerm: (t) => ({ editor: e, state: s, dispatch: n }) => (e.storage.searchAndReplace.searchTerm = t, C(s, n), !1),
setReplaceTerm: (t) => ({ editor: e, state: s, dispatch: n }) => (e.storage.searchAndReplace.replaceTerm = t, C(s, n), !1),
setCaseSensitive: (t) => ({ editor: e, state: s, dispatch: n }) => (e.storage.searchAndReplace.caseSensitive = t, C(s, n), !1),
resetIndex: () => ({ editor: t, state: e, dispatch: s }) => (t.storage.searchAndReplace.resultIndex = 0, C(e, s), !1),
nextSearchResult: () => ({ editor: t }) => {
const { results: e, resultIndex: s } = t.storage.searchAndReplace, n = s + 1;
return e[n] ? t.storage.searchAndReplace.resultIndex = n : t.storage.searchAndReplace.resultIndex = 0, !1;
},
previousSearchResult: () => ({ editor: t }) => {
const { results: e, resultIndex: s } = t.storage.searchAndReplace, n = s - 1;
return e[n] ? t.storage.searchAndReplace.resultIndex = n : t.storage.searchAndReplace.resultIndex = e.length - 1, !1;
},
replace: () => ({ editor: t, state: e, dispatch: s }) => {
const { replaceTerm: n, results: i, resultIndex: l } = t.storage.searchAndReplace, c = i[l];
return c ? (K(n, [c], { state: e, dispatch: s }), t.storage.searchAndReplace.results.splice(l, 1)) : (K(n, i, { state: e, dispatch: s }), t.storage.searchAndReplace.results.shift()), C(e, s), !1;
},
replaceAll: () => ({ editor: t, tr: e, state: s, dispatch: n }) => {
const { replaceTerm: i, results: l } = t.storage.searchAndReplace;
return me(i, l, { tr: e, dispatch: n }), t.storage.searchAndReplace.resultIndex = 0, t.storage.searchAndReplace.results = [], C(s, n), !1;
}
};
},
addProseMirrorPlugins() {
const t = this.editor, { searchResultClass: e, disableRegex: s } = this.options, n = (c) => t.storage.searchAndReplace.lastSearchTerm = c, i = (c) => t.storage.searchAndReplace.lastCaseSensitive = c, l = (c) => t.storage.searchAndReplace.lastResultIndex = c;
return [
new X({
key: new Y(`richtextCustomPlugin${this.name}`),
state: {
init: () => $.empty,
apply({ doc: c, docChanged: h }, m) {
const {
searchTerm: o,
lastSearchTerm: f,
caseSensitive: d,
lastCaseSensitive: x,
resultIndex: A,
lastResultIndex: S
} = t.storage.searchAndReplace;
if (!h && f === o && x === d && S === A)
return m;
if (n(o), i(d), l(A), !o)
return t.storage.searchAndReplace.results = [], $.empty;
const { decorationsToReturn: w, results: I } = ue(
c,
ie(o, s, d),
e,
A
);
return t.storage.searchAndReplace.results = I, w;
}
},
props: {
decorations(c) {
return this.getState(c);
}
}
})
];
}
});
export {
Ce as RichTextSearchAndReplace,
xe as SearchAndReplace
};