@seafile/sdoc-editor 0.4.21 → 0.4.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/basic-sdk/constants/index.js +2 -1
  2. package/dist/basic-sdk/extension/plugins/callout/helper.js +1 -10
  3. package/dist/basic-sdk/extension/plugins/callout/render-elem/color-selector.js +18 -21
  4. package/dist/basic-sdk/extension/plugins/callout/render-elem/index.js +33 -32
  5. package/dist/basic-sdk/extension/plugins/image/plugin.js +1 -1
  6. package/dist/basic-sdk/extension/plugins/search-replace/menu/index.js +4 -2
  7. package/dist/basic-sdk/extension/plugins/search-replace/popover/index.css +7 -0
  8. package/dist/basic-sdk/extension/plugins/search-replace/popover/index.js +16 -3
  9. package/dist/basic-sdk/extension/plugins/table/constants/index.js +8 -1
  10. package/dist/basic-sdk/extension/plugins/table/helpers.js +87 -86
  11. package/dist/basic-sdk/extension/plugins/table/render/index.css +47 -1
  12. package/dist/basic-sdk/extension/plugins/table/render/index.js +27 -3
  13. package/dist/basic-sdk/extension/plugins/table/render/render-cell.js +17 -5
  14. package/dist/basic-sdk/extension/plugins/table/render/resize-handlers/column-resize-handler.js +25 -45
  15. package/dist/basic-sdk/extension/plugins/table/render/resize-handlers/first-column-left-resize-handler.js +21 -41
  16. package/dist/basic-sdk/extension/plugins/table/render/resize-handlers/index.js +44 -35
  17. package/dist/basic-sdk/extension/plugins/table/render/resize-handlers/row-resize-handler.js +22 -32
  18. package/dist/basic-sdk/extension/plugins/table/render/resize-mask/index.js +133 -0
  19. package/dist/basic-sdk/extension/plugins/table/render/table-header/rows-header/row-header.js +9 -15
  20. package/package.json +1 -1
  21. package/public/locales/zh_CN/sdoc-editor.json +1 -1
  22. package/public/media/sdoc-editor-font/iconfont.eot +0 -0
  23. package/public/media/sdoc-editor-font/iconfont.svg +16 -2
  24. package/public/media/sdoc-editor-font/iconfont.ttf +0 -0
  25. package/public/media/sdoc-editor-font/iconfont.woff +0 -0
  26. package/public/media/sdoc-editor-font/iconfont.woff2 +0 -0
  27. package/public/media/sdoc-editor-font.css +38 -10
@@ -16,7 +16,8 @@ export const INTERNAL_EVENT = {
16
16
  UNSEEN_NOTIFICATIONS_COUNT: 'unseen_notifications_count',
17
17
  CLOSE_CALLOUT_COLOR_PICKER: 'close_callout_color_picker',
18
18
  OPEN_SEARCH_REPLACE_MODAL: 'open_search_replace_modal',
19
- UPDATE_SEARCH_REPLACE_HIGHLIGHT: 'update_search_replace_highlight'
19
+ UPDATE_SEARCH_REPLACE_HIGHLIGHT: 'update_search_replace_highlight',
20
+ TABLE_CELL_MOUSE_ENTER: 'table_cell_mouse_enter'
20
21
  };
21
22
  export const REVISION_DIFF_KEY = 'diff';
22
23
  export const REVISION_DIFF_VALUE = '1';
@@ -1,6 +1,6 @@
1
1
  import { Editor, Node, Path, Transforms } from '@seafile/slate';
2
2
  import { CALL_OUT } from '../../constants/element-type';
3
- import { findPath, focusEditor, generateEmptyElement, getSelectedElems, isRangeAcrossBlocks } from '../../core';
3
+ import { focusEditor, generateEmptyElement, getSelectedElems, isRangeAcrossBlocks } from '../../core';
4
4
  import { CALLOUT_ALLOWED_INSIDE_TYPES, CALLOUT_COLOR_MAP } from './constant';
5
5
  export const isMenuActive = editor => {
6
6
  const {
@@ -91,15 +91,6 @@ export const getCalloutEntry = function (editor) {
91
91
  });
92
92
  return aboveNodeEntry;
93
93
  };
94
-
95
- // Use in render-elem, to judge whether to display placeholder
96
- export const isFocusOnElement = (editor, element) => {
97
- var _editor$selection, _editor$selection$anc;
98
- if (!editor.selection || !element) return false;
99
- const elementPath = findPath(editor, element);
100
- const isSelectOnElement = Path.isAncestor(elementPath, editor === null || editor === void 0 ? void 0 : (_editor$selection = editor.selection) === null || _editor$selection === void 0 ? void 0 : (_editor$selection$anc = _editor$selection.anchor) === null || _editor$selection$anc === void 0 ? void 0 : _editor$selection$anc.path);
101
- return isSelectOnElement;
102
- };
103
94
  export const isCalloutContentEmpty = calloutEntry => {
104
95
  const [calloutNode] = calloutEntry;
105
96
  const contentString = Node.string(calloutNode);
@@ -1,39 +1,34 @@
1
- import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
1
+ import React, { useCallback } from 'react';
2
2
  import { Transforms } from '@seafile/slate';
3
3
  import { CALLOUT_COLOR_MAP } from '../constant';
4
4
  import { findPath } from '../../../core';
5
5
  import { changeFillBackgroundColor } from '../helper';
6
6
  import { ElementPopover } from '../../../commons';
7
7
  import './index.css';
8
- const ColorSelector = (_ref, ref) => {
8
+ const ColorSelector = _ref => {
9
9
  let {
10
10
  editor,
11
11
  element,
12
12
  popoverPosition
13
13
  } = _ref;
14
- const selectorRef = useRef(null);
15
- const handleClickColor = useCallback((editor, element, background_color) => {
14
+ const onColorClick = useCallback(event => {
15
+ const {
16
+ backgroundColor
17
+ } = event.target.dataset;
16
18
  const currentPath = findPath(editor, element);
17
19
  Transforms.select(editor, currentPath);
18
- changeFillBackgroundColor(editor, background_color);
19
- }, []);
20
- useImperativeHandle(ref, () => ({
21
- colorSelectorContainer: selectorRef.current
22
- }));
23
- const isShowCheckedIcon = useCallback((element, currentBackgroundColor) => {
20
+ changeFillBackgroundColor(editor, backgroundColor);
21
+ }, [editor, element]);
22
+ const isShowCheckedIcon = useCallback(currentBackgroundColor => {
24
23
  const {
25
24
  background_color
26
- } = element.style;
25
+ } = element.style || {};
27
26
  return background_color && background_color === currentBackgroundColor;
28
- }, []);
27
+ }, [element.style]);
29
28
  return /*#__PURE__*/React.createElement(ElementPopover, null, /*#__PURE__*/React.createElement("div", {
30
29
  className: "sdoc-callout-color-selector-container",
31
- ref: selectorRef,
32
30
  contentEditable: false,
33
- style: {
34
- top: popoverPosition.top,
35
- left: popoverPosition.left
36
- }
31
+ style: popoverPosition
37
32
  }, /*#__PURE__*/React.createElement("ul", {
38
33
  className: "sdoc-color-selector-list"
39
34
  }, Object.values(CALLOUT_COLOR_MAP).map((_ref2, index) => {
@@ -44,14 +39,16 @@ const ColorSelector = (_ref, ref) => {
44
39
  return /*#__PURE__*/React.createElement("li", {
45
40
  key: "sdoc-callout-color-selector-".concat(index),
46
41
  className: "sdoc-callout-color-item",
47
- onClick: () => handleClickColor(editor, element, Object.keys(CALLOUT_COLOR_MAP)[index]),
42
+ "data-border-color": border_color,
43
+ "data-background-color": background_color,
48
44
  style: {
49
45
  borderColor: border_color,
50
46
  backgroundColor: background_color
51
- }
52
- }, isShowCheckedIcon(element, background_color) && /*#__PURE__*/React.createElement("i", {
47
+ },
48
+ onClick: onColorClick
49
+ }, isShowCheckedIcon(background_color) && /*#__PURE__*/React.createElement("i", {
53
50
  className: "sdoc-callout-color-checked-icon sdocfont sdoc-check-mark"
54
51
  }));
55
52
  }))));
56
53
  };
57
- export default forwardRef(ColorSelector);
54
+ export default ColorSelector;
@@ -1,12 +1,10 @@
1
1
  /* eslint-disable react-hooks/rules-of-hooks */
2
- import React, { useCallback, useEffect, useRef, useState } from 'react';
3
- import { useReadOnly } from '@seafile/slate-react';
2
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { Node } from '@seafile/slate';
4
+ import { useReadOnly, useSelected } from '@seafile/slate-react';
4
5
  import { useTranslation } from 'react-i18next';
5
6
  import ColorSelector from './color-selector';
6
7
  import { CALLOUT_COLOR_MAP } from '../constant';
7
- import { isFocusOnElement } from '../helper';
8
- import { Editor } from '@seafile/slate';
9
- import { findPath } from '../../../core';
10
8
  import { INTERNAL_EVENT } from '../../../../constants';
11
9
  import EventBus from '../../../../utils/event-bus';
12
10
  import { useScrollContext } from '../../../../hooks/use-scroll-context';
@@ -17,24 +15,40 @@ const renderCallout = (_ref, editor) => {
17
15
  children,
18
16
  element
19
17
  } = _ref;
20
- const [isShowColorSelector, setIsShowColorSelector] = useState(false);
21
- const [popoverPosition, setPopoverPosition] = useState({
22
- top: '',
23
- left: ''
24
- });
25
18
  const readOnly = useReadOnly();
26
19
  const scrollRef = useScrollContext();
27
- const calloutRef = useRef();
28
- const colorSelectorRef = useRef({
29
- colorSelectorContainer: null
30
- });
20
+ const isSelected = useSelected();
31
21
  const {
32
22
  t
33
23
  } = useTranslation();
34
- const {
35
- background_color
36
- } = element.style;
37
- const isFocusOnCallout = isFocusOnElement(editor, element);
24
+ const calloutRef = useRef();
25
+ const [isShowColorSelector, setIsShowColorSelector] = useState(false);
26
+ const [popoverPosition, setPopoverPosition] = useState({
27
+ top: '',
28
+ left: ''
29
+ });
30
+ const containerStyle = useMemo(() => {
31
+ const {
32
+ background_color = 'transparent'
33
+ } = element.style;
34
+ let borderColor = 'transparent';
35
+ if (isSelected) {
36
+ var _CALLOUT_COLOR_MAP$ba;
37
+ borderColor = (_CALLOUT_COLOR_MAP$ba = CALLOUT_COLOR_MAP[background_color]) === null || _CALLOUT_COLOR_MAP$ba === void 0 ? void 0 : _CALLOUT_COLOR_MAP$ba.border_color;
38
+ }
39
+ return {
40
+ backgroundColor: background_color,
41
+ borderColor
42
+ };
43
+ }, [element.style, isSelected]);
44
+ const isShowPlaceholder = useCallback(() => {
45
+ if (isSelected) return false;
46
+ // If element contains more than one element or element is not paragraph, show placeholder
47
+ const isContainUnitElement = element.children.length !== 1 || element.children.some(childElement => childElement.type !== 'paragraph');
48
+ if (isContainUnitElement) return false;
49
+ const elementContent = Node.string(element);
50
+ return !elementContent.length;
51
+ }, [element, isSelected]);
38
52
  const handleCloseColorSelector = useCallback(() => {
39
53
  setIsShowColorSelector(false);
40
54
  }, []);
@@ -87,15 +101,6 @@ const renderCallout = (_ref, editor) => {
87
101
  const handleClick = useCallback(e => {
88
102
  handleDisplayColorSelector();
89
103
  }, [handleDisplayColorSelector]);
90
- const isShowPlaceholder = useCallback(() => {
91
- if (isFocusOnCallout) return false;
92
- // If element contains more than one element or element is not paragraph, show placeholder
93
- const isContainUnitElement = element.children.length !== 1 || element.children.some(childElement => childElement.type !== 'paragraph');
94
- if (isContainUnitElement) return false;
95
- const elementPath = findPath(editor, element);
96
- const elementContent = Editor.string(editor, elementPath);
97
- return !elementContent.length;
98
- }, [editor, element, isFocusOnCallout]);
99
104
  return /*#__PURE__*/React.createElement("div", Object.assign({}, attributes, {
100
105
  "data-id": element.id,
101
106
  className: "sdoc-callout-white-wrapper",
@@ -104,15 +109,11 @@ const renderCallout = (_ref, editor) => {
104
109
  onClick: handleClick,
105
110
  ref: calloutRef,
106
111
  className: "".concat(attributes.className, " sdoc-callout-container"),
107
- style: {
108
- backgroundColor: background_color ? CALLOUT_COLOR_MAP[background_color].background_color : 'transparent',
109
- borderColor: isFocusOnCallout ? CALLOUT_COLOR_MAP[background_color].border_color : 'transparent'
110
- }
112
+ style: containerStyle
111
113
  }, children, isShowPlaceholder() && /*#__PURE__*/React.createElement("div", {
112
114
  contentEditable: false,
113
115
  className: "sdoc-callout-placeholder"
114
116
  }, t('Please_enter...')), isShowColorSelector && /*#__PURE__*/React.createElement(ColorSelector, {
115
- ref: colorSelectorRef,
116
117
  editor: editor,
117
118
  element: element,
118
119
  popoverPosition: popoverPosition
@@ -112,7 +112,7 @@ const withImage = editor => {
112
112
  } = editor;
113
113
  const focusPoint = Editor.before(editor, selection);
114
114
  const point = Editor.before(editor, selection, {
115
- distance: 2
115
+ distance: 1
116
116
  });
117
117
  if (!point) return;
118
118
  const [node, path] = Editor.node(editor, [point.path[0], point.path[1]]);
@@ -12,7 +12,8 @@ const SearchReplaceMenu = _ref => {
12
12
  let {
13
13
  isRichEditor,
14
14
  className,
15
- editor
15
+ editor,
16
+ readonly
16
17
  } = _ref;
17
18
  const [isOpenPopover, setIsOpenPopover] = useState(false);
18
19
  useEffect(() => {
@@ -38,7 +39,7 @@ const SearchReplaceMenu = _ref => {
38
39
  clientHeight
39
40
  };
40
41
  // eslint-disable-next-line react-hooks/exhaustive-deps
41
- }, [isOpenPopover, editor.children]);
42
+ }, [isOpenPopover]);
42
43
  const renderCanvasses = useMemo(() => {
43
44
  if (!isOpenPopover) return false;
44
45
  const generateCount = Math.ceil(articleContainerSize.offsetHeight / 5000);
@@ -66,6 +67,7 @@ const SearchReplaceMenu = _ref => {
66
67
  onMouseDown: onMouseDown
67
68
  }, menuConfig)), isOpenPopover && /*#__PURE__*/React.createElement(SearchReplacePopover, {
68
69
  editor: editor,
70
+ readonly: readonly,
69
71
  isOpen: isOpenPopover,
70
72
  closePopover: onMouseDown
71
73
  }), isOpenPopover && createPortal( /*#__PURE__*/React.createElement("div", {
@@ -52,6 +52,13 @@
52
52
  justify-content: space-between;
53
53
  }
54
54
 
55
+ .sdoc-search-replace-popover-body .sdoc-search-replace-popover-btn-group .btn {
56
+ padding: 0;
57
+ width: 23%;
58
+ min-height: 35px;
59
+ border-radius: 5px;
60
+ }
61
+
55
62
  .sdoc-replace-all-confirm-modal {
56
63
  position: absolute;
57
64
  top: 0;
@@ -8,11 +8,13 @@ import { handleReplaceKeyword, getHighlightInfos, drawHighlights } from '../help
8
8
  import ReplaceAllConfirmModal from './replace-all-confirm-modal';
9
9
  import EventBus from '../../../../utils/event-bus';
10
10
  import { INTERNAL_EVENT } from '../../../../constants';
11
+ import context from '../../../../../context';
11
12
  import './index.css';
12
13
  const SearchReplacePopover = _ref => {
13
14
  let {
14
15
  editor,
15
- closePopover
16
+ closePopover,
17
+ readonly
16
18
  } = _ref;
17
19
  const [searchContent, setSearchContent] = useState('');
18
20
  const [replacementContent, setReplacementContent] = useState('');
@@ -44,6 +46,17 @@ const SearchReplacePopover = _ref => {
44
46
  y: 95
45
47
  });
46
48
  }, []);
49
+ const isOwnReplacePermission = useMemo(() => {
50
+ if (readonly) return false;
51
+ const isFreezed = context.getSetting('isFreezed');
52
+ if (isFreezed) return false;
53
+ const isPublished = context.getSetting('isPublished');
54
+ const isSdocRevision = context.getSetting('isSdocRevision');
55
+ if (isSdocRevision && isPublished) return false;
56
+ const docPerm = context.getSetting('docPerm');
57
+ if (docPerm === 'rw') return true;
58
+ return false;
59
+ }, [readonly]);
47
60
  const handleDrawHighlight = useCallback((editor, keyword) => {
48
61
  const newHighlightInfos = getHighlightInfos(editor, keyword);
49
62
  setHighlightInfos(newHighlightInfos);
@@ -193,11 +206,11 @@ const SearchReplacePopover = _ref => {
193
206
  onClick: handleNext,
194
207
  className: "btn btn-secondary"
195
208
  }, t('Next')), /*#__PURE__*/React.createElement("button", {
196
- disabled: !highlightInfos.length,
209
+ disabled: !highlightInfos.length || !isOwnReplacePermission,
197
210
  onClick: handleReplace,
198
211
  className: "btn btn-primary"
199
212
  }, t('Replace')), /*#__PURE__*/React.createElement("button", {
200
- disabled: !highlightInfos.length,
213
+ disabled: !highlightInfos.length || !isOwnReplacePermission,
201
214
  onClick: handleOpenReplaceAllModal,
202
215
  className: "btn btn-primary"
203
216
  }, t('Replace_all'))))), /*#__PURE__*/React.createElement(ReplaceAllConfirmModal, {
@@ -38,4 +38,11 @@ export const TABLE_ALTERNATE_HIGHLIGHT_CLASS_MAP = {
38
38
  'sdoc-table-header-0099f4': 'sdoc-table-body-0099f4'
39
39
  };
40
40
  export const INHERIT_CELL_STYLE_WHEN_SELECT_SINGLE = ['background_color'];
41
- export const INHERIT_CELL_STYLE_WHEN_SELECT_MULTIPLE = ['background_color', 'text_align'];
41
+ export const INHERIT_CELL_STYLE_WHEN_SELECT_MULTIPLE = ['background_color', 'text_align'];
42
+ export const RESIZE_MASK_TOP = 'top';
43
+ export const RESIZE_MASK_RIGHT = 'right';
44
+ export const RESIZE_MASK_BOTTOM = 'bottom';
45
+ export const RESIZE_MASK_LEFT = 'left';
46
+ export const RESIZE_HANDLER_ROW = 'row';
47
+ export const RESIZE_HANDLER_COLUMN = 'column';
48
+ export const RESIZE_HANDLER_FIRST_COLUMN = 'first_column';
@@ -1249,6 +1249,25 @@ export const getRowHeight = (element, rowIndex) => {
1249
1249
  const rowHeight = style[TABLE_ROW_STYLE.MIN_HEIGHT] || TABLE_ROW_MIN_HEIGHT;
1250
1250
  return rowIndex === 0 ? rowHeight + 1 : rowHeight;
1251
1251
  };
1252
+ export const getRowDomHeight = (editor, row) => {
1253
+ let height = 0;
1254
+ for (const cell of row.children) {
1255
+ const {
1256
+ is_combined,
1257
+ rowspan = 1
1258
+ } = cell;
1259
+ if (is_combined || rowspan > 1) continue;
1260
+ const cellDom = ReactEditor.toDOMNode(editor, cell);
1261
+ if (!cellDom) break;
1262
+ height = cellDom.getBoundingClientRect().height;
1263
+ break;
1264
+ }
1265
+ // if the row is empty, get the height from style
1266
+ if (!height) {
1267
+ height = row.style[TABLE_ROW_STYLE.MIN_HEIGHT] || TABLE_ROW_MIN_HEIGHT;
1268
+ }
1269
+ return height;
1270
+ };
1252
1271
  const normalizeTableCell = (editor, cell) => {
1253
1272
  if (!cell) return generateTableCell(editor);
1254
1273
  let newCell = _objectSpread({
@@ -1398,96 +1417,78 @@ export const getCellHighlightClassName = (primaryColorClassName, rowIndex) => {
1398
1417
  }
1399
1418
  return className;
1400
1419
  };
1401
- export const getRowBottomPositions = (editor, rows) => {
1402
- const recorder = [];
1403
- let bottomPosition = 0;
1404
- rows.forEach(row => {
1405
- let cellHeight;
1406
- row.children.forEach(cell => {
1407
- const currentCellHeight = ReactEditor.toDOMNode(editor, cell).getBoundingClientRect().height;
1408
- if (!cellHeight || currentCellHeight < cellHeight) cellHeight = currentCellHeight;
1409
- });
1410
- bottomPosition += cellHeight;
1411
- recorder.push(bottomPosition);
1412
- });
1413
- return recorder;
1420
+ export const focusClosestCellWhenJustifyCellSize = (editor, adjustingCell) => {
1421
+ const cellPath = ReactEditor.findPath(editor, adjustingCell);
1422
+ focusEditor(editor, Editor.end(editor, cellPath));
1414
1423
  };
1415
- export const focusClosestCellWhenJustifyCellSize = _ref => {
1416
- let {
1417
- editor,
1418
- rowIndex,
1419
- columnIndex,
1420
- table,
1421
- mouseDownInfo,
1422
- rowBottoms
1423
- } = _ref;
1424
- if (!editor) return;
1425
- const {
1426
- columns
1427
- } = table;
1428
- // Get cursor position in table, calculate columnIndex and rowIndex
1429
- const tableDom = ReactEditor.toDOMNode(editor, table);
1430
- const {
1431
- left,
1432
- top
1433
- } = tableDom.getBoundingClientRect();
1434
- const {
1435
- positionY,
1436
- positionX
1437
- } = mouseDownInfo;
1438
- const tableScrollWrapper = document.querySelector('.sdoc-table-scroll-wrapper');
1439
- const scrollX = tableScrollWrapper.scrollLeft;
1440
- const tableInnerX = positionX - left + scrollX;
1441
- const tableInnerY = positionY - top;
1442
- // Calculate columnIndex
1443
- if (columnIndex === undefined) {
1444
- let passedColumnsWidth = 0;
1445
- columns.some((_ref2, index) => {
1446
- let {
1447
- width
1448
- } = _ref2;
1449
- passedColumnsWidth += width;
1450
- if (passedColumnsWidth > tableInnerX) {
1451
- columnIndex = index;
1452
- return true;
1453
- }
1454
- return false;
1455
- });
1424
+ const searchCombinedMainCell = (table, startRowIndex, startColIndex) => {
1425
+ for (let rowIndex = startRowIndex; rowIndex >= 0; rowIndex--) {
1426
+ const row = table.children[rowIndex];
1427
+ for (let cellIndex = startColIndex; cellIndex >= 0; cellIndex--) {
1428
+ const currentCell = row.children[cellIndex];
1429
+ const {
1430
+ colspan = 0,
1431
+ rowspan = 0
1432
+ } = currentCell;
1433
+ if (colspan <= 1 && rowspan <= 1) continue;
1434
+ const isInColRange = cellIndex + colspan >= startColIndex;
1435
+ const isInRowRange = rowIndex + rowspan >= startRowIndex;
1436
+ if (isInColRange && isInRowRange) {
1437
+ return {
1438
+ currentCell,
1439
+ rowIndex,
1440
+ cellIndex
1441
+ };
1442
+ } else break;
1443
+ }
1456
1444
  }
1457
- // Calculate rowIndex
1458
- if (rowIndex === undefined) {
1459
- rowBottoms.some((bottom, index) => {
1460
- if (bottom >= tableInnerY) {
1461
- rowIndex = index;
1462
- return true;
1463
- }
1464
- return false;
1465
- });
1445
+ };
1446
+ export const getResizeMaskCellInfo = (editor, table, rowIndex, cellIndex) => {
1447
+ // The cell shown cursor as resize (mouse is on this cell)
1448
+ const focusCellIndex = cellIndex;
1449
+ let focusCell = table.children[rowIndex].children[cellIndex];
1450
+ // The cell dominating resize handlers (the true cell to be resized)
1451
+ let cell = table.children[rowIndex].children[cellIndex];
1452
+ // Resolve combined cell
1453
+ if (cell.is_combined) {
1454
+ const targetCellInfo = searchCombinedMainCell(table, rowIndex, cellIndex);
1455
+ cellIndex = targetCellInfo.cellIndex;
1456
+ rowIndex = targetCellInfo.rowIndex;
1457
+ cell = targetCellInfo.currentCell;
1466
1458
  }
1467
- // Correct focus position
1468
- const is_combined = table.children[rowIndex].children[columnIndex].is_combined;
1469
- if (is_combined) {
1470
- for (let i = columnIndex - 1; i >= 0; i--) {
1471
- const {
1472
- is_combined: ci_is_combined,
1473
- colspan: ci_colspan
1474
- } = table.children[rowIndex].children[i];
1475
- if (!ci_is_combined && ci_colspan && i + ci_colspan - 1 >= columnIndex) {
1476
- columnIndex = i;
1477
- break;
1478
- }
1459
+ const columns = table.columns;
1460
+ const focussedCell = ReactEditor.toDOMNode(editor, cell);
1461
+ const {
1462
+ colspan,
1463
+ rowspan
1464
+ } = focusCell;
1465
+ let width = columns[cellIndex].width;
1466
+ let height = focussedCell.getBoundingClientRect().height;
1467
+ // Calculate cell width and height
1468
+ if (colspan > 1) {
1469
+ let index = cellIndex + 1;
1470
+ while (index < cellIndex + colspan) {
1471
+ width += columns[index].width;
1472
+ index++;
1479
1473
  }
1480
- for (let j = rowIndex - 1; j >= 0; j--) {
1481
- const {
1482
- is_combined: ri_is_combined,
1483
- rowspan: ri_rowspan
1484
- } = table.children[j].children[columnIndex];
1485
- if (!ri_is_combined && ri_rowspan && j + ri_rowspan - 1 >= rowIndex) {
1486
- rowIndex = j;
1487
- break;
1488
- }
1474
+ }
1475
+ if (rowspan > 1) {
1476
+ let index = rowIndex + 1;
1477
+ while (index < rowIndex + rowspan) {
1478
+ const currentCell = table.children[index].children[cellIndex];
1479
+ const currentHeight = ReactEditor.toDOMNode(editor, currentCell).getBoundingClientRect().height;
1480
+ height += currentHeight;
1481
+ index++;
1489
1482
  }
1490
1483
  }
1491
- const focusPath = ReactEditor.findPath(editor, table.children[rowIndex].children[columnIndex]);
1492
- focusEditor(editor, Editor.end(editor, focusPath));
1484
+ return {
1485
+ width,
1486
+ height,
1487
+ top: focussedCell.offsetTop,
1488
+ left: focussedCell.offsetLeft,
1489
+ rowIndex,
1490
+ cellIndex,
1491
+ cell,
1492
+ focusCellIndex
1493
+ };
1493
1494
  };
@@ -3,7 +3,7 @@
3
3
  margin: 16px 0;
4
4
  }
5
5
 
6
- .sdoc-table-wrapper + .sdoc-table-wrapper {
6
+ .sdoc-table-wrapper+.sdoc-table-wrapper {
7
7
  margin-top: 32px;
8
8
  }
9
9
 
@@ -68,6 +68,7 @@
68
68
  bottom: -2.5px;
69
69
  left: 0;
70
70
  z-index: 1;
71
+ pointer-events: none;
71
72
  }
72
73
 
73
74
  .sdoc-table-wrapper .table-row-height-just:hover {
@@ -88,6 +89,7 @@
88
89
  width: 5px;
89
90
  top: 0;
90
91
  z-index: 1;
92
+ pointer-events: none;
91
93
  }
92
94
 
93
95
  .sdoc-table-wrapper .table-cell-width-just:hover {
@@ -110,3 +112,47 @@
110
112
  .sdoc-table-wrapper .sdoc-table-selected-range .table-cell *::selection {
111
113
  background-color: unset;
112
114
  }
115
+
116
+ .sdoc-table-resize-mask {
117
+ position: absolute;
118
+ top: 0;
119
+ left: 0;
120
+ user-select: none;
121
+ opacity: 0;
122
+ z-index: 2;
123
+ pointer-events: none;
124
+ }
125
+
126
+ .sdoc-table-resize-mask .sdoc-table-resize-top {
127
+ position: absolute;
128
+ top: 1px;
129
+ width: 100%;
130
+ height: 1px;
131
+ cursor: row-resize;
132
+ }
133
+
134
+ .sdoc-table-resize-mask>div {
135
+ position: absolute;
136
+ pointer-events: auto;
137
+ }
138
+
139
+ .sdoc-table-resize-mask .sdoc-table-resize-bottom {
140
+ bottom: 1px;
141
+ width: 100%;
142
+ height: 1px;
143
+ cursor: row-resize;
144
+ }
145
+
146
+ .sdoc-table-resize-mask .sdoc-table-resize-right {
147
+ right: -1px;
148
+ width: 1px;
149
+ height: 100%;
150
+ cursor: col-resize;
151
+ }
152
+
153
+ .sdoc-table-resize-mask .sdoc-table-resize-left {
154
+ left: -1px;
155
+ width: 1px;
156
+ height: 100%;
157
+ cursor: col-resize;
158
+ }
@@ -15,6 +15,7 @@ import { registerResizeEvents, unregisterResizeEvents } from '../../../../utils/
15
15
  import TableRoot from './table-root';
16
16
  import TableHeader from './table-header';
17
17
  import { findPath } from '../../../core';
18
+ import ResizeMask from './resize-mask';
18
19
  import './index.css';
19
20
  import './alternate-color.css';
20
21
  const Table = _ref => {
@@ -36,6 +37,9 @@ const Table = _ref => {
36
37
  const oldColumns = getTableColumns(editor, element);
37
38
  const [columns, setColumns] = useState(oldColumns);
38
39
  const path = findPath(editor, element);
40
+ const [resizeCellMaskInfo, setResizeCellMaskInfo] = useState({});
41
+ const [isShowResizeHandlers, setIsShowResizeHandlers] = useState(false);
42
+ const [isDraggingResizeHandler, setIsDraggingResizeHandler] = useState(false);
39
43
  const onMouseDown = useCallback(event => {
40
44
  if (event.button !== 0) return; // right click not deal
41
45
  setIsSettingSelectRange(true);
@@ -146,7 +150,6 @@ const Table = _ref => {
146
150
  });
147
151
  };
148
152
  }
149
-
150
153
  // eslint-disable-next-line react-hooks/exhaustive-deps
151
154
  }, [element, isSettingSelectRange, selectedRange, element]);
152
155
  const setRange = useCallback((table, range) => {
@@ -168,6 +171,17 @@ const Table = _ref => {
168
171
  'sdoc-table-selected': isSelected,
169
172
  'sdoc-table-selected-range': !ObjectUtils.isSameObject(selectedRange, EMPTY_SELECTED_RANGE)
170
173
  });
174
+ const handleShowResizeHandler = useCallback(cellInfo => {
175
+ setResizeCellMaskInfo(cellInfo);
176
+ setIsShowResizeHandlers(true);
177
+ }, []);
178
+ const hideResizeHandlers = useCallback(() => {
179
+ setIsShowResizeHandlers(false);
180
+ setIsDraggingResizeHandler(false);
181
+ }, []);
182
+ const handlerStartDragging = useCallback(() => {
183
+ setIsDraggingResizeHandler(true);
184
+ }, []);
171
185
  let style = element.style ? _objectSpread({}, element.style) : {};
172
186
  const columnWidthList = columns.map(item => "".concat(item.width, "px"));
173
187
  style.gridTemplateColumns = columnWidthList.join(' ');
@@ -191,8 +205,18 @@ const Table = _ref => {
191
205
  onMouseDown: onMouseDown,
192
206
  ref: table,
193
207
  "data-id": element.id
194
- }, children, !isSettingSelectRange && /*#__PURE__*/React.createElement(ResizeHandlers, {
195
- element: element
208
+ }, children, !isSettingSelectRange && /*#__PURE__*/React.createElement(ResizeMask, {
209
+ editor: editor,
210
+ table: element,
211
+ handleShowResizeHandler: handleShowResizeHandler,
212
+ hideResizeHandlers: hideResizeHandlers,
213
+ handlerStartDragging: handlerStartDragging,
214
+ isDraggingResizeHandler: isDraggingResizeHandler
215
+ }), !isSettingSelectRange && isShowResizeHandlers && /*#__PURE__*/React.createElement(ResizeHandlers, {
216
+ hideResizeHandlers: hideResizeHandlers,
217
+ element: element,
218
+ resizeCellMaskInfo: resizeCellMaskInfo,
219
+ isDraggingResizeHandler: isDraggingResizeHandler
196
220
  }))))));
197
221
  };
198
222
  function renderTable(props) {