UNPKG

@rtdui/editor

Version:

React rich text editor based on tiptap

468 lines (466 loc) 19.5 kB
'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;