@rtdui/editor
Version:
React rich text editor based on tiptap
468 lines (466 loc) • 19.5 kB
JavaScript
'use client';
const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
const require_toolbarStateSelector = require('./toolbarStateSelector.cjs');
const require_ColorPickerPopover = require('./ColorPickerPopover.cjs');
const require_editorMan = require('./editorMan.cjs');
let react = require("react");
let clsx = require("clsx");
clsx = require_runtime.__toESM(clsx);
let _tiptap_react = require("@tiptap/react");
let _tabler_icons_react = require("@tabler/icons-react");
let react_jsx_runtime = require("react/jsx-runtime");
let _rtdui_core = require("@rtdui/core");
//#region packages/editor/src/RichTextEditor/toolbar/Toolbar.tsx
const FONT_SIZES = [
"8",
"9",
"10",
"11",
"12",
"14",
"16",
"18",
"20",
"22",
"24",
"26",
"28",
"36",
"48",
"72"
];
const Toolbar = (props) => {
const { editor, className, ...other } = props;
if (!editor) return null;
const editorState = (0, _tiptap_react.useEditorState)({
editor,
selector: require_toolbarStateSelector.menuBarStateSelector
});
const [color, setColor] = (0, react.useState)("auto");
const [bgColor, setBgColor] = (0, react.useState)("auto");
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
...other,
className: (0, clsx.default)("flex gap-1 p-1", className),
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "join",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "撤销",
onClick: () => editor.chain().focus().undo().run(),
disabled: !editorState.canUndo,
className: (0, clsx.default)("join-item btn btn-xs btn-square"),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconArrowBackUp, {
stroke: 1.5,
size: 18
})
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "重做",
onClick: () => editor.chain().focus().redo().run(),
disabled: !editorState.canRedo,
className: (0, clsx.default)("join-item btn btn-xs btn-square"),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconArrowForwardUp, {
stroke: 1.5,
size: 18
})
})]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "join",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
value: editorState.fontSize,
onChange: (ev) => {
const val = ev.target.value;
if (val === "16") editor.chain().focus().unsetFontSize().run();
else editor.chain().focus().setFontSize(`${val}px`).run();
},
className: "select select-xs w-16 join-item",
children: FONT_SIZES.map((d) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { children: d }, d))
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "join",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "粗体",
onClick: () => editor.chain().focus().toggleBold().run(),
disabled: !editorState.canBold,
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isBold }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconBold, {
stroke: 2,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "斜体",
onClick: () => editor.chain().focus().toggleItalic().run(),
disabled: !editorState.canItalic,
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isItalic }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconItalic, {
stroke: 1.5,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "下划线",
onClick: () => editor.chain().focus().toggleUnderline().run(),
disabled: !editorState.canUnderline,
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isUnderline }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconUnderline, {
stroke: 1.5,
size: 20
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "删除线",
onClick: () => editor.chain().focus().toggleStrike().run(),
disabled: !editorState.canStrike,
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isStrike }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconStrikethrough, {
stroke: 1.5,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "上标",
onClick: () => editor.chain().focus().toggleSuperscript().run(),
disabled: !editorState.canSuperscript,
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isSuperscript }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconSuperscript, {
stroke: 1.5,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "下标",
onClick: () => editor.chain().focus().toggleSubscript().run(),
disabled: !editorState.canSubscript,
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isSubscript }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconSubscript, {
stroke: 1.5,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "代码",
onClick: () => editor.chain().focus().toggleCode().run(),
disabled: !editorState.canCode,
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isCode }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconCode, {
stroke: 1.5,
size: 18
})
})
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "join",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "高亮",
onClick: () => editor.chain().focus().toggleHighlight().run(),
disabled: !editorState.canHighlight,
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isHighlight }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "size-4 flex items-center justify-center bg-warning text-sm font-normal",
children: "A"
})
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "join",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
title: "文本颜色",
onClick: () => {
if (color === "auto") editor.chain().focus().unsetColor().run();
else editor.chain().focus().setColor(color).run();
},
disabled: !editorState.canColor,
className: (0, clsx.default)("join-item btn btn-xs btn-square relative text-sm font-normal"),
children: ["A", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "absolute left-1 right-1 bottom-0.5 z-1 h-1",
style: { backgroundColor: color === "auto" ? "black" : color }
})]
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ColorPickerPopover.ColorPickerPopover, { onChangeEnd: (val) => setColor(val) })]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "join",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
title: "背景颜色",
onClick: () => {
if (bgColor === "auto") editor.chain().focus().unsetBackgroundColor().run();
else editor.chain().focus().setBackgroundColor(bgColor).run();
},
disabled: !editorState.canBgColor,
className: (0, clsx.default)("join-item btn btn-xs btn-square relative text-sm font-normal"),
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconPaint, {
stroke: 1.5,
size: 14
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "absolute left-1 right-1 bottom-0.5 z-1 h-1",
style: { backgroundColor: bgColor === "auto" ? "black" : bgColor }
})]
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ColorPickerPopover.ColorPickerPopover, { onChangeEnd: (val) => setBgColor(val) })]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "join",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "清除格式",
onClick: () => editor.chain().focus().unsetAllMarks().clearNodes().run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square"),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconEraser, {
stroke: 1.5,
size: 18
})
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "join",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "H1",
onClick: () => editor.chain().focus().toggleHeading({ level: 1 }).run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isHeading1 }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconH1, {
stroke: 1.5,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "H2",
onClick: () => editor.chain().focus().toggleHeading({ level: 2 }).run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isHeading2 }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconH2, {
stroke: 1.5,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "H3",
onClick: () => editor.chain().focus().toggleHeading({ level: 3 }).run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isHeading3 }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconH3, {
stroke: 1.5,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "H4",
onClick: () => editor.chain().focus().toggleHeading({ level: 4 }).run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isHeading4 }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconH4, {
stroke: 1.5,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "H5",
onClick: () => editor.chain().focus().toggleHeading({ level: 5 }).run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isHeading5 }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconH5, {
stroke: 1.5,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "H6",
onClick: () => editor.chain().focus().toggleHeading({ level: 6 }).run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isHeading6 }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconH6, {
stroke: 1.5,
size: 18
})
})
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "join",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "符号列表",
onClick: () => editor.chain().focus().toggleBulletList().run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isBulletList }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconList, {
stroke: 1.5,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "序号列表",
onClick: () => editor.chain().focus().toggleOrderedList().run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isOrderedList }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconListNumbers, {
stroke: 1.5,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "代码块",
onClick: () => editor.chain().focus().toggleCodeBlock().run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isCodeBlock }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconCodeblock, {
stroke: 1.5,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "引用",
onClick: () => editor.chain().focus().toggleBlockquote().run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isBlockquote }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconBlockquote, {
stroke: 1.5,
size: 18
})
})
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "join",
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "左对齐",
onClick: () => editor.chain().focus().toggleTextAlign("left").run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isLeftAlign }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconAlignLeft, {
stroke: 1.5,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "居中对齐",
onClick: () => editor.chain().focus().toggleTextAlign("center").run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isCenterAlign }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconAlignCenter, {
stroke: 1.5,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "右对齐",
onClick: () => editor.chain().focus().toggleTextAlign("right").run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isRightAlign }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconAlignRight, {
stroke: 1.5,
size: 18
})
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "两端对齐",
onClick: () => editor.chain().focus().toggleTextAlign("justify").run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isJustifyAlign }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconAlignJustified, {
stroke: 1.5,
size: 18
})
})
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "join",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "超链接",
onClick: () => {
const previousUrl = editor.getAttributes("link").href;
const url = window.prompt("URL", previousUrl);
if (url === null) return;
if (url === "") {
editor.chain().focus().extendMarkRange("link").unsetLink().run();
return;
}
try {
editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
} catch (e) {
alert(e.message);
}
},
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isLink }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconLink, {
stroke: 1.5,
size: 18
})
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "取消超链接",
onClick: () => editor.chain().focus().unsetLink().run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isLink }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconUnlink, {
stroke: 1.5,
size: 18
})
})]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "join",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "水平线",
onClick: () => editor.chain().focus().setHorizontalRule().run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square"),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconSeparator, {
stroke: 1.5,
size: 18
})
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "断行",
onClick: () => editor.chain().focus().setHardBreak().run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square", { "btn-active": editorState.isHardBreak }),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconCornerDownLeft, {
stroke: 1.5,
size: 18
})
})]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "join",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "插入图片",
onClick: () => editor.chain().focus().uploadImage({}).run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square"),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconPhoto, {
stroke: 1.5,
size: 18
})
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
title: "插入表格",
onClick: () => editor.chain().focus().insertTable({
rows: 3,
cols: 3,
withHeaderRow: true
}).run(),
className: (0, clsx.default)("join-item btn btn-xs btn-square"),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconTable, {
stroke: 1.5,
size: 18
})
})]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: "join",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
type: "button",
className: "join-item btn btn-xs btn-square",
onClick: () => document.getElementById("edit_helper_dialog")?.showModal(),
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconHelp, {
size: 20,
stroke: 1.5
})
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dialog", {
id: "edit_helper_dialog",
className: "modal",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: (0, clsx.default)("modal-box flex flex-col p-6 w-11/12 max-w-5xl", className),
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("form", {
method: "dialog",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "sticky top-0 flex justify-end mb-1",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rtdui_core.CloseButton, { type: "submit" })
})
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: "flex-1 overflow-y-auto prose max-w-none",
...other,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_editorMan.default, {})
})]
})
})]
})
]
});
};
//#endregion
exports.Toolbar = Toolbar;