reactjs-tiptap-editor
Version:
A modern WYSIWYG rich text editor based on tiptap and shadcn ui for React
708 lines (707 loc) • 20.8 kB
JavaScript
import { N as L, w as k, m, r as E, E as H, p as M, i as g, a as B, b as j, g as $, c as K } from "./clsx-DaPvp9ji.js";
var V = Object.defineProperty, X = (t, e) => {
for (var s in e)
V(t, s, { get: e[s], enumerable: !0 });
}, z = "listItem", x = "textStyle", w = /^\s*([-+*])\s$/, F = L.create({
name: "bulletList",
addOptions() {
return {
itemTypeName: "listItem",
HTMLAttributes: {},
keepMarks: !1,
keepAttributes: !1
};
},
group: "block list",
content() {
return `${this.options.itemTypeName}+`;
},
parseHTML() {
return [{ tag: "ul" }];
},
renderHTML({ HTMLAttributes: t }) {
return ["ul", m(this.options.HTMLAttributes, t), 0];
},
markdownTokenName: "list",
parseMarkdown: (t, e) => t.type !== "list" || t.ordered ? [] : {
type: "bulletList",
content: t.items ? e.parseChildren(t.items) : []
},
renderMarkdown: (t, e) => t.content ? e.renderChildren(t.content, `
`) : "",
markdownOptions: {
indentsContent: !0
},
addCommands() {
return {
toggleBulletList: () => ({ commands: t, chain: e }) => this.options.keepAttributes ? e().toggleList(this.name, this.options.itemTypeName, this.options.keepMarks).updateAttributes(z, this.editor.getAttributes(x)).run() : t.toggleList(this.name, this.options.itemTypeName, this.options.keepMarks)
};
},
addKeyboardShortcuts() {
return {
"Mod-Shift-8": () => this.editor.commands.toggleBulletList()
};
},
addInputRules() {
let t = k({
find: w,
type: this.type
});
return (this.options.keepMarks || this.options.keepAttributes) && (t = k({
find: w,
type: this.type,
keepMarks: this.options.keepMarks,
keepAttributes: this.options.keepAttributes,
getAttributes: () => this.editor.getAttributes(x),
editor: this.editor
})), [t];
}
}), G = L.create({
name: "listItem",
addOptions() {
return {
HTMLAttributes: {},
bulletListTypeName: "bulletList",
orderedListTypeName: "orderedList"
};
},
content: "paragraph block*",
defining: !0,
parseHTML() {
return [
{
tag: "li"
}
];
},
renderHTML({ HTMLAttributes: t }) {
return ["li", m(this.options.HTMLAttributes, t), 0];
},
markdownTokenName: "list_item",
parseMarkdown: (t, e) => {
if (t.type !== "list_item")
return [];
let s = [];
if (t.tokens && t.tokens.length > 0)
if (t.tokens.some((r) => r.type === "paragraph"))
s = e.parseChildren(t.tokens);
else {
const r = t.tokens[0];
if (r && r.type === "text" && r.tokens && r.tokens.length > 0) {
if (s = [
{
type: "paragraph",
content: e.parseInline(r.tokens)
}
], t.tokens.length > 1) {
const c = t.tokens.slice(1), a = e.parseChildren(c);
s.push(...a);
}
} else
s = e.parseChildren(t.tokens);
}
return s.length === 0 && (s = [
{
type: "paragraph",
content: []
}
]), {
type: "listItem",
content: s
};
},
renderMarkdown: (t, e, s) => E(
t,
e,
(n) => n.parentType === "bulletList" ? "- " : n.parentType === "orderedList" ? `${n.index + 1}. ` : "- ",
s
),
addKeyboardShortcuts() {
return {
Enter: () => this.editor.commands.splitListItem(this.name),
Tab: () => this.editor.commands.sinkListItem(this.name),
"Shift-Tab": () => this.editor.commands.liftListItem(this.name)
};
}
}), W = {};
X(W, {
findListItemPos: () => b,
getNextListDepth: () => T,
handleBackspace: () => v,
handleDelete: () => I,
hasListBefore: () => S,
hasListItemAfter: () => q,
hasListItemBefore: () => O,
listItemHasSubList: () => P,
nextListIsDeeper: () => _,
nextListIsHigher: () => D
});
var b = (t, e) => {
const { $from: s } = e.selection, n = $(t, e.schema);
let r = null, i = s.depth, c = s.pos, a = null;
for (; i > 0 && a === null; )
r = s.node(i), r.type === n ? a = i : (i -= 1, c -= 1);
return a === null ? null : { $pos: e.doc.resolve(c), depth: a };
}, T = (t, e) => {
const s = b(t, e);
if (!s)
return !1;
const [, n] = K(e, t, s.$pos.pos + 4);
return n;
}, S = (t, e, s) => {
const { $anchor: n } = t.selection, r = Math.max(0, n.pos - 2), i = t.doc.resolve(r).node();
return !(!i || !s.includes(i.type.name));
}, O = (t, e) => {
var s;
const { $anchor: n } = e.selection, r = e.doc.resolve(n.pos - 2);
return !(r.index() === 0 || ((s = r.nodeBefore) == null ? void 0 : s.type.name) !== t);
}, P = (t, e, s) => {
if (!s)
return !1;
const n = $(t, e.schema);
let r = !1;
return s.descendants((i) => {
i.type === n && (r = !0);
}), r;
}, v = (t, e, s) => {
if (t.commands.undoInputRule())
return !0;
if (t.state.selection.from !== t.state.selection.to)
return !1;
if (!g(t.state, e) && S(t.state, e, s)) {
const { $anchor: a } = t.state.selection, p = t.state.doc.resolve(a.before() - 1), h = [];
p.node().descendants((l, u) => {
l.type.name === e && h.push({ node: l, pos: u });
});
const o = h.at(-1);
if (!o)
return !1;
const d = t.state.doc.resolve(p.start() + o.pos + 1);
return t.chain().cut({ from: a.start() - 1, to: a.end() + 1 }, d.end()).joinForward().run();
}
if (!g(t.state, e) || !B(t.state))
return !1;
const n = b(e, t.state);
if (!n)
return !1;
const i = t.state.doc.resolve(n.$pos.pos - 2).node(n.depth), c = P(e, t.state, i);
return O(e, t.state) && !c ? t.commands.joinItemBackward() : t.chain().liftListItem(e).run();
}, _ = (t, e) => {
const s = T(t, e), n = b(t, e);
return !n || !s ? !1 : s > n.depth;
}, D = (t, e) => {
const s = T(t, e), n = b(t, e);
return !n || !s ? !1 : s < n.depth;
}, I = (t, e) => {
if (!g(t.state, e) || !j(t.state, e))
return !1;
const { selection: s } = t.state, { $from: n, $to: r } = s;
return !s.empty && n.sameParent(r) ? !1 : _(e, t.state) ? t.chain().focus(t.state.selection.from + 4).lift(e).joinBackward().run() : D(e, t.state) ? t.chain().joinForward().joinBackward().run() : t.commands.joinItemForward();
}, q = (t, e) => {
var s;
const { $anchor: n } = e.selection, r = e.doc.resolve(n.pos - n.parentOffset - 2);
return !(r.index() === r.parent.childCount - 1 || ((s = r.nodeAfter) == null ? void 0 : s.type.name) !== t);
}, Y = H.create({
name: "listKeymap",
addOptions() {
return {
listTypes: [
{
itemName: "listItem",
wrapperNames: ["bulletList", "orderedList"]
},
{
itemName: "taskItem",
wrapperNames: ["taskList"]
}
]
};
},
addKeyboardShortcuts() {
return {
Delete: ({ editor: t }) => {
let e = !1;
return this.options.listTypes.forEach(({ itemName: s }) => {
t.state.schema.nodes[s] !== void 0 && I(t, s) && (e = !0);
}), e;
},
"Mod-Delete": ({ editor: t }) => {
let e = !1;
return this.options.listTypes.forEach(({ itemName: s }) => {
t.state.schema.nodes[s] !== void 0 && I(t, s) && (e = !0);
}), e;
},
Backspace: ({ editor: t }) => {
let e = !1;
return this.options.listTypes.forEach(({ itemName: s, wrapperNames: n }) => {
t.state.schema.nodes[s] !== void 0 && v(t, s, n) && (e = !0);
}), e;
},
"Mod-Backspace": ({ editor: t }) => {
let e = !1;
return this.options.listTypes.forEach(({ itemName: s, wrapperNames: n }) => {
t.state.schema.nodes[s] !== void 0 && v(t, s, n) && (e = !0);
}), e;
}
};
}
}), A = /^(\s*)(\d+)\.\s+(.*)$/, J = /^\s/;
function Q(t) {
const e = [];
let s = 0, n = 0;
for (; s < t.length; ) {
const r = t[s], i = r.match(A);
if (!i)
break;
const [, c, a, p] = i, h = c.length;
let o = p, d = s + 1;
const l = [r];
for (; d < t.length; ) {
const u = t[d];
if (u.match(A))
break;
if (u.trim() === "")
l.push(u), o += `
`, d += 1;
else if (u.match(J))
l.push(u), o += `
${u.slice(h + 2)}`, d += 1;
else
break;
}
e.push({
indent: h,
number: parseInt(a, 10),
content: o.trim(),
raw: l.join(`
`)
}), n = d, s = d;
}
return [e, n];
}
function R(t, e, s) {
var n;
const r = [];
let i = 0;
for (; i < t.length; ) {
const c = t[i];
if (c.indent === e) {
const a = c.content.split(`
`), p = ((n = a[0]) == null ? void 0 : n.trim()) || "", h = [];
p && h.push({
type: "paragraph",
raw: p,
tokens: s.inlineTokens(p)
});
const o = a.slice(1).join(`
`).trim();
if (o) {
const u = s.blockTokens(o);
h.push(...u);
}
let d = i + 1;
const l = [];
for (; d < t.length && t[d].indent > e; )
l.push(t[d]), d += 1;
if (l.length > 0) {
const u = Math.min(...l.map((y) => y.indent)), f = R(l, u, s);
h.push({
type: "list",
ordered: !0,
start: l[0].number,
items: f,
raw: l.map((y) => y.raw).join(`
`)
});
}
r.push({
type: "list_item",
raw: c.raw,
tokens: h
}), i = d;
} else
i += 1;
}
return r;
}
function U(t, e) {
return t.map((s) => {
if (s.type !== "list_item")
return e.parseChildren([s])[0];
const n = [];
return s.tokens && s.tokens.length > 0 && s.tokens.forEach((r) => {
if (r.type === "paragraph" || r.type === "list" || r.type === "blockquote" || r.type === "code")
n.push(...e.parseChildren([r]));
else if (r.type === "text" && r.tokens) {
const i = e.parseChildren([r]);
n.push({
type: "paragraph",
content: i
});
} else {
const i = e.parseChildren([r]);
i.length > 0 && n.push(...i);
}
}), {
type: "listItem",
content: n
};
});
}
var Z = "listItem", C = "textStyle", N = /^(\d+)\.\s$/, tt = L.create({
name: "orderedList",
addOptions() {
return {
itemTypeName: "listItem",
HTMLAttributes: {},
keepMarks: !1,
keepAttributes: !1
};
},
group: "block list",
content() {
return `${this.options.itemTypeName}+`;
},
addAttributes() {
return {
start: {
default: 1,
parseHTML: (t) => t.hasAttribute("start") ? parseInt(t.getAttribute("start") || "", 10) : 1
},
type: {
default: null,
parseHTML: (t) => t.getAttribute("type")
}
};
},
parseHTML() {
return [
{
tag: "ol"
}
];
},
renderHTML({ HTMLAttributes: t }) {
const { start: e, ...s } = t;
return e === 1 ? ["ol", m(this.options.HTMLAttributes, s), 0] : ["ol", m(this.options.HTMLAttributes, t), 0];
},
markdownTokenName: "list",
parseMarkdown: (t, e) => {
if (t.type !== "list" || !t.ordered)
return [];
const s = t.start || 1, n = t.items ? U(t.items, e) : [];
return s !== 1 ? {
type: "orderedList",
attrs: { start: s },
content: n
} : {
type: "orderedList",
content: n
};
},
renderMarkdown: (t, e) => t.content ? e.renderChildren(t.content, `
`) : "",
markdownTokenizer: {
name: "orderedList",
level: "block",
start: (t) => {
const e = t.match(/^(\s*)(\d+)\.\s+/), s = e == null ? void 0 : e.index;
return s !== void 0 ? s : -1;
},
tokenize: (t, e, s) => {
var n;
const r = t.split(`
`), [i, c] = Q(r);
if (i.length === 0)
return;
const a = R(i, 0, s);
return a.length === 0 ? void 0 : {
type: "list",
ordered: !0,
start: ((n = i[0]) == null ? void 0 : n.number) || 1,
items: a,
raw: r.slice(0, c).join(`
`)
};
}
},
markdownOptions: {
indentsContent: !0
},
addCommands() {
return {
toggleOrderedList: () => ({ commands: t, chain: e }) => this.options.keepAttributes ? e().toggleList(this.name, this.options.itemTypeName, this.options.keepMarks).updateAttributes(Z, this.editor.getAttributes(C)).run() : t.toggleList(this.name, this.options.itemTypeName, this.options.keepMarks)
};
},
addKeyboardShortcuts() {
return {
"Mod-Shift-7": () => this.editor.commands.toggleOrderedList()
};
},
addInputRules() {
let t = k({
find: N,
type: this.type,
getAttributes: (e) => ({ start: +e[1] }),
joinPredicate: (e, s) => s.childCount + s.attrs.start === +e[1]
});
return (this.options.keepMarks || this.options.keepAttributes) && (t = k({
find: N,
type: this.type,
keepMarks: this.options.keepMarks,
keepAttributes: this.options.keepAttributes,
getAttributes: (e) => ({ start: +e[1], ...this.editor.getAttributes(C) }),
joinPredicate: (e, s) => s.childCount + s.attrs.start === +e[1],
editor: this.editor
})), [t];
}
}), et = /^\s*(\[([( |x])?\])\s$/, st = L.create({
name: "taskItem",
addOptions() {
return {
nested: !1,
HTMLAttributes: {},
taskListTypeName: "taskList",
a11y: void 0
};
},
content() {
return this.options.nested ? "paragraph block*" : "paragraph+";
},
defining: !0,
addAttributes() {
return {
checked: {
default: !1,
keepOnSplit: !1,
parseHTML: (t) => {
const e = t.getAttribute("data-checked");
return e === "" || e === "true";
},
renderHTML: (t) => ({
"data-checked": t.checked
})
}
};
},
parseHTML() {
return [
{
tag: `li[data-type="${this.name}"]`,
priority: 51
}
];
},
renderHTML({ node: t, HTMLAttributes: e }) {
return [
"li",
m(this.options.HTMLAttributes, e, {
"data-type": this.name
}),
[
"label",
[
"input",
{
type: "checkbox",
checked: t.attrs.checked ? "checked" : null
}
],
["span"]
],
["div", 0]
];
},
parseMarkdown: (t, e) => {
const s = [];
if (t.tokens && t.tokens.length > 0 ? s.push(e.createNode("paragraph", {}, e.parseInline(t.tokens))) : t.text ? s.push(e.createNode("paragraph", {}, [e.createNode("text", { text: t.text })])) : s.push(e.createNode("paragraph", {}, [])), t.nestedTokens && t.nestedTokens.length > 0) {
const n = e.parseChildren(t.nestedTokens);
s.push(...n);
}
return e.createNode("taskItem", { checked: t.checked || !1 }, s);
},
renderMarkdown: (t, e) => {
var s;
const r = `- [${(s = t.attrs) != null && s.checked ? "x" : " "}] `;
return E(t, e, r);
},
addKeyboardShortcuts() {
const t = {
Enter: () => this.editor.commands.splitListItem(this.name),
"Shift-Tab": () => this.editor.commands.liftListItem(this.name)
};
return this.options.nested ? {
...t,
Tab: () => this.editor.commands.sinkListItem(this.name)
} : t;
},
addNodeView() {
return ({ node: t, HTMLAttributes: e, getPos: s, editor: n }) => {
const r = document.createElement("li"), i = document.createElement("label"), c = document.createElement("span"), a = document.createElement("input"), p = document.createElement("div"), h = (o) => {
var d, l;
a.ariaLabel = ((l = (d = this.options.a11y) == null ? void 0 : d.checkboxLabel) == null ? void 0 : l.call(d, o, a.checked)) || `Task item checkbox for ${o.textContent || "empty task item"}`;
};
return h(t), i.contentEditable = "false", a.type = "checkbox", a.addEventListener("mousedown", (o) => o.preventDefault()), a.addEventListener("change", (o) => {
if (!n.isEditable && !this.options.onReadOnlyChecked) {
a.checked = !a.checked;
return;
}
const { checked: d } = o.target;
n.isEditable && typeof s == "function" && n.chain().focus(void 0, { scrollIntoView: !1 }).command(({ tr: l }) => {
const u = s();
if (typeof u != "number")
return !1;
const f = l.doc.nodeAt(u);
return l.setNodeMarkup(u, void 0, {
...f == null ? void 0 : f.attrs,
checked: d
}), !0;
}).run(), !n.isEditable && this.options.onReadOnlyChecked && (this.options.onReadOnlyChecked(t, d) || (a.checked = !a.checked));
}), Object.entries(this.options.HTMLAttributes).forEach(([o, d]) => {
r.setAttribute(o, d);
}), r.dataset.checked = t.attrs.checked, a.checked = t.attrs.checked, i.append(a, c), r.append(i, p), Object.entries(e).forEach(([o, d]) => {
r.setAttribute(o, d);
}), {
dom: r,
contentDOM: p,
update: (o) => o.type !== this.type ? !1 : (r.dataset.checked = o.attrs.checked, a.checked = o.attrs.checked, h(o), !0)
};
};
},
addInputRules() {
return [
k({
find: et,
type: this.type,
getAttributes: (t) => ({
checked: t[t.length - 1] === "x"
})
})
];
}
}), nt = L.create({
name: "taskList",
addOptions() {
return {
itemTypeName: "taskItem",
HTMLAttributes: {}
};
},
group: "block list",
content() {
return `${this.options.itemTypeName}+`;
},
parseHTML() {
return [
{
tag: `ul[data-type="${this.name}"]`,
priority: 51
}
];
},
renderHTML({ HTMLAttributes: t }) {
return ["ul", m(this.options.HTMLAttributes, t, { "data-type": this.name }), 0];
},
parseMarkdown: (t, e) => e.createNode("taskList", {}, e.parseChildren(t.items || [])),
renderMarkdown: (t, e) => t.content ? e.renderChildren(t.content, `
`) : "",
markdownTokenizer: {
name: "taskList",
level: "block",
start(t) {
var e;
const s = (e = t.match(/^\s*[-+*]\s+\[([ xX])\]\s+/)) == null ? void 0 : e.index;
return s !== void 0 ? s : -1;
},
tokenize(t, e, s) {
const n = (i) => {
const c = M(
i,
{
itemPattern: /^(\s*)([-+*])\s+\[([ xX])\]\s+(.*)$/,
extractItemData: (a) => ({
indentLevel: a[1].length,
mainContent: a[4],
checked: a[3].toLowerCase() === "x"
}),
createToken: (a, p) => ({
type: "taskItem",
raw: "",
mainContent: a.mainContent,
indentLevel: a.indentLevel,
checked: a.checked,
text: a.mainContent,
tokens: s.inlineTokens(a.mainContent),
nestedTokens: p
}),
// Allow recursive nesting
customNestedParser: n
},
s
);
return c ? [
{
type: "taskList",
raw: c.raw,
items: c.items
}
] : s.blockTokens(i);
}, r = M(
t,
{
itemPattern: /^(\s*)([-+*])\s+\[([ xX])\]\s+(.*)$/,
extractItemData: (i) => ({
indentLevel: i[1].length,
mainContent: i[4],
checked: i[3].toLowerCase() === "x"
}),
createToken: (i, c) => ({
type: "taskItem",
raw: "",
mainContent: i.mainContent,
indentLevel: i.indentLevel,
checked: i.checked,
text: i.mainContent,
tokens: s.inlineTokens(i.mainContent),
nestedTokens: c
}),
// Use the recursive parser for nested content
customNestedParser: n
},
s
);
if (r)
return {
type: "taskList",
raw: r.raw,
items: r.items
};
}
},
markdownOptions: {
indentsContent: !0
},
addCommands() {
return {
toggleTaskList: () => ({ commands: t }) => t.toggleList(this.name, this.options.itemTypeName)
};
},
addKeyboardShortcuts() {
return {
"Mod-Shift-9": () => this.editor.commands.toggleTaskList()
};
}
});
H.create({
name: "listKit",
addExtensions() {
const t = [];
return this.options.bulletList !== !1 && t.push(F.configure(this.options.bulletList)), this.options.listItem !== !1 && t.push(G.configure(this.options.listItem)), this.options.listKeymap !== !1 && t.push(Y.configure(this.options.listKeymap)), this.options.orderedList !== !1 && t.push(tt.configure(this.options.orderedList)), this.options.taskItem !== !1 && t.push(st.configure(this.options.taskItem)), this.options.taskList !== !1 && t.push(nt.configure(this.options.taskList)), t;
}
});
export {
F as B,
tt as O,
nt as T,
st as a
};