UNPKG

@lobehub/editor

Version:

A powerful and extensible rich text editor built on Meta's Lexical framework, providing a modern editing experience with React integration.

177 lines (173 loc) 8.79 kB
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * */ import { $getTableCellNodeFromLexicalNode, $getTableNodeFromLexicalNodeOrThrow, $isTableCellNode, $isTableSelection, getTableElement, getTableObserverFromTableElement } from '@lexical/table'; import { mergeRegister } from '@lexical/utils'; import { ActionIcon } from '@lobehub/ui'; import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_CRITICAL, SELECTION_CHANGE_COMMAND, getDOMSelection } from 'lexical'; import { ChevronDown } from 'lucide-react'; import { memo, useCallback, useEffect, useRef, useState } from 'react'; import { useAnchor } from "../../../../editor-kernel/react/useAnchor"; import { cleanPosition, updatePosition } from "../../../../utils/updatePosition"; import ActionMenu from "./ActionMenu"; import { styles } from "./style"; import { jsx as _jsx } from "react/jsx-runtime"; var TableActionMenu = /*#__PURE__*/memo(function (_ref) { var cellMerge = _ref.cellMerge, editor = _ref.editor; var anchorElem = useAnchor(); var menuButtonRef = useRef(null); var _useState = useState(null), _useState2 = _slicedToArray(_useState, 2), tableCellNode = _useState2[0], setTableMenuCellNode = _useState2[1]; // const [colorPickerModal, showColorPickerModal] = useModal(); var checkTableCellOverflow = useCallback(function (tableCellParentNodeDOM) { var scrollableContainer = tableCellParentNodeDOM.closest('.PlaygroundEditorTheme__tableScrollableWrapper'); if (scrollableContainer) { var containerRect = scrollableContainer.getBoundingClientRect(); var cellRect = tableCellParentNodeDOM.getBoundingClientRect(); // Calculate where the action button would be positioned (5px from right edge of cell) // Also account for the button width and table cell padding (8px) var actionButtonRight = cellRect.right - 5; var actionButtonLeft = actionButtonRight - 28; // 20px width + 8px padding // Only hide if the action button would overflow the container if (actionButtonRight > containerRect.right || actionButtonLeft < containerRect.left) { return true; } } return false; }, []); var $moveMenu = useCallback(function () { var menu = menuButtonRef.current; var selection = $getSelection(); var nativeSelection = getDOMSelection(editor._window); var activeElement = typeof document !== 'undefined' ? document.activeElement : null; function disable() { if (menu) { menu.classList.remove('table-cell-action-button-container--active'); menu.classList.add('table-cell-action-button-container--inactive'); } setTableMenuCellNode(null); } if (!selection || !menu) { return disable(); } var rootElement = editor.getRootElement(); var tableObserver = null; var tableCellParentNodeDOM = null; if ($isRangeSelection(selection) && rootElement !== null && nativeSelection !== null && rootElement.contains(nativeSelection.anchorNode)) { var tableCellNodeFromSelection = $getTableCellNodeFromLexicalNode(selection.anchor.getNode()); if (!tableCellNodeFromSelection) { return disable(); } tableCellParentNodeDOM = editor.getElementByKey(tableCellNodeFromSelection.getKey()); if (!tableCellParentNodeDOM || !tableCellNodeFromSelection.isAttached()) { return disable(); } if (checkTableCellOverflow(tableCellParentNodeDOM)) { return disable(); } var tableNode = $getTableNodeFromLexicalNodeOrThrow(tableCellNodeFromSelection); var tableElement = getTableElement(tableNode, editor.getElementByKey(tableNode.getKey())); if (tableElement === null) { throw new Error('TableActionMenu: Expected to find tableElement in DOM'); } tableObserver = getTableObserverFromTableElement(tableElement); setTableMenuCellNode(tableCellNodeFromSelection); } else if ($isTableSelection(selection)) { var anchorNode = $getTableCellNodeFromLexicalNode(selection.anchor.getNode()); if (!$isTableCellNode(anchorNode)) { throw new Error('TableSelection anchorNode must be a TableCellNode'); } var _tableNode = $getTableNodeFromLexicalNodeOrThrow(anchorNode); var _tableElement = getTableElement(_tableNode, editor.getElementByKey(_tableNode.getKey())); if (_tableElement === null) { throw new Error('TableActionMenu: Expected to find tableElement in DOM'); } tableObserver = getTableObserverFromTableElement(_tableElement); tableCellParentNodeDOM = editor.getElementByKey(anchorNode.getKey()); if (tableCellParentNodeDOM === null) { return disable(); } if (checkTableCellOverflow(tableCellParentNodeDOM)) { return disable(); } } else if (!activeElement) { return disable(); } if (tableObserver === null || tableCellParentNodeDOM === null) { return disable(); } var enabled = !tableObserver || !tableObserver.isSelecting; menu.classList.toggle('table-cell-action-button-container--active', enabled); menu.classList.toggle('table-cell-action-button-container--inactive', !enabled); if (enabled) { updatePosition({ floating: menu, offset: 0, placement: 'top-end', reference: tableCellParentNodeDOM }); } else { cleanPosition(menu); } }, [editor, anchorElem, checkTableCellOverflow]); useEffect(function () { // We call the $moveMenu callback every time the selection changes, // once up front, and once after each pointerUp var timeoutId = undefined; var callback = function callback() { timeoutId = undefined; editor.getEditorState().read($moveMenu); }; var delayedCallback = function delayedCallback() { if (timeoutId === undefined) { timeoutId = setTimeout(callback, 0); } return false; }; return mergeRegister(editor.registerUpdateListener(delayedCallback), editor.registerCommand(SELECTION_CHANGE_COMMAND, delayedCallback, COMMAND_PRIORITY_CRITICAL), editor.registerRootListener(function (rootElement, prevRootElement) { if (prevRootElement) { prevRootElement.removeEventListener('pointerup', delayedCallback); } if (rootElement) { rootElement.addEventListener('pointerup', delayedCallback); delayedCallback(); } }), function () { return clearTimeout(timeoutId); }); }); var prevTableCellDOM = useRef(tableCellNode); useEffect(function () { prevTableCellDOM.current = tableCellNode; }, [prevTableCellDOM, tableCellNode]); return /*#__PURE__*/_jsx("div", { className: styles.root, ref: menuButtonRef, children: tableCellNode && /*#__PURE__*/_jsx(ActionMenu, { cellMerge: cellMerge, editor: editor, tableCellNode: tableCellNode, children: /*#__PURE__*/_jsx(ActionIcon, { className: styles.actionIcon, glass: true, icon: ChevronDown, size: 12, variant: 'filled' }) }) }); }); export default TableActionMenu;