@lobehub/editor
Version:
A powerful and extensible rich text editor built on Meta's Lexical framework, providing a modern editing experience with React integration.
378 lines (373 loc) • 18 kB
JavaScript
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
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; }
import { $computeTableMapSkipCellCheck, $getTableNodeFromLexicalNodeOrThrow, $getTableRowIndexFromTableCellNode, $isTableCellNode, $isTableRowNode, TableNode, getDOMCellFromTarget, getTableElement } from '@lexical/table';
import { calculateZoomLevel, mergeRegister } from '@lexical/utils';
import { cssVar, cx } from 'antd-style';
import { $getNearestNodeFromDOMNode, SKIP_SCROLL_INTO_VIEW_TAG, isHTMLElement } from 'lexical';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { MIN_COLUMN_WIDTH, MIN_ROW_HEIGHT, styles } from "./style";
import { getCellColumnIndex, getCellNodeHeight } from "./utils";
import { jsx as _jsx } from "react/jsx-runtime";
import { Fragment as _Fragment } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
export var TableCellResize = /*#__PURE__*/memo(function (_ref) {
var editor = _ref.editor,
eventEmitter = _ref.eventEmitter;
var targetRef = useRef(null);
var resizerRef = useRef(null);
// eslint-disable-next-line no-undef
var tableRectRef = useRef(null);
var _useState = useState(false),
_useState2 = _slicedToArray(_useState, 2),
hasTable = _useState2[0],
setHasTable = _useState2[1];
var pointerStartPosRef = useRef(null);
var _useState3 = useState(null),
_useState4 = _slicedToArray(_useState3, 2),
pointerCurrentPos = _useState4[0],
updatePointerCurrentPos = _useState4[1];
var _useState5 = useState(null),
_useState6 = _slicedToArray(_useState5, 2),
activeCell = _useState6[0],
updateActiveCell = _useState6[1];
var _useState7 = useState(null),
_useState8 = _slicedToArray(_useState7, 2),
draggingDirection = _useState8[0],
updateDraggingDirection = _useState8[1];
var resetState = useCallback(function () {
updateActiveCell(null);
targetRef.current = null;
updateDraggingDirection(null);
pointerStartPosRef.current = null;
tableRectRef.current = null;
}, []);
useEffect(function () {
var tableKeys = new Set();
return mergeRegister(editor.registerMutationListener(TableNode, function (nodeMutations) {
var _iterator = _createForOfIteratorHelper(nodeMutations),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var _step$value = _slicedToArray(_step.value, 2),
nodeKey = _step$value[0],
mutation = _step$value[1];
if (mutation === 'destroyed') {
tableKeys.delete(nodeKey);
} else {
tableKeys.add(nodeKey);
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
setHasTable(tableKeys.size > 0);
}), editor.registerNodeTransform(TableNode, function (tableNode) {
if (tableNode.getColWidths()) {
return tableNode;
}
var numColumns = tableNode.getColumnCount();
var columnWidth = MIN_COLUMN_WIDTH;
// eslint-disable-next-line unicorn/no-new-array
tableNode.setColWidths(new Array(numColumns).fill(columnWidth));
return tableNode;
}));
}, [editor]);
useEffect(function () {
if (!hasTable) {
return;
}
var onPointerMove = function onPointerMove(event) {
var target = event.target;
if (!isHTMLElement(target)) {
return;
}
if (draggingDirection) {
event.preventDefault();
event.stopPropagation();
updatePointerCurrentPos({
x: event.clientX,
y: event.clientY
});
return;
}
if (resizerRef.current && resizerRef.current.contains(target)) {
return;
}
if (targetRef.current !== target) {
targetRef.current = target;
var cell = getDOMCellFromTarget(target);
if (cell && activeCell !== cell) {
editor.getEditorState().read(function () {
var tableCellNode = $getNearestNodeFromDOMNode(cell.elem);
if (!tableCellNode) {
throw new Error('TableCellResizer: Table cell node not found.');
}
var tableNode = $getTableNodeFromLexicalNodeOrThrow(tableCellNode);
var tableElement = getTableElement(tableNode, editor.getElementByKey(tableNode.getKey()));
if (!tableElement) {
throw new Error('TableCellResizer: Table element not found.');
}
targetRef.current = target;
tableRectRef.current = tableElement.getBoundingClientRect();
updateActiveCell(cell);
}, {
editor: editor
});
} else if (!cell) {
resetState();
}
}
};
var onPointerDown = function onPointerDown(event) {
var isTouchEvent = event.pointerType === 'touch';
if (isTouchEvent) {
onPointerMove(event);
}
};
var resizerContainer = resizerRef.current;
resizerContainer === null || resizerContainer === void 0 || resizerContainer.addEventListener('pointermove', onPointerMove, {
capture: true
});
var removeRootListener = editor.registerRootListener(function (rootElement, prevRootElement) {
prevRootElement === null || prevRootElement === void 0 || prevRootElement.removeEventListener('pointermove', onPointerMove);
prevRootElement === null || prevRootElement === void 0 || prevRootElement.removeEventListener('pointerdown', onPointerDown);
rootElement === null || rootElement === void 0 || rootElement.addEventListener('pointermove', onPointerMove);
rootElement === null || rootElement === void 0 || rootElement.addEventListener('pointerdown', onPointerDown);
});
return function () {
removeRootListener();
resizerContainer === null || resizerContainer === void 0 || resizerContainer.removeEventListener('pointermove', onPointerMove);
};
}, [activeCell, draggingDirection, editor, resetState, hasTable]);
var isHeightChanging = function isHeightChanging(direction) {
if (direction === 'bottom') {
return true;
}
return false;
};
var updateRowHeight = useCallback(function (heightChange) {
if (!activeCell) {
throw new Error('TableCellResizer: Expected active cell.');
}
editor.update(function () {
var tableCellNode = $getNearestNodeFromDOMNode(activeCell.elem);
if (!$isTableCellNode(tableCellNode)) {
throw new Error('TableCellResizer: Table cell node not found.');
}
var tableNode = $getTableNodeFromLexicalNodeOrThrow(tableCellNode);
var baseRowIndex = $getTableRowIndexFromTableCellNode(tableCellNode);
var tableRows = tableNode.getChildren();
// Determine if this is a full row merge by checking colspan
var isFullRowMerge = tableCellNode.getColSpan() === tableNode.getColumnCount();
// For full row merges, apply to first row. For partial merges, apply to last row
var tableRowIndex = isFullRowMerge ? baseRowIndex : baseRowIndex + tableCellNode.getRowSpan() - 1;
if (tableRowIndex >= tableRows.length || tableRowIndex < 0) {
throw new Error('Expected table cell to be inside of table row.');
}
var tableRow = tableRows[tableRowIndex];
if (!$isTableRowNode(tableRow)) {
throw new Error('Expected table row');
}
var height = tableRow.getHeight();
if (height === undefined) {
var rowCells = tableRow.getChildren();
height = Math.min.apply(Math, _toConsumableArray(rowCells.map(
// eslint-disable-next-line @typescript-eslint/no-use-before-define
function (cell) {
var _getCellNodeHeight;
return (_getCellNodeHeight = getCellNodeHeight(cell, editor)) !== null && _getCellNodeHeight !== void 0 ? _getCellNodeHeight : Infinity;
})));
}
var newHeight = Math.max(height + heightChange, MIN_ROW_HEIGHT);
tableRow.setHeight(newHeight);
eventEmitter.emit('table:resize', {
heightChange: heightChange,
newHeight: newHeight
});
}, {
tag: SKIP_SCROLL_INTO_VIEW_TAG
});
}, [activeCell, editor]);
var updateColumnWidth = useCallback(function (widthChange) {
if (!activeCell) {
throw new Error('TableCellResizer: Expected active cell.');
}
editor.update(function () {
var tableCellNode = $getNearestNodeFromDOMNode(activeCell.elem);
if (!$isTableCellNode(tableCellNode)) {
throw new Error('TableCellResizer: Table cell node not found.');
}
var tableNode = $getTableNodeFromLexicalNodeOrThrow(tableCellNode);
var _$computeTableMapSkip = $computeTableMapSkipCellCheck(tableNode, null, null),
_$computeTableMapSkip2 = _slicedToArray(_$computeTableMapSkip, 1),
tableMap = _$computeTableMapSkip2[0];
var columnIndex = getCellColumnIndex(tableCellNode, tableMap);
if (columnIndex === undefined) {
throw new Error('TableCellResizer: Table column not found.');
}
var colWidths = tableNode.getColWidths();
if (!colWidths) {
return;
}
var width = colWidths[columnIndex];
if (width === undefined) {
return;
}
var newColWidths = _toConsumableArray(colWidths);
var newWidth = Math.max(width + widthChange, MIN_COLUMN_WIDTH);
newColWidths[columnIndex] = newWidth;
tableNode.setColWidths(newColWidths);
requestAnimationFrame(function () {
eventEmitter.emit('table:resize', {
newColWidths: newColWidths
});
});
}, {
tag: SKIP_SCROLL_INTO_VIEW_TAG
});
}, [activeCell, editor]);
var pointerUpHandler = useCallback(function (direction) {
var handler = function handler(event) {
event.preventDefault();
event.stopPropagation();
if (!activeCell) {
throw new Error('TableCellResizer: Expected active cell.');
}
if (pointerStartPosRef.current) {
var _pointerStartPosRef$c = pointerStartPosRef.current,
x = _pointerStartPosRef$c.x,
y = _pointerStartPosRef$c.y;
if (activeCell === null) {
return;
}
var zoom = calculateZoomLevel(event.target);
if (isHeightChanging(direction)) {
var heightChange = (event.clientY - y) / zoom;
updateRowHeight(heightChange);
} else {
var widthChange = (event.clientX - x) / zoom;
updateColumnWidth(widthChange);
}
resetState();
if (typeof document !== 'undefined') {
document.removeEventListener('pointerup', handler);
}
}
};
return handler;
}, [activeCell, resetState, updateColumnWidth, updateRowHeight]);
var toggleResize = useCallback(function (direction) {
return function (event) {
event.preventDefault();
event.stopPropagation();
if (!activeCell) {
throw new Error('TableCellResizer: Expected active cell.');
}
pointerStartPosRef.current = {
x: event.clientX,
y: event.clientY
};
updatePointerCurrentPos(pointerStartPosRef.current);
updateDraggingDirection(direction);
if (typeof document !== 'undefined') {
document.addEventListener('pointerup', pointerUpHandler(direction));
}
};
}, [activeCell, pointerUpHandler]);
var getResizers = useCallback(function () {
if (activeCell) {
var _activeCell$elem$getB = activeCell.elem.getBoundingClientRect(),
height = _activeCell$elem$getB.height,
width = _activeCell$elem$getB.width,
top = _activeCell$elem$getB.top,
left = _activeCell$elem$getB.left;
var zoom = calculateZoomLevel(activeCell.elem);
var zoneWidth = 16; // Pixel width of the zone where you can drag the edge
// Default to 0 for server side
var scrollX = typeof window !== 'undefined' ? window.scrollX : 0;
var scrollY = typeof window !== 'undefined' ? window.scrollY : 0;
var _styles = {
bottom: {
backgroundColor: 'none',
cursor: 'row-resize',
height: "".concat(zoneWidth, "px"),
left: "".concat(scrollX + left, "px"),
top: "".concat(scrollY + top + height - zoneWidth / 2, "px"),
width: "".concat(width, "px")
},
right: {
backgroundColor: 'none',
cursor: 'col-resize',
height: "".concat(height, "px"),
left: "".concat(scrollX + left + width - zoneWidth / 2, "px"),
top: "".concat(scrollY + top, "px"),
width: "".concat(zoneWidth, "px")
}
};
var tableRect = tableRectRef.current;
if (draggingDirection && pointerCurrentPos && tableRect) {
if (isHeightChanging(draggingDirection)) {
_styles[draggingDirection].left = "".concat(scrollX + tableRect.left, "px");
_styles[draggingDirection].top = "".concat(scrollY + pointerCurrentPos.y / zoom, "px");
_styles[draggingDirection].height = '3px';
_styles[draggingDirection].width = "".concat(tableRect.width, "px");
} else {
_styles[draggingDirection].top = "".concat(scrollY + tableRect.top, "px");
_styles[draggingDirection].left = "".concat(scrollX + pointerCurrentPos.x / zoom, "px");
_styles[draggingDirection].width = '3px';
_styles[draggingDirection].height = "".concat(tableRect.height, "px");
}
_styles[draggingDirection].backgroundColor = cssVar.colorPrimary;
_styles[draggingDirection].mixBlendMode = 'unset';
}
return _styles;
}
return {
bottom: null,
left: null,
right: null,
top: null
};
}, [activeCell, draggingDirection, pointerCurrentPos]);
var resizerStyles = getResizers();
return /*#__PURE__*/_jsx("div", {
ref: resizerRef,
children: activeCell && /*#__PURE__*/_jsxs(_Fragment, {
children: [/*#__PURE__*/_jsx("div", {
className: cx(styles, 'TableCellResizer__resizer', 'TableCellResizer__ui'),
onPointerDown: toggleResize('right'),
style: resizerStyles.right || undefined
}), /*#__PURE__*/_jsx("div", {
className: cx(styles, 'TableCellResizer__resizer', 'TableCellResizer__ui'),
onPointerDown: toggleResize('bottom'),
style: resizerStyles.bottom || undefined
})]
})
});
});
export default /*#__PURE__*/memo(function (_ref2) {
var editor = _ref2.editor,
eventEmitter = _ref2.eventEmitter;
// Don't render portal on server side
if (typeof document === 'undefined') {
return null;
}
// Mount to .ant-app if exists, otherwise document.body
var container = document.querySelector('.ant-app') || document.body;
return /*#__PURE__*/createPortal( /*#__PURE__*/_jsx(TableCellResize, {
editor: editor,
eventEmitter: eventEmitter
}), container);
});