UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

459 lines (458 loc) 21.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.FileItem = void 0; var _react = _interopRequireWildcard(require("react")); var _extensionTextStyle = require("@tiptap/extension-text-style"); var _starterKit = _interopRequireDefault(require("@tiptap/starter-kit")); var _extensionImage = require("@tiptap/extension-image"); var _core = require("@tiptap/core"); var _react2 = require("@tiptap/react"); var _index = require("../../index"); var _constants = require("@douyinfe/semi-foundation/lib/cjs/sidebar/constants"); var _semiIcons = require("@douyinfe/semi-icons"); var _classnames = _interopRequireDefault(require("classnames")); var _code = require("./code"); var _extensionTextAlign = require("@tiptap/extension-text-align"); var _imageSlot = require("./imageSlot"); var _localeConsumer = _interopRequireDefault(require("../../locale/localeConsumer")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } var __rest = void 0 && (void 0).__rest || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; const collapseCls = _constants.cssClasses.COLLAPSE; const prefixCls = _constants.cssClasses.FILE; // 用于保证在输入link ,选区 UI 对用户保持一致性,否则会在输入时候,因为富文本本区域焦点丢失而看不到选区 const SelectionMark = _core.Mark.create({ name: 'selectionMark', inclusive: false, parseHTML() { return [{ tag: 'span.select' }]; }, renderHTML() { return ['span', { class: 'select' }, 0]; } }); const ConfigureButton = /*#__PURE__*/_react.default.memo(props => { const { active, className } = props, rest = __rest(props, ["active", "className"]); return /*#__PURE__*/_react.default.createElement(_index.Button, Object.assign({}, rest, { theme: 'borderless', type: 'tertiary', className: (0, _classnames.default)(`${prefixCls}-menu-bar-btn`, { [`${prefixCls}-menu-bar-btn-active`]: active, [className]: className }) })); }); const ConfigureDropdownItem = /*#__PURE__*/_react.default.memo(props => { const { active, children } = props, rest = __rest(props, ["active", "children"]); return /*#__PURE__*/_react.default.createElement(_index.Dropdown.Item, Object.assign({ className: (0, _classnames.default)(`${prefixCls}-menu-bar-dropdown-item`, { [`${prefixCls}-menu-bar-dropdown-item-active`]: active }) }, rest), children); }); function MenuBar(_ref) { let { editor, className } = _ref; const [linkDropdownVisible, setLinkDropdownVisible] = (0, _react.useState)(false); const [linkInputValue, setLinkInputValue] = (0, _react.useState)(''); const [linkSelectionRange, setLinkSelectionRange] = (0, _react.useState)(null); const editorState = (0, _react2.useEditorState)({ editor, selector: ctx => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4; const { from, to } = ctx.editor.state.selection; const hasSelection = from !== to; const hasCursor = from === to && ctx.editor.isFocused; return { isBold: (_a = ctx.editor.isActive('bold')) !== null && _a !== void 0 ? _a : false, canBold: (_b = ctx.editor.can().chain().toggleBold().run()) !== null && _b !== void 0 ? _b : false, isItalic: (_c = ctx.editor.isActive('italic')) !== null && _c !== void 0 ? _c : false, canItalic: (_d = ctx.editor.can().chain().toggleItalic().run()) !== null && _d !== void 0 ? _d : false, isStrike: (_e = ctx.editor.isActive('strike')) !== null && _e !== void 0 ? _e : false, canStrike: (_f = ctx.editor.can().chain().toggleStrike().run()) !== null && _f !== void 0 ? _f : false, isCode: (_g = ctx.editor.isActive('code')) !== null && _g !== void 0 ? _g : false, canCode: (_h = ctx.editor.can().chain().toggleCode().run()) !== null && _h !== void 0 ? _h : false, canClearMarks: (_j = ctx.editor.can().chain().unsetAllMarks().run()) !== null && _j !== void 0 ? _j : false, isParagraph: (_k = ctx.editor.isActive('paragraph')) !== null && _k !== void 0 ? _k : false, isHeading: (_l = ctx.editor.isActive('heading')) !== null && _l !== void 0 ? _l : false, isHeading1: (_m = ctx.editor.isActive('heading', { level: 1 })) !== null && _m !== void 0 ? _m : false, isHeading2: (_o = ctx.editor.isActive('heading', { level: 2 })) !== null && _o !== void 0 ? _o : false, isHeading3: (_p = ctx.editor.isActive('heading', { level: 3 })) !== null && _p !== void 0 ? _p : false, isHeading4: (_q = ctx.editor.isActive('heading', { level: 4 })) !== null && _q !== void 0 ? _q : false, isHeading5: (_r = ctx.editor.isActive('heading', { level: 5 })) !== null && _r !== void 0 ? _r : false, isHeading6: (_s = ctx.editor.isActive('heading', { level: 6 })) !== null && _s !== void 0 ? _s : false, isBulletList: (_t = ctx.editor.isActive('bulletList')) !== null && _t !== void 0 ? _t : false, isOrderedList: (_u = ctx.editor.isActive('orderedList')) !== null && _u !== void 0 ? _u : false, isCodeBlock: (_v = ctx.editor.isActive('codeBlock')) !== null && _v !== void 0 ? _v : false, isBlockquote: (_w = ctx.editor.isActive('blockquote')) !== null && _w !== void 0 ? _w : false, isLink: (_x = ctx.editor.isActive('link')) !== null && _x !== void 0 ? _x : false, canLink: hasSelection || hasCursor, canUndo: (_y = ctx.editor.can().chain().undo().run()) !== null && _y !== void 0 ? _y : false, canRedo: (_z = ctx.editor.can().chain().redo().run()) !== null && _z !== void 0 ? _z : false, isAlignLeft: (_0 = ctx.editor.isActive({ textAlign: 'left' })) !== null && _0 !== void 0 ? _0 : false, isAlignCenter: (_1 = ctx.editor.isActive({ textAlign: 'center' })) !== null && _1 !== void 0 ? _1 : false, isAlignRight: (_2 = ctx.editor.isActive({ textAlign: 'right' })) !== null && _2 !== void 0 ? _2 : false, isAlignJustify: (_3 = ctx.editor.isActive({ textAlign: 'justify' })) !== null && _3 !== void 0 ? _3 : false, isImage: (_4 = ctx.editor.isActive("imageUpload")) !== null && _4 !== void 0 ? _4 : false, canInsertImage: ctx.editor.can().insertContent({ type: "imageUpload" }) }; } }); const handleConfirmLink = (0, _react.useCallback)(locale => { const href = linkInputValue.trim(); if (!href) { return; } const { from, to } = linkSelectionRange !== null && linkSelectionRange !== void 0 ? linkSelectionRange : editor.state.selection; const chain = editor.chain().focus(); if (from !== to) { // With a selection area, set a link for the currently selected text. chain.setTextSelection({ from, to }).extendMarkRange('link').setLink({ href }).unsetMark('selectionMark').run(); } else { // No selection area but cursor: Insert a linked text at the cursor position. chain.setTextSelection(from).insertContent({ type: 'text', text: href, marks: [{ type: 'link', attrs: { href } }] }).unsetMark('selectionMark').run(); } _index.Toast.success(locale.linkAddSuccess); setLinkDropdownVisible(false); setLinkSelectionRange(null); }, [editor, linkInputValue, linkSelectionRange]); const handleUnsetLink = (0, _react.useCallback)(locale => { editor.chain().focus().unsetLink().unsetMark('selectionMark').run(); _index.Toast.success(locale.linkRemoveSuccess); setLinkDropdownVisible(false); setLinkSelectionRange(null); }, [editor]); const handleLinkInputKeyDown = (0, _react.useCallback)((e, locale) => { if (e.key === 'Enter') { handleConfirmLink(locale); } }, [handleConfirmLink]); const handleImageAdd = (0, _react.useCallback)(() => { if (!editor) { return false; } try { editor.chain().focus().insertContent({ type: "imageUpload" }).run(); } catch (_a) { return false; } return true; }, [editor]); return /*#__PURE__*/_react.default.createElement("div", { className: className }, /*#__PURE__*/_react.default.createElement(ConfigureButton, { icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconUndo, null), onClick: () => editor.chain().focus().undo().run(), disabled: !editorState.canUndo }), /*#__PURE__*/_react.default.createElement(ConfigureButton, { icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconRedo, null), onClick: () => editor.chain().focus().redo().run(), disabled: !editorState.canRedo }), /*#__PURE__*/_react.default.createElement(_index.Divider, { layout: "vertical" }), /*#__PURE__*/_react.default.createElement(_index.Dropdown, { render: /*#__PURE__*/_react.default.createElement(_index.Dropdown.Menu, null, /*#__PURE__*/_react.default.createElement(ConfigureDropdownItem, { className: editorState.isHeading1 ? `${prefixCls}-menu-bar-dropdown-item-active` : '', onClick: () => editor.chain().focus().toggleHeading({ level: 1 }).run() }, /*#__PURE__*/_react.default.createElement(_semiIcons.IconH1, null)), /*#__PURE__*/_react.default.createElement(ConfigureDropdownItem, { className: editorState.isHeading2 ? `${prefixCls}-menu-bar-dropdown-item-active` : '', onClick: () => editor.chain().focus().toggleHeading({ level: 2 }).run() }, /*#__PURE__*/_react.default.createElement(_semiIcons.IconH2, null)), /*#__PURE__*/_react.default.createElement(ConfigureDropdownItem, { className: editorState.isHeading3 ? `${prefixCls}-menu-bar-dropdown-item-active` : '', onClick: () => editor.chain().focus().toggleHeading({ level: 3 }).run() }, /*#__PURE__*/_react.default.createElement(_semiIcons.IconH3, null)), /*#__PURE__*/_react.default.createElement(ConfigureDropdownItem, { className: editorState.isHeading4 ? `${prefixCls}-menu-bar-dropdown-item-active` : '', onClick: () => editor.chain().focus().toggleHeading({ level: 4 }).run() }, /*#__PURE__*/_react.default.createElement(_semiIcons.IconH4, null)), /*#__PURE__*/_react.default.createElement(ConfigureDropdownItem, { className: editorState.isHeading5 ? `${prefixCls}-menu-bar-dropdown-item-active` : '', onClick: () => editor.chain().focus().toggleHeading({ level: 5 }).run() }, /*#__PURE__*/_react.default.createElement(_semiIcons.IconH5, null)), /*#__PURE__*/_react.default.createElement(ConfigureDropdownItem, { className: editorState.isHeading6 ? `${prefixCls}-menu-bar-dropdown-item-active` : '', onClick: () => editor.chain().focus().toggleHeading({ level: 6 }).run() }, /*#__PURE__*/_react.default.createElement(_semiIcons.IconH6, null))) }, /*#__PURE__*/_react.default.createElement("span", null, /*#__PURE__*/_react.default.createElement(ConfigureButton, { icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconHn, null), active: editorState.isHeading }))), /*#__PURE__*/_react.default.createElement(ConfigureButton, { icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconText, null), onClick: () => editor.chain().focus().setParagraph().run(), active: editorState.isParagraph }), /*#__PURE__*/_react.default.createElement(ConfigureButton, { icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconList, null), onClick: () => editor.chain().focus().toggleBulletList().run(), active: editorState.isBulletList }), /*#__PURE__*/_react.default.createElement(ConfigureButton, { icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconOrderedList, null), onClick: () => editor.chain().focus().toggleOrderedList().run(), active: editorState.isOrderedList }), /*#__PURE__*/_react.default.createElement(ConfigureButton, { icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconQuote, null), active: editorState.isBlockquote, onClick: () => editor.chain().focus().setBlockquote().run() }), /*#__PURE__*/_react.default.createElement(ConfigureButton, { active: editorState.isCodeBlock, className: `${prefixCls}-menu-bar-btn-codeblock`, onClick: () => editor.chain().focus().toggleCodeBlock().run() }, "CB"), /*#__PURE__*/_react.default.createElement(ConfigureButton, { icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconMinus, null), onClick: () => editor.chain().focus().setHorizontalRule().run() }), /*#__PURE__*/_react.default.createElement(_index.Divider, { layout: "vertical" }), /*#__PURE__*/_react.default.createElement(ConfigureButton, { active: editorState.isAlignLeft, icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconAlignLeft, null), onClick: () => editor.chain().focus().setTextAlign('left').run() }), /*#__PURE__*/_react.default.createElement(ConfigureButton, { active: editorState.isAlignCenter, icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconAlignCenter, null), onClick: () => editor.chain().focus().setTextAlign('center').run() }), /*#__PURE__*/_react.default.createElement(ConfigureButton, { active: editorState.isAlignRight, icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconAlignRight, null), onClick: () => editor.chain().focus().setTextAlign('right').run() }), /*#__PURE__*/_react.default.createElement(ConfigureButton, { active: editorState.isAlignJustify, icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconAlignJustify, null), onClick: () => editor.chain().focus().setTextAlign('justify').run() }), /*#__PURE__*/_react.default.createElement(_index.Divider, { layout: "vertical" }), /*#__PURE__*/_react.default.createElement(ConfigureButton, { icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconBold, null), active: editorState.isBold, onClick: () => editor.chain().focus().toggleBold().run() }), /*#__PURE__*/_react.default.createElement(ConfigureButton, { icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconItalic, null), onClick: () => editor.chain().focus().toggleItalic().run(), active: editorState.isItalic, disabled: !editorState.canItalic }), /*#__PURE__*/_react.default.createElement(ConfigureButton, { icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconStrikeThrough, null), onClick: () => editor.chain().focus().toggleStrike().run(), active: editorState.isStrike, disabled: !editorState.canStrike }), /*#__PURE__*/_react.default.createElement(ConfigureButton, { icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconCode, null), onClick: () => editor.chain().focus().toggleCode().run(), active: editorState.isCode, disabled: !editorState.canCode }), /*#__PURE__*/_react.default.createElement(_index.Dropdown, { trigger: "click", visible: linkDropdownVisible, onVisibleChange: visible => { var _a; setLinkDropdownVisible(visible); if (visible) { const { from, to } = editor.state.selection; setLinkSelectionRange({ from, to }); if (from !== to) { editor.chain().focus().setMark('selectionMark').run(); } const currentHref = ((_a = editor.getAttributes('link')) === null || _a === void 0 ? void 0 : _a.href) || ''; setLinkInputValue(currentHref); } else { editor.chain().focus().unsetMark('selectionMark').run(); setLinkSelectionRange(null); } }, render: /*#__PURE__*/_react.default.createElement("div", { className: `${prefixCls}-menu-bar-link-dropdown` }, /*#__PURE__*/_react.default.createElement(_localeConsumer.default, { componentName: "Sidebar" }, locale => (/*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_index.Input, { size: "small", placeholder: locale.enterLinkAddress, value: linkInputValue, onChange: setLinkInputValue, onKeyDown: e => handleLinkInputKeyDown(e, locale), className: `${prefixCls}-menu-bar-link-input` }), /*#__PURE__*/_react.default.createElement(_index.Button, { size: "small", theme: "borderless", type: "tertiary", icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconCheckCircleStroked, null), onClick: e => handleConfirmLink(locale), disabled: !linkInputValue.trim() }), /*#__PURE__*/_react.default.createElement(_index.Button, { size: "small", theme: "borderless", icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconDeleteStroked, null), onClick: e => handleUnsetLink(locale), disabled: !editorState.isLink }))))) }, /*#__PURE__*/_react.default.createElement("span", null, /*#__PURE__*/_react.default.createElement(ConfigureButton, { icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconLink, null), active: editorState.isLink }))), /*#__PURE__*/_react.default.createElement(_index.Divider, { layout: "vertical" }), /*#__PURE__*/_react.default.createElement(ConfigureButton, { icon: /*#__PURE__*/_react.default.createElement(_semiIcons.IconImage, null), disabled: !editorState.canInsertImage, onClick: handleImageAdd })); } const FileItem = exports.FileItem = /*#__PURE__*/_react.default.memo(props => { const { editable = true, content, onContentChange, extensions = [], className, style, imgUploadProps } = props; const defaultExtensions = (0, _react.useMemo)(() => [_extensionTextStyle.TextStyleKit, _starterKit.default.configure({ link: { openOnClick: false, enableClickSelection: true } }), _extensionImage.Image, SelectionMark, _extensionTextAlign.TextAlign.configure({ types: ["heading", "paragraph"] }), _imageSlot.ImageUploadNode.configure(imgUploadProps)], [imgUploadProps]); const allExtensions = (0, _react.useMemo)(() => [...defaultExtensions, ...extensions], [defaultExtensions, extensions]); const editor = (0, _react2.useEditor)({ extensions: allExtensions, editable: editable, content: content, onUpdate: _ref2 => { let { editor } = _ref2; onContentChange === null || onContentChange === void 0 ? void 0 : onContentChange(editor.getHTML()); } }); if (!editor) { return null; } return /*#__PURE__*/_react.default.createElement("div", { className: (0, _classnames.default)(prefixCls, { [className]: className }), style: style }, editable && /*#__PURE__*/_react.default.createElement(MenuBar, { editor: editor, className: `${prefixCls}-menu-bar` }), /*#__PURE__*/_react.default.createElement(_react2.EditorContent, { editor: editor, className: `${prefixCls}-editor` })); }); const FileContent = /*#__PURE__*/_react.default.memo(props => { const { activeKey, files = [], onExpand, style, className, onChange } = props; return /*#__PURE__*/_react.default.createElement(_index.Collapse, { className: (0, _classnames.default)(collapseCls, `${collapseCls}-file`, { [className]: className }), style: style, onChange: onChange, activeKey: activeKey, clickHeaderToExpand: false }, files.map(file => { return /*#__PURE__*/_react.default.createElement(_index.Collapse.Panel, { header: /*#__PURE__*/_react.default.createElement(_code.CollapseHeader, { content: file, onExpand: onExpand, mode: 'file' }), itemKey: file.key, key: file.key }, /*#__PURE__*/_react.default.createElement(FileItem, { key: file.key, content: file.content, editable: false })); })); }); var _default = exports.default = FileContent;