@seafile/sdoc-editor 0.3.16 → 0.3.17

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 (26) hide show
  1. package/dist/assets/css/simple-editor.css +1 -1
  2. package/dist/basic-sdk/extension/commons/insert-element-dialog/index.js +5 -1
  3. package/dist/basic-sdk/extension/constants/index.js +1 -1
  4. package/dist/basic-sdk/extension/constants/menus-config.js +6 -0
  5. package/dist/basic-sdk/extension/plugins/table/constants/index.js +1 -1
  6. package/dist/basic-sdk/extension/plugins/table/dialogs/index.js +3 -0
  7. package/dist/basic-sdk/extension/plugins/table/dialogs/split-cell-setting-dialog.js +99 -0
  8. package/dist/basic-sdk/extension/plugins/table/helpers.js +324 -1
  9. package/dist/basic-sdk/extension/plugins/table/menu/active-table-menu/combine-cells.js +32 -0
  10. package/dist/basic-sdk/extension/plugins/table/menu/active-table-menu/index.css +0 -4
  11. package/dist/basic-sdk/extension/plugins/table/menu/active-table-menu/index.js +6 -0
  12. package/dist/basic-sdk/extension/plugins/table/menu/table-context-menu/index.js +31 -4
  13. package/dist/basic-sdk/extension/plugins/table/render/index.css +1 -11
  14. package/dist/basic-sdk/extension/plugins/table/render/index.js +35 -7
  15. package/dist/basic-sdk/extension/plugins/table/render/render-cell.js +34 -15
  16. package/dist/basic-sdk/extension/plugins/table/render/render-row.js +5 -118
  17. package/dist/basic-sdk/extension/plugins/table/render/resize-handlers/{resize-handler.js → column-resize-handler.js} +2 -2
  18. package/dist/basic-sdk/extension/plugins/table/render/resize-handlers/index.js +17 -4
  19. package/dist/basic-sdk/extension/plugins/table/render/resize-handlers/row-resize-handler.js +89 -0
  20. package/dist/basic-sdk/extension/plugins/table/render/table-header/rows-header/row-header.js +12 -13
  21. package/package.json +1 -1
  22. package/public/locales/en/sdoc-editor.json +7 -1
  23. package/public/locales/zh_CN/sdoc-editor.json +7 -1
  24. /package/dist/basic-sdk/extension/plugins/table/{dialog → dialogs}/custom-table-size-dialog/index.css +0 -0
  25. /package/dist/basic-sdk/extension/plugins/table/{dialog → dialogs}/custom-table-size-dialog/index.js +0 -0
  26. /package/dist/basic-sdk/extension/plugins/table/{dialog → dialogs}/custom-table-size-dialog/number-input.js +0 -0
@@ -1,6 +1,6 @@
1
1
  /* reset common css */
2
2
  .sdoc-editor-page-wrapper .dropdown-item,
3
- .sdoc-context-menu .dropdown-item {
3
+ .sdoc-context-menu .dropdown-item:not(:disabled) {
4
4
  color: #212529;
5
5
  }
6
6
 
@@ -1,5 +1,5 @@
1
1
  import React, { useCallback, useEffect, useState, useRef } from 'react';
2
- import CustomTableSizeDialog from '../../plugins/table/dialog/custom-table-size-dialog';
2
+ import { CustomTableSizeDialog, SplitCellSettingDialog } from '../../plugins/table/dialogs';
3
3
  import AddLinkDialog from '../../plugins/link/dialog/add-link-dialog';
4
4
  import SelectFileDialog from '../select-file-dialog/index.js';
5
5
  import EventBus from '../../../utils/event-bus';
@@ -77,6 +77,10 @@ const InsertElementDialog = _ref => {
77
77
  {
78
78
  return /*#__PURE__*/React.createElement(CustomTableSizeDialog, props);
79
79
  }
80
+ case ELEMENT_TYPE.TABLE_CELL:
81
+ {
82
+ return /*#__PURE__*/React.createElement(SplitCellSettingDialog, props);
83
+ }
80
84
  case ELEMENT_TYPE.LINK:
81
85
  {
82
86
  return /*#__PURE__*/React.createElement(AddLinkDialog, props);
@@ -5,7 +5,7 @@ export { DEFAULT_COLORS, STANDARD_COLORS, DEFAULT_RECENT_USED_LIST, DEFAULT_FONT
5
5
  export { FONT_SIZE, DEFAULT_FONT, FONT, GOOGLE_FONT_CLASS, RECENT_USED_FONTS_KEY, SDOC_FONT_SIZE } from './font';
6
6
  export { DIFF_TYPE, ADDED_STYLE, DELETED_STYLE } from './diff-view';
7
7
  export { KEYBOARD, MAC_HOTKEYS, WIN_HOTKEYS } from './keyboard';
8
- export { UNDO, REDO, TEXT_STYLE, TEXT_STYLE_MAP, TEXT_STYLE_MORE, TEXT_ALIGN, REMOVE_TABLE, CLEAR_FORMAT, MENUS_CONFIG_MAP, SIDE_TRANSFORM_MENUS_CONFIG, SIDE_INSERT_MENUS_CONFIG } from './menus-config';
8
+ export { UNDO, REDO, TEXT_STYLE, TEXT_STYLE_MAP, TEXT_STYLE_MORE, TEXT_ALIGN, REMOVE_TABLE, COMBINE_CELL, CLEAR_FORMAT, MENUS_CONFIG_MAP, SIDE_TRANSFORM_MENUS_CONFIG, SIDE_INSERT_MENUS_CONFIG } from './menus-config';
9
9
  export const HEADERS = [HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6];
10
10
  export const HEADER_TITLE_MAP = {
11
11
  [TITLE]: 'Title',
@@ -4,6 +4,7 @@ export const UNDO = 'undo';
4
4
  export const REDO = 'redo';
5
5
  export const CLEAR_FORMAT = 'clear_format';
6
6
  export const REMOVE_TABLE = 'remove_table';
7
+ export const COMBINE_CELL = 'combine_cell';
7
8
 
8
9
  // text style
9
10
  export const TEXT_STYLE = 'text_style';
@@ -89,6 +90,11 @@ export const MENUS_CONFIG_MAP = {
89
90
  iconClass: 'sdocfont sdoc-delete-table',
90
91
  text: 'Delete_table'
91
92
  },
93
+ [COMBINE_CELL]: {
94
+ id: "sdoc_".concat(COMBINE_CELL),
95
+ iconClass: 'sdocfont sdoc-merge-cell',
96
+ text: 'Combine_cell'
97
+ },
92
98
  [TEXT_STYLE]: [{
93
99
  id: ITALIC,
94
100
  iconClass: 'sdocfont sdoc-italic',
@@ -1,4 +1,4 @@
1
- export const TABLE_MAX_ROWS = 50;
1
+ export const TABLE_MAX_ROWS = 500;
2
2
  export const TABLE_MAX_COLUMNS = 50;
3
3
  export const EMPTY_SELECTED_RANGE = {
4
4
  minRowIndex: -1,
@@ -0,0 +1,3 @@
1
+ import CustomTableSizeDialog from './custom-table-size-dialog';
2
+ import SplitCellSettingDialog from './split-cell-setting-dialog';
3
+ export { CustomTableSizeDialog, SplitCellSettingDialog };
@@ -0,0 +1,99 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Alert, Row, Col, FormGroup, Label, Input } from 'reactstrap';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { getSelectedInfo, splitCell } from '../helpers';
5
+ const SplitCellSettingDialog = _ref => {
6
+ let {
7
+ editor,
8
+ closeDialog
9
+ } = _ref;
10
+ const {
11
+ t
12
+ } = useTranslation();
13
+ const {
14
+ cell
15
+ } = getSelectedInfo(editor);
16
+ const {
17
+ rowspan = 1,
18
+ colspan = 1
19
+ } = cell;
20
+ const [rowNumber, setRowNumber] = useState(rowspan);
21
+ const [columnNumber, setColumnNumber] = useState(colspan);
22
+ const CELL_SPLIT_MAX_ROWS = rowspan;
23
+ const CELL_SPLIT_MAX_COLUMNS = colspan;
24
+ const [errorMessage, setErrorMessage] = useState('');
25
+ const onRowNumberChange = useCallback(e => {
26
+ setRowNumber(e.target.value);
27
+ // eslint-disable-next-line react-hooks/exhaustive-deps
28
+ }, []);
29
+ const onColumnNumberChange = useCallback(e => {
30
+ setColumnNumber(e.target.value);
31
+ // eslint-disable-next-line react-hooks/exhaustive-deps
32
+ }, []);
33
+ const handleSubmit = useCallback(() => {
34
+ const parsedRowNumber = parseInt(rowNumber);
35
+ const parsedColumnNumber = parseInt(columnNumber);
36
+ if (!parsedRowNumber || !parsedColumnNumber || parsedRowNumber < 0 || parsedColumnNumber < 0) {
37
+ setErrorMessage(t('Please_enter_a_non-negative_integer'));
38
+ return false;
39
+ }
40
+ let targetRowNumber = parsedRowNumber;
41
+ let targetColumnNumber = parsedColumnNumber;
42
+ if (parsedRowNumber > CELL_SPLIT_MAX_ROWS) {
43
+ targetRowNumber = CELL_SPLIT_MAX_ROWS;
44
+ setRowNumber(targetRowNumber);
45
+ setErrorMessage(t('The_maximum_row_number_is_{number}').replace('{number}', CELL_SPLIT_MAX_ROWS));
46
+ return false;
47
+ }
48
+ if (parsedColumnNumber > CELL_SPLIT_MAX_COLUMNS) {
49
+ targetColumnNumber = CELL_SPLIT_MAX_COLUMNS;
50
+ setColumnNumber(targetColumnNumber);
51
+ setErrorMessage(t('The_maximum_column_number_is_{number}').replace('{number}', CELL_SPLIT_MAX_COLUMNS));
52
+ return false;
53
+ }
54
+ splitCell(editor, targetRowNumber, targetColumnNumber);
55
+ closeDialog();
56
+ // eslint-disable-next-line react-hooks/exhaustive-deps
57
+ }, [rowNumber, columnNumber]);
58
+ return /*#__PURE__*/React.createElement(Modal, {
59
+ isOpen: true,
60
+ autoFocus: false,
61
+ toggle: closeDialog,
62
+ zIndex: 1071,
63
+ returnFocusAfterClose: true
64
+ }, /*#__PURE__*/React.createElement(ModalHeader, {
65
+ toggle: closeDialog
66
+ }, t('Split_cell')), /*#__PURE__*/React.createElement(ModalBody, null, /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(Col, {
67
+ md: 6
68
+ }, /*#__PURE__*/React.createElement(FormGroup, null, /*#__PURE__*/React.createElement(Label, {
69
+ for: "row-number"
70
+ }, t('Row_number')), /*#__PURE__*/React.createElement(Input, {
71
+ id: "row-number",
72
+ name: "row-number",
73
+ type: "number",
74
+ min: 1,
75
+ value: rowNumber,
76
+ onChange: onRowNumberChange
77
+ }))), /*#__PURE__*/React.createElement(Col, {
78
+ md: 6
79
+ }, /*#__PURE__*/React.createElement(FormGroup, null, /*#__PURE__*/React.createElement(Label, {
80
+ for: "column-number"
81
+ }, t('Column_number')), /*#__PURE__*/React.createElement(Input, {
82
+ id: "column-number",
83
+ name: "column-number",
84
+ type: "number",
85
+ min: 1,
86
+ value: columnNumber,
87
+ onChange: onColumnNumberChange
88
+ })))), errorMessage && /*#__PURE__*/React.createElement(Alert, {
89
+ className: "mt-2 mb-0",
90
+ color: "danger"
91
+ }, t(errorMessage))), /*#__PURE__*/React.createElement(ModalFooter, null, /*#__PURE__*/React.createElement(Button, {
92
+ color: "secondary",
93
+ onClick: closeDialog
94
+ }, t('Cancel')), /*#__PURE__*/React.createElement(Button, {
95
+ color: "primary",
96
+ onClick: handleSubmit
97
+ }, t('Submit'))));
98
+ };
99
+ export default SplitCellSettingDialog;
@@ -41,6 +41,18 @@ export const isTableMenuDisabled = (editor, readonly) => {
41
41
  if (match) return true;
42
42
  return false;
43
43
  };
44
+ export const isCombineCellsDisabled = (editor, readonly) => {
45
+ if (readonly) return true;
46
+ const {
47
+ selection,
48
+ tableSelectedRange
49
+ } = editor;
50
+ if (!selection) return true;
51
+ if (!ObjectUtils.isSameObject(tableSelectedRange, EMPTY_SELECTED_RANGE)) {
52
+ return false;
53
+ }
54
+ return true;
55
+ };
44
56
  export const generateTableCell = () => {
45
57
  return {
46
58
  id: slugid.nice(),
@@ -99,9 +111,13 @@ export const generateEmptyTable = (editor, tableProps) => {
99
111
  type: ELEMENT_TYPE.TABLE,
100
112
  children: children,
101
113
  columns,
102
- style: {
114
+ ui: {
103
115
  alternate_highlight,
104
116
  alternate_highlight_color
117
+ },
118
+ style: {
119
+ gridTemplateColumns: "repeat(".concat(colsCount, ", ").concat(columnWidth, "px)"),
120
+ gridAutoRows: "minmax(".concat(TABLE_ROW_MIN_HEIGHT, "px, auto)")
105
121
  }
106
122
  };
107
123
  };
@@ -219,6 +235,61 @@ export const insertTableRow = function (editor, table, rowIndex) {
219
235
  });
220
236
  const focusPath = [...targetPath, 0];
221
237
  focusEditor(editor, focusPath);
238
+
239
+ // handle cells with the rowspan > 1
240
+ if (position === TABLE_ELEMENT_POSITION.AFTER) {
241
+ handleCombinedCellsAfterInsertTableRow(editor, tablePath, table, rowIndex);
242
+ }
243
+ };
244
+ export const handleCombinedCellsAfterInsertTableRow = (editor, tablePath, table, rowIndex) => {
245
+ // important background info: the new row is inserted after rowIndex
246
+ const cells = table.children[rowIndex].children;
247
+ for (let i = 0, len = cells.length; i < len; i++) {
248
+ const {
249
+ is_combined,
250
+ rowspan,
251
+ colspan
252
+ } = cells[i];
253
+ if (is_combined) {
254
+ for (let ri = rowIndex - 1; ri >= 0; ri--) {
255
+ const {
256
+ is_combined: ri_is_combined,
257
+ rowspan: ri_rowspan,
258
+ colspan: ri_colspan
259
+ } = table.children[ri].children[i];
260
+ if (!ri_is_combined && ri + ri_rowspan - 1 > rowIndex) {
261
+ Transforms.setNodes(editor, {
262
+ rowspan: ri_rowspan + 1
263
+ }, {
264
+ at: [...tablePath, ri, i]
265
+ });
266
+ for (let j = 0; j < ri_colspan; j++) {
267
+ Transforms.setNodes(editor, {
268
+ 'is_combined': true
269
+ }, {
270
+ at: [...tablePath, rowIndex + 1, i + j]
271
+ });
272
+ }
273
+ break;
274
+ }
275
+ }
276
+ } else {
277
+ if (rowspan > 1) {
278
+ Transforms.setNodes(editor, {
279
+ rowspan: rowspan + 1
280
+ }, {
281
+ at: [...tablePath, rowIndex, i]
282
+ });
283
+ for (let j = 0; j < colspan; j++) {
284
+ Transforms.setNodes(editor, {
285
+ 'is_combined': true
286
+ }, {
287
+ at: [...tablePath, rowIndex + 1, i + j]
288
+ });
289
+ }
290
+ }
291
+ }
292
+ }
222
293
  };
223
294
  export const insertTableColumn = function (editor, table, columnIndex) {
224
295
  let position = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : TABLE_ELEMENT_POSITION.AFTER;
@@ -238,6 +309,60 @@ export const insertTableColumn = function (editor, table, columnIndex) {
238
309
  }
239
310
  const focusPath = [...tablePath, 0, newCellIndex, 0];
240
311
  focusEditor(editor, focusPath);
312
+
313
+ // handle cells with the colspan > 1
314
+ if (position === TABLE_ELEMENT_POSITION.AFTER) {
315
+ handleCombinedCellsAfterInsertTableColumn(editor, tablePath, table, columnIndex);
316
+ }
317
+ };
318
+ export const handleCombinedCellsAfterInsertTableColumn = (editor, tablePath, table, columnIndex) => {
319
+ // important background info: the new column is inserted after columnIndex
320
+ for (let i = 0, len = table.children.length; i < len; i++) {
321
+ const {
322
+ is_combined,
323
+ rowspan,
324
+ colspan
325
+ } = table.children[i].children[columnIndex];
326
+ if (is_combined) {
327
+ for (let ci = columnIndex - 1; ci >= 0; ci--) {
328
+ const {
329
+ is_combined: ci_is_combined,
330
+ rowspan: ci_rowspan,
331
+ colspan: ci_colspan
332
+ } = table.children[i].children[ci];
333
+ if (!ci_is_combined && ci + ci_colspan - 1 > columnIndex) {
334
+ Transforms.setNodes(editor, {
335
+ colspan: ci_colspan + 1
336
+ }, {
337
+ at: [...tablePath, i, ci]
338
+ });
339
+ for (let j = 0; j < ci_rowspan; j++) {
340
+ Transforms.setNodes(editor, {
341
+ 'is_combined': true
342
+ }, {
343
+ at: [...tablePath, i + j, columnIndex + 1]
344
+ });
345
+ }
346
+ break;
347
+ }
348
+ }
349
+ } else {
350
+ if (colspan > 1) {
351
+ Transforms.setNodes(editor, {
352
+ colspan: colspan + 1
353
+ }, {
354
+ at: [...tablePath, i, columnIndex]
355
+ });
356
+ for (let j = 0; j < rowspan; j++) {
357
+ Transforms.setNodes(editor, {
358
+ 'is_combined': true
359
+ }, {
360
+ at: [...tablePath, i + j, columnIndex + 1]
361
+ });
362
+ }
363
+ }
364
+ }
365
+ }
241
366
  };
242
367
  export const insertTableElement = function (editor, type) {
243
368
  let position = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : TABLE_ELEMENT_POSITION.AFTER;
@@ -260,6 +385,12 @@ export const insertTableElement = function (editor, type) {
260
385
  Transforms.insertNodes(editor, row, {
261
386
  at: targetPath
262
387
  });
388
+ // handle combined cells
389
+ if (!(rowIndex == 0 && position === TABLE_ELEMENT_POSITION.BEFORE)) {
390
+ const targetRowIndex = position === TABLE_ELEMENT_POSITION.AFTER ? rowIndex : rowIndex - 1;
391
+ const currentTable = getSelectedNodeByType(editor, ELEMENT_TYPE.TABLE);
392
+ handleCombinedCellsAfterInsertTableRow(editor, tablePath, currentTable, targetRowIndex);
393
+ }
263
394
  }
264
395
  const focusPath = [...targetPath, cellIndex];
265
396
  focusEditor(editor, focusPath);
@@ -279,12 +410,121 @@ export const insertTableElement = function (editor, type) {
279
410
  at: newCellPath
280
411
  });
281
412
  }
413
+ // handle combined cells
414
+ if (!(cellIndex == 0 && position === TABLE_ELEMENT_POSITION.BEFORE)) {
415
+ const targetColumnIndex = position === TABLE_ELEMENT_POSITION.AFTER ? cellIndex : cellIndex - 1;
416
+ const currentTable = getSelectedNodeByType(editor, ELEMENT_TYPE.TABLE);
417
+ handleCombinedCellsAfterInsertTableColumn(editor, tablePath, currentTable, targetColumnIndex);
418
+ }
282
419
  }
283
420
  const focusPath = [...tablePath, rowIndex, cellIndex + 1, 0];
284
421
  focusEditor(editor, focusPath);
285
422
  return;
286
423
  }
287
424
  };
425
+ export const combineCells = editor => {
426
+ const {
427
+ tablePath
428
+ } = getSelectedInfo(editor);
429
+ const {
430
+ minColIndex,
431
+ maxColIndex,
432
+ minRowIndex,
433
+ maxRowIndex
434
+ } = editor.tableSelectedRange;
435
+ let newCellContent = [];
436
+ for (let i = minRowIndex; i < maxRowIndex + 1; i++) {
437
+ for (let j = minColIndex; j < maxColIndex + 1; j++) {
438
+ let nodePath = [...tablePath, i, j];
439
+ let node = Editor.node(editor, nodePath);
440
+ if (node[0].is_combined) {
441
+ continue;
442
+ }
443
+ Transforms.setNodes(editor, {
444
+ 'is_combined': true
445
+ }, {
446
+ at: nodePath
447
+ });
448
+ newCellContent = newCellContent.concat(node[0].children);
449
+ }
450
+ }
451
+ const targetCellPath = [...tablePath, minRowIndex, minColIndex];
452
+ const newCell = generateTableCell();
453
+ newCell.children = newCellContent;
454
+ newCell.rowspan = maxRowIndex - minRowIndex + 1;
455
+ newCell.colspan = maxColIndex - minColIndex + 1;
456
+ // keep row.chilren.length not changed
457
+ Transforms.removeNodes(editor, {
458
+ at: targetCellPath
459
+ });
460
+ Transforms.insertNodes(editor, newCell, {
461
+ at: targetCellPath
462
+ });
463
+ focusEditor(editor, targetCellPath);
464
+
465
+ // for clicking the 'combine cell' icon in the toolbar
466
+ const eventBus = EventBus.getInstance();
467
+ eventBus.dispatch(INTERNAL_EVENT.CANCEL_TABLE_SELECT_RANGE);
468
+ };
469
+ export const splitCell = (editor, rowNumber, columnNumber) => {
470
+ if (rowNumber == 1 && columnNumber == 1) {
471
+ return;
472
+ }
473
+ const {
474
+ cell,
475
+ rowIndex,
476
+ cellIndex,
477
+ cellPath,
478
+ tablePath
479
+ } = getSelectedInfo(editor);
480
+ const {
481
+ rowspan,
482
+ colspan
483
+ } = cell;
484
+ const rowspanBase = Math.floor(rowspan / rowNumber);
485
+ const rowspanLeft = rowspan % rowNumber;
486
+ const colspanBase = Math.floor(colspan / columnNumber);
487
+ const colspanLeft = colspan % columnNumber;
488
+ const cellNumber = rowNumber * columnNumber;
489
+ const dataBlockNumber = Math.ceil(cell.children.length / cellNumber);
490
+ let firstNewCell;
491
+ let rowspanSum = 0;
492
+ for (let i = 0; i < rowNumber; i++) {
493
+ let newRowSpan = rowspanBase + (i + 1 <= rowspanLeft ? 1 : 0);
494
+ let colspanSum = 0;
495
+ for (let j = 0; j < columnNumber; j++) {
496
+ const newCell = generateTableCell();
497
+ let startIndex = (i * columnNumber + j) * dataBlockNumber;
498
+ if (startIndex < cell.children.length) {
499
+ let endIndex = Math.min(startIndex + dataBlockNumber, cell.children.length);
500
+ newCell.children = cell.children.slice(startIndex, endIndex);
501
+ }
502
+ newCell.rowspan = newRowSpan;
503
+ newCell.colspan = colspanBase + (j + 1 <= colspanLeft ? 1 : 0);
504
+ const newRowIndex = rowIndex + rowspanSum;
505
+ const newCellIndex = cellIndex + colspanSum;
506
+ const targetCellPath = [...tablePath, newRowIndex, newCellIndex];
507
+ if (i == 0 && j == 0) {
508
+ firstNewCell = newCell;
509
+ } else {
510
+ Transforms.removeNodes(editor, {
511
+ at: targetCellPath
512
+ });
513
+ Transforms.insertNodes(editor, newCell, {
514
+ at: targetCellPath
515
+ });
516
+ }
517
+ colspanSum += newCell.colspan;
518
+ }
519
+ rowspanSum += newRowSpan;
520
+ }
521
+ Transforms.removeNodes(editor, {
522
+ at: cellPath
523
+ });
524
+ Transforms.insertNodes(editor, firstNewCell, {
525
+ at: cellPath
526
+ });
527
+ };
288
528
  export const removeTable = (editor, path) => {
289
529
  let validPath = path;
290
530
  if (!validPath) {
@@ -336,6 +576,7 @@ export const removeTableElement = (editor, type) => {
336
576
  focusEditor(editor, focusPath);
337
577
  return;
338
578
  }
579
+ handleCombinedCellsBeforeDeleteTableRow(editor, tablePath, table, rowIndex);
339
580
  Transforms.removeNodes(editor, {
340
581
  at: rowPath
341
582
  });
@@ -378,6 +619,7 @@ export const removeTableElement = (editor, type) => {
378
619
  const newColumns = columns.slice(0);
379
620
  newColumns.splice(cellIndex, 1);
380
621
  updateColumnWidth(editor, table, newColumns);
622
+ handleCombinedCellsBeforeDeleteTableColumn(editor, tablePath, table, cellIndex);
381
623
  for (let i = 0; i < tableSize[0]; i++) {
382
624
  const cellPath = [...tablePath, i, cellIndex];
383
625
  Transforms.removeNodes(editor, {
@@ -390,6 +632,87 @@ export const removeTableElement = (editor, type) => {
390
632
  return;
391
633
  }
392
634
  };
635
+
636
+ // handle combined cells before deleting a row
637
+ export const handleCombinedCellsBeforeDeleteTableRow = (editor, tablePath, table, rowIndex) => {
638
+ const cells = table.children[rowIndex].children;
639
+ for (let i = 0, len = cells.length; i < len; i++) {
640
+ const {
641
+ is_combined,
642
+ rowspan,
643
+ colspan
644
+ } = cells[i];
645
+ if (is_combined) {
646
+ for (let ri = rowIndex - 1; ri >= 0; ri--) {
647
+ const {
648
+ is_combined: ri_is_combined,
649
+ rowspan: ri_rowspan
650
+ } = table.children[ri].children[i];
651
+ if (!ri_is_combined && ri + ri_rowspan - 1 >= rowIndex) {
652
+ Transforms.setNodes(editor, {
653
+ rowspan: ri_rowspan - 1
654
+ }, {
655
+ at: [...tablePath, ri, i]
656
+ });
657
+ break;
658
+ }
659
+ }
660
+ } else {
661
+ if (rowspan > 1) {
662
+ const targetCellPath = [...tablePath, rowIndex + 1, i];
663
+ const newCell = generateTableCell();
664
+ newCell.rowspan = rowspan - 1;
665
+ newCell.colspan = colspan;
666
+ Transforms.removeNodes(editor, {
667
+ at: targetCellPath
668
+ });
669
+ Transforms.insertNodes(editor, newCell, {
670
+ at: targetCellPath
671
+ });
672
+ }
673
+ }
674
+ }
675
+ };
676
+
677
+ // handle combined cells before deleting a column
678
+ export const handleCombinedCellsBeforeDeleteTableColumn = (editor, tablePath, table, columnIndex) => {
679
+ for (let i = 0, len = table.children.length; i < len; i++) {
680
+ const {
681
+ is_combined,
682
+ rowspan,
683
+ colspan
684
+ } = table.children[i].children[columnIndex];
685
+ if (is_combined) {
686
+ for (let ci = columnIndex - 1; ci >= 0; ci--) {
687
+ const {
688
+ is_combined: ci_is_combined,
689
+ colspan: ci_colspan
690
+ } = table.children[i].children[ci];
691
+ if (!ci_is_combined && ci + ci_colspan - 1 >= columnIndex) {
692
+ Transforms.setNodes(editor, {
693
+ colspan: ci_colspan - 1
694
+ }, {
695
+ at: [...tablePath, i, ci]
696
+ });
697
+ break;
698
+ }
699
+ }
700
+ } else {
701
+ if (colspan > 1) {
702
+ const targetCellPath = [...tablePath, i, columnIndex + 1];
703
+ const newCell = generateTableCell();
704
+ newCell.rowspan = rowspan;
705
+ newCell.colspan = colspan - 1;
706
+ Transforms.removeNodes(editor, {
707
+ at: targetCellPath
708
+ });
709
+ Transforms.insertNodes(editor, newCell, {
710
+ at: targetCellPath
711
+ });
712
+ }
713
+ }
714
+ }
715
+ };
393
716
  export const setTableSelectedRange = (editor, range) => {
394
717
  if (range) {
395
718
  editor.tableSelectedRange = range;
@@ -0,0 +1,32 @@
1
+ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
+ import React, { useCallback, useEffect, useState } from 'react';
3
+ import { MenuItem } from '../../../../commons';
4
+ import { MENUS_CONFIG_MAP, COMBINE_CELL } from '../../../../constants';
5
+ import { isCombineCellsDisabled, combineCells } from '../../helpers';
6
+ const CombineCells = _ref => {
7
+ let {
8
+ isRichEditor,
9
+ className,
10
+ editor,
11
+ readonly
12
+ } = _ref;
13
+ const [disabled, setDisabled] = useState(false);
14
+ useEffect(() => {
15
+ const disabled = isCombineCellsDisabled(editor, readonly);
16
+ setDisabled(disabled);
17
+ }, [editor, editor.tableSelectedRange, readonly]);
18
+ const _combineCells = useCallback(() => {
19
+ if (readonly) return;
20
+ combineCells(editor);
21
+ }, [editor, readonly]);
22
+ const menuConfig = MENUS_CONFIG_MAP[COMBINE_CELL];
23
+ const props = _objectSpread(_objectSpread({
24
+ isRichEditor,
25
+ disabled: disabled,
26
+ isActive: false
27
+ }, menuConfig), {}, {
28
+ onMouseDown: _combineCells
29
+ });
30
+ return /*#__PURE__*/React.createElement(MenuItem, props);
31
+ };
32
+ export default CombineCells;
@@ -1,7 +1,3 @@
1
- .sdoc-table-menu-group.menu-group .menu-group-item:not(.sdoc-remove-table) {
2
- width: 36px;
3
- }
4
-
5
1
  .sdoc-table-menu-group .sdoc-menu-with-dropdown .sdoc-menu-with-dropdown-icon {
6
2
  width: 24px;
7
3
  }
@@ -4,6 +4,7 @@ import { MenuGroup } from '../../../../commons';
4
4
  import { isAllInTable } from '../../helpers';
5
5
  import CellBackgroundColorMenu from './cell-bg-color-menu';
6
6
  import CellTextAlignMenu from './cell-text-align-menu';
7
+ import CombineCells from './combine-cells';
7
8
  import RemoveTable from './remove-table-menu';
8
9
  import TableColumnMenu from './table-column-menu';
9
10
  import TableRowMenu from './table-row-menu';
@@ -32,6 +33,11 @@ const ActiveTableMenu = _ref => {
32
33
  isRichEditor: isRichEditor,
33
34
  className: className,
34
35
  readonly: readonly
36
+ }), /*#__PURE__*/React.createElement(CombineCells, {
37
+ editor: editor,
38
+ isRichEditor: isRichEditor,
39
+ className: className,
40
+ readonly: readonly
35
41
  }), /*#__PURE__*/React.createElement(RemoveTable, {
36
42
  editor: editor,
37
43
  isRichEditor: isRichEditor,
@@ -4,9 +4,11 @@ import ObjectUtils from '../../../../../utils/object-utils';
4
4
  import { ElementPopover } from '../../../../commons';
5
5
  import { ELEMENT_TYPE } from '../../../../constants';
6
6
  import { getSelectedNodeByType } from '../../../../core';
7
- import { TABLE_MAX_COLUMNS, TABLE_MAX_ROWS, TABLE_ELEMENT, TABLE_ELEMENT_POSITION } from '../../constants';
8
- import { insertTableElement, removeTableElement } from '../../helpers';
7
+ import { TABLE_MAX_COLUMNS, TABLE_MAX_ROWS, TABLE_ELEMENT, TABLE_ELEMENT_POSITION, EMPTY_SELECTED_RANGE } from '../../constants';
8
+ import { insertTableElement, removeTableElement, getSelectedInfo, combineCells } from '../../helpers';
9
9
  import InsertTableElement from './insert-table-element';
10
+ import EventBus from '../../../../../utils/event-bus';
11
+ import { INTERNAL_EVENT } from '../../../../../constants';
10
12
  import './index.css';
11
13
  class TableContextMenu extends React.Component {
12
14
  constructor(props) {
@@ -51,10 +53,22 @@ class TableContextMenu extends React.Component {
51
53
  className: "dropdown-item"
52
54
  }, this.props.t(title));
53
55
  };
56
+ this.combineCells = () => {
57
+ const {
58
+ editor
59
+ } = this.props;
60
+ combineCells(editor);
61
+ };
62
+ this.toggleSplitCellSettingDialog = () => {
63
+ this.eventBus.dispatch(INTERNAL_EVENT.INSERT_ELEMENT, {
64
+ type: ELEMENT_TYPE.TABLE_CELL
65
+ });
66
+ };
54
67
  this.state = {
55
68
  contextStyle: {}
56
69
  };
57
70
  this.position = null;
71
+ this.eventBus = EventBus.getInstance();
58
72
  }
59
73
  componentDidMount() {
60
74
  this.position = this.props.contextMenuPosition;
@@ -75,7 +89,8 @@ class TableContextMenu extends React.Component {
75
89
  contextStyle
76
90
  } = this.state;
77
91
  const {
78
- editor
92
+ editor,
93
+ t
79
94
  } = this.props;
80
95
  const currentTable = getSelectedNodeByType(editor, ELEMENT_TYPE.TABLE);
81
96
  if (!currentTable) return null;
@@ -89,6 +104,8 @@ class TableContextMenu extends React.Component {
89
104
  const selectedCols = tableSelectedRange.maxColIndex - tableSelectedRange.minColIndex + 1;
90
105
  const canAddRowsCount = currentRowsCount + selectedRows > TABLE_MAX_ROWS ? TABLE_MAX_ROWS - currentRowsCount : selectedRows;
91
106
  const canAddColsCount = currentColumnsCount + selectedCols > TABLE_MAX_COLUMNS ? TABLE_MAX_COLUMNS - currentColumnsCount : selectedCols;
107
+ const enableCombineCell = !ObjectUtils.isSameObject(tableSelectedRange, EMPTY_SELECTED_RANGE);
108
+ const enableSplitCell = !enableCombineCell;
92
109
  return /*#__PURE__*/React.createElement(ElementPopover, {
93
110
  className: "sdoc-context-menu"
94
111
  }, /*#__PURE__*/React.createElement("div", {
@@ -121,7 +138,17 @@ class TableContextMenu extends React.Component {
121
138
  insertTableElement: this.insertTableElement
122
139
  }), /*#__PURE__*/React.createElement("div", {
123
140
  className: 'seafile-divider dropdown-divider'
124
- }), this.renderRemoveBtn(TABLE_ELEMENT.ROW, 'Delete_row'), this.renderRemoveBtn(TABLE_ELEMENT.COLUMN, 'Delete_column'), this.renderRemoveBtn(TABLE_ELEMENT.TABLE, 'Delete_table')));
141
+ }), this.renderRemoveBtn(TABLE_ELEMENT.ROW, 'Delete_row'), this.renderRemoveBtn(TABLE_ELEMENT.COLUMN, 'Delete_column'), this.renderRemoveBtn(TABLE_ELEMENT.TABLE, 'Delete_table'), /*#__PURE__*/React.createElement("div", {
142
+ className: 'seafile-divider dropdown-divider'
143
+ }), /*#__PURE__*/React.createElement("button", {
144
+ className: "dropdown-item",
145
+ disabled: !enableCombineCell,
146
+ onMouseDown: this.combineCells
147
+ }, t('Combine_cell')), /*#__PURE__*/React.createElement("button", {
148
+ className: "dropdown-item",
149
+ disabled: !enableSplitCell,
150
+ onMouseDown: this.toggleSplitCellSettingDialog
151
+ }, t('Split_cell'))));
125
152
  }
126
153
  }
127
154
  export default withTranslation('sdoc-editor')(TableContextMenu);
@@ -40,18 +40,16 @@
40
40
 
41
41
  .sdoc-table-wrapper .sdoc-table-container {
42
42
  width: fit-content;
43
- display: table;
43
+ display: grid;
44
44
  overflow: hidden;
45
45
  cursor: text;
46
46
  }
47
47
 
48
48
  .sdoc-table-wrapper .table-row {
49
- display: table-row ;
50
49
  height: auto;
51
50
  }
52
51
 
53
52
  .sdoc-table-wrapper .table-cell {
54
- display: table-cell;
55
53
  padding: 10px 10px;
56
54
  border-right: 1px solid #ccc;
57
55
  border-bottom: 1px solid #ccc;
@@ -59,14 +57,6 @@
59
57
  vertical-align: top;
60
58
  }
61
59
 
62
- .sdoc-table-wrapper .table-row:first-child .table-cell {
63
- border-top: 1px solid #ddd;
64
- }
65
-
66
- .sdoc-table-wrapper .table-row .table-cell:first-child {
67
- border-left: 1px solid #ddd;
68
- }
69
-
70
60
  .sdoc-table-wrapper .cell-selected {
71
61
  caret-color: transparent;
72
62
  position: relative;
@@ -1,3 +1,4 @@
1
+ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
1
2
  import React, { useRef, useState, useEffect, useCallback } from 'react';
2
3
  import classnames from 'classnames';
3
4
  import throttle from 'lodash.throttle';
@@ -7,7 +8,7 @@ import { EMPTY_SELECTED_RANGE, TABLE_ALTERNATE_HIGHLIGHT_CLASS_MAP } from '../co
7
8
  import { ResizeHandlersContext, TableSelectedRangeContext, SettingSelectRangeContext } from './hooks';
8
9
  import EventBus from '../../../../utils/event-bus';
9
10
  import { INTERNAL_EVENT } from '../../../../constants';
10
- import { getTableColumns, setTableSelectedRange, getFirstTableCell } from '../helpers';
11
+ import { getTableColumns, setTableSelectedRange, getFirstTableCell, getRowHeight } from '../helpers';
11
12
  import ObjectUtils from '../../../../utils/object-utils';
12
13
  import ResizeHandlers from './resize-handlers';
13
14
  import { registerResizeEvents, unregisterResizeEvents } from '../../../../utils/mouse-event';
@@ -17,7 +18,7 @@ import { findPath } from '../../../core';
17
18
  import './index.css';
18
19
  import './alternate-color.css';
19
20
  const Table = _ref => {
20
- var _element$style, _element$style2;
21
+ var _element$ui, _element$ui2;
21
22
  let {
22
23
  className,
23
24
  attributes,
@@ -29,6 +30,8 @@ const Table = _ref => {
29
30
  const table = useRef(null);
30
31
  const [startRowIndex, setStartRowIndex] = useState(0);
31
32
  const [startColIndex, setStartColIndex] = useState(0);
33
+ const [startRowSpan, setStartRowSpan] = useState(1);
34
+ const [startColSpan, setStartColSpan] = useState(1);
32
35
  const [isSettingSelectRange, setIsSettingSelectRange] = useState(false);
33
36
  const [selectedRange, setSelectedRange] = useState(EMPTY_SELECTED_RANGE);
34
37
  const oldColumns = getTableColumns(editor, element);
@@ -38,8 +41,11 @@ const Table = _ref => {
38
41
  if (event.button !== 0) return; // right click not deal
39
42
  setIsSettingSelectRange(true);
40
43
  const tableCell = getFirstTableCell(event.target);
44
+ const cellData = tableCell.style.gridArea.split(' / ');
41
45
  setStartRowIndex(Number(tableCell.getAttribute('row-index')));
42
46
  setStartColIndex(Number(tableCell.getAttribute('cell-index')));
47
+ setStartRowSpan(Number(cellData[2].split(' ')[1]));
48
+ setStartColSpan(Number(cellData[3].split(' ')[1]));
43
49
  setSelectedRange(EMPTY_SELECTED_RANGE);
44
50
  setTableSelectedRange(editor, EMPTY_SELECTED_RANGE);
45
51
 
@@ -49,6 +55,8 @@ const Table = _ref => {
49
55
  setIsSettingSelectRange(false);
50
56
  setStartColIndex(0);
51
57
  setStartRowIndex(0);
58
+ setStartRowSpan(1);
59
+ setStartColSpan(1);
52
60
 
53
61
  // eslint-disable-next-line react-hooks/exhaustive-deps
54
62
  }, []);
@@ -74,11 +82,14 @@ const Table = _ref => {
74
82
  const tableCell = getFirstTableCell(event.target);
75
83
  const endRowIndex = Number(tableCell.getAttribute('row-index'));
76
84
  const endColIndex = Number(tableCell.getAttribute('cell-index'));
85
+ const cellData = tableCell.style.gridArea.split(' / ');
86
+ const endRowSpan = Number(cellData[2].split(' ')[1]);
87
+ const endColSpan = Number(cellData[3].split(' ')[1]);
77
88
  const newSelectedRange = {
78
89
  minRowIndex: Math.min(startRowIndex, endRowIndex),
79
- maxRowIndex: Math.max(startRowIndex, endRowIndex),
90
+ maxRowIndex: startRowIndex < endRowIndex ? endRowIndex + endRowSpan - 1 : startRowIndex + startRowSpan - 1,
80
91
  minColIndex: Math.min(startColIndex, endColIndex),
81
- maxColIndex: Math.max(startColIndex, endColIndex)
92
+ maxColIndex: startColIndex < endColIndex ? endColIndex + endColSpan - 1 : startColIndex + startColSpan - 1
82
93
  };
83
94
  if (!ObjectUtils.isSameObject(selectedRange, EMPTY_SELECTED_RANGE)) {
84
95
  event.preventDefault();
@@ -97,14 +108,18 @@ const Table = _ref => {
97
108
  // same cell
98
109
  if (newSelectedRange.minRowIndex === newSelectedRange.maxRowIndex && newSelectedRange.minColIndex === newSelectedRange.maxColIndex) {
99
110
  setSelectedRange(EMPTY_SELECTED_RANGE);
111
+ setTableSelectedRange(editor, EMPTY_SELECTED_RANGE);
100
112
  return;
101
113
  }
102
114
  setSelectedRange(newSelectedRange);
115
+ setTableSelectedRange(editor, newSelectedRange);
103
116
  }, 200);
104
117
  const onMouseUp = event => {
118
+ /*
105
119
  if (!ObjectUtils.isSameObject(selectedRange, EMPTY_SELECTED_RANGE)) {
106
120
  setTableSelectedRange(editor, selectedRange);
107
121
  }
122
+ */
108
123
  resetState();
109
124
  };
110
125
  registerResizeEvents({
@@ -157,6 +172,11 @@ const Table = _ref => {
157
172
  'sdoc-table-selected': isSelected,
158
173
  'sdoc-table-selected-range': !ObjectUtils.isSameObject(selectedRange, EMPTY_SELECTED_RANGE)
159
174
  });
175
+ let style = element.style ? _objectSpread({}, element.style) : {};
176
+ const columnWidthList = columns.map(item => "".concat(item.width, "px"));
177
+ style.gridTemplateColumns = columnWidthList.join(' ');
178
+ const rowHeightList = element.children.map((item, index) => getRowHeight(item, index));
179
+ style.gridAutoRows = rowHeightList.map(item => "minmax(".concat(item, "px, auto)")).join(' ');
160
180
  return /*#__PURE__*/React.createElement(TableSelectedRangeContext.Provider, {
161
181
  value: selectedRange
162
182
  }, /*#__PURE__*/React.createElement(ResizeHandlersContext.Provider, {
@@ -171,8 +191,9 @@ const Table = _ref => {
171
191
  setSelectedRange: setSelectedRangeByClick
172
192
  }), /*#__PURE__*/React.createElement("div", {
173
193
  className: classnames(tableContainerClassName, {
174
- [TABLE_ALTERNATE_HIGHLIGHT_CLASS_MAP[element === null || element === void 0 ? void 0 : (_element$style = element.style) === null || _element$style === void 0 ? void 0 : _element$style.alternate_highlight_color]]: element === null || element === void 0 ? void 0 : (_element$style2 = element.style) === null || _element$style2 === void 0 ? void 0 : _element$style2.alternate_highlight
194
+ [TABLE_ALTERNATE_HIGHLIGHT_CLASS_MAP[element === null || element === void 0 ? void 0 : (_element$ui = element.ui) === null || _element$ui === void 0 ? void 0 : _element$ui.alternate_highlight_color]]: element === null || element === void 0 ? void 0 : (_element$ui2 = element.ui) === null || _element$ui2 === void 0 ? void 0 : _element$ui2.alternate_highlight
175
195
  }),
196
+ style: style,
176
197
  onMouseDown: onMouseDown,
177
198
  ref: table,
178
199
  "data-id": element.id
@@ -192,12 +213,19 @@ function renderTable(props) {
192
213
  } = props;
193
214
  // eslint-disable-next-line react-hooks/rules-of-hooks
194
215
  const editor = useSlateStatic();
216
+ const columns = getTableColumns(editor, element);
217
+ let style = element.style ? _objectSpread({}, element.style) : {};
218
+ const columnWidthList = columns.map(item => "".concat(item.width, "px"));
219
+ style.gridTemplateColumns = columnWidthList.join(' ');
220
+ const rowHeightList = element.children.map((item, index) => getRowHeight(item, index));
221
+ style.gridAutoRows = rowHeightList.map(item => "minmax(".concat(item, "px, auto)")).join(' ');
195
222
  return /*#__PURE__*/React.createElement(TableRoot, {
196
- columns: getTableColumns(editor, element),
223
+ columns: columns,
197
224
  attributes: attributes
198
225
  }, /*#__PURE__*/React.createElement("div", {
199
226
  className: classnames('sdoc-table-container', attributes.className, className),
200
- "data-id": element.id
227
+ "data-id": element.id,
228
+ style: style
201
229
  }, children));
202
230
  }
203
231
  return /*#__PURE__*/React.createElement(Table, props);
@@ -21,8 +21,6 @@ const TableCell = _ref => {
21
21
  const pathLength = cellPath.length;
22
22
  const rowIndex = cellPath[pathLength - 2];
23
23
  const cellIndex = cellPath[pathLength - 1];
24
- const column = columns ? columns[cellIndex] : {};
25
- const columnWidth = (column === null || column === void 0 ? void 0 : column.width) || 0;
26
24
  const {
27
25
  minColIndex,
28
26
  maxColIndex,
@@ -55,12 +53,22 @@ const TableCell = _ref => {
55
53
  style['backgroundColor'] = isSelected ? colorBlend(SELECTED_TABLE_CELL_BACKGROUND_COLOR, color, 0.9) : color;
56
54
  }
57
55
  }
56
+ if (element.is_combined) {
57
+ style.display = 'none';
58
+ }
59
+ if (rowIndex == 0) {
60
+ style.borderTop = '1px solid #ddd';
61
+ }
62
+ if (cellIndex == 0) {
63
+ style.borderLeft = '1px solid #ddd';
64
+ }
65
+ const {
66
+ rowspan = 1,
67
+ colspan = 1
68
+ } = element;
69
+ style.gridArea = "".concat(rowIndex + 1, " / ").concat(cellIndex + 1, " / span ").concat(rowspan, " / span ").concat(colspan);
58
70
  return /*#__PURE__*/React.createElement("div", Object.assign({}, attributes, {
59
- style: _objectSpread(_objectSpread(_objectSpread({}, element.style), style), {}, {
60
- minWidth: columnWidth,
61
- width: columnWidth,
62
- maxWidth: columnWidth
63
- }),
71
+ style: _objectSpread(_objectSpread({}, element.style), style),
64
72
  className: classnames('table-cell', attributes.className, {
65
73
  'cell-selected': isSelected,
66
74
  'cell-light-height-left-border': isSelectedFirstCell,
@@ -86,8 +94,11 @@ function renderTableCell(props) {
86
94
 
87
95
  // eslint-disable-next-line react-hooks/rules-of-hooks
88
96
  const editor = useSlateStatic();
89
- const cellPath = findPath(editor, element);
97
+ const cellPath = findPath(editor, element, [0, 0]);
90
98
  if (!cellPath) return null;
99
+ const pathLength = cellPath.length;
100
+ const rowIndex = cellPath[pathLength - 2];
101
+ const cellIndex = cellPath[pathLength - 1];
91
102
 
92
103
  // const cellValue = element;
93
104
  let style = attributes.style || {};
@@ -97,14 +108,22 @@ function renderTableCell(props) {
97
108
  if (ObjectUtils.hasProperty(element.style, TABLE_CELL_STYLE.BACKGROUND_COLOR) && element.style[TABLE_CELL_STYLE.BACKGROUND_COLOR]) {
98
109
  style['backgroundColor'] = element.style[TABLE_CELL_STYLE.BACKGROUND_COLOR];
99
110
  }
100
- const column = getCellColumn(editor, element);
101
- const width = column.width;
111
+ if (element.is_combined) {
112
+ style.display = 'none';
113
+ }
114
+ if (rowIndex == 0) {
115
+ style.borderTop = '1px solid #ddd';
116
+ }
117
+ if (cellIndex == 0) {
118
+ style.borderLeft = '1px solid #ddd';
119
+ }
120
+ const {
121
+ rowspan = 1,
122
+ colspan = 1
123
+ } = element;
124
+ style.gridArea = "".concat(rowIndex + 1, " / ").concat(cellIndex + 1, " / span ").concat(rowspan, " / span ").concat(colspan);
102
125
  return /*#__PURE__*/React.createElement("div", Object.assign({}, attributes, {
103
- style: _objectSpread(_objectSpread(_objectSpread({}, element.style), style), {}, {
104
- minWidth: width,
105
- width: width,
106
- maxWidth: width
107
- }),
126
+ style: _objectSpread(_objectSpread({}, element.style), style),
108
127
  className: classnames('table-cell', attributes.className),
109
128
  "data-id": element.id
110
129
  }), children);
@@ -1,121 +1,8 @@
1
- import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
- import React, { useEffect, useState, useRef, useCallback } from 'react';
3
- import classnames from 'classnames';
4
- import { useSlateStatic, useReadOnly } from '@seafile/slate-react';
5
- import { ReactEditor } from '@seafile/slate-react';
6
- import { findPath } from '../../../core';
7
- import { TABLE_ROW_MIN_HEIGHT } from '../constants';
8
- import { getRowHeight, updateTableRowHeight } from '../helpers';
9
- import { eventStopPropagation, getMouseDownInfo, getMouseMoveInfo, registerResizeEvents, unregisterResizeEvents } from '../../../../utils/mouse-event';
10
- import { useSettingSelectRangeContext, useResizeHandlersContext } from './hooks';
11
- import { useScrollContext } from '../../../../hooks/use-scroll-context';
12
- const TableRow = _ref => {
13
- let {
14
- element,
15
- attributes,
16
- children
17
- } = _ref;
18
- const editor = useSlateStatic();
19
- const isSettingSelectRange = useSettingSelectRangeContext();
20
- const columns = useResizeHandlersContext();
21
- const [isResizing, setIsResizing] = useState(false);
22
- const [mouseDownInfo, setMouseDownInfo] = useState({});
23
- const rowPath = findPath(editor, element);
24
- const pathLength = rowPath ? rowPath.length : 0;
25
- const rowIndex = rowPath ? rowPath[pathLength - 1] : -1;
26
- const rowHeight = getRowHeight(element, rowIndex);
27
- const tableRow = useRef(rowHeight);
28
- const [height, setHeight] = useState(rowHeight);
29
- const scrollContent = useScrollContext();
30
- const onMouseDown = useCallback(event => {
31
- eventStopPropagation(event);
32
- const mouseDownInfo = getMouseDownInfo(event, scrollContent.current);
33
- setMouseDownInfo(mouseDownInfo);
34
- setIsResizing(true);
35
-
36
- // eslint-disable-next-line react-hooks/exhaustive-deps
37
- }, []);
38
- useEffect(() => {
39
- if (!isResizing) return;
40
- const onMouseMove = event => {
41
- eventStopPropagation(event);
42
- const mouseMoveInfo = getMouseMoveInfo(event, mouseDownInfo, scrollContent.current);
43
- const newHeight = tableRow.current + mouseMoveInfo.displacementY;
44
- const validHeight = Math.max(TABLE_ROW_MIN_HEIGHT, newHeight);
45
- setHeight(validHeight);
46
- };
47
- const onMouseUp = event => {
48
- eventStopPropagation(event);
49
- setIsResizing(false);
50
- tableRow.current = height;
51
- updateTableRowHeight(editor, element, height);
52
- };
53
- registerResizeEvents({
54
- 'mousemove': onMouseMove,
55
- 'mouseup': onMouseUp
56
- });
57
- return () => {
58
- unregisterResizeEvents({
59
- 'mousemove': onMouseMove,
60
- 'mouseup': onMouseUp
61
- });
62
- };
63
-
64
- // eslint-disable-next-line react-hooks/exhaustive-deps
65
- }, [isResizing, mouseDownInfo, element, attributes, height]);
66
- useEffect(() => {
67
- const rowDom = ReactEditor.toDOMNode(editor, element);
68
- if (!rowDom) return;
69
- tableRow.current = rowDom.clientHeight;
70
-
71
- // eslint-disable-next-line react-hooks/exhaustive-deps
72
- }, [columns, element]);
73
- return /*#__PURE__*/React.createElement("div", Object.assign({}, attributes, {
74
- className: classnames('table-row position-relative', attributes.className),
75
- style: _objectSpread(_objectSpread({}, attributes.style), {}, {
76
- height: height,
77
- maxHeight: 'fit-content'
78
- }),
79
- "data-id": element.id
80
- }), children, !isSettingSelectRange && /*#__PURE__*/React.createElement("div", {
81
- className: classnames('table-row-height-just position-absolute', {
82
- 'resizing': isResizing
83
- }),
84
- contentEditable: false,
85
- onMouseDown: onMouseDown
86
- }, /*#__PURE__*/React.createElement("div", {
87
- className: "table-row-height-just-color-tip"
88
- })));
89
- };
1
+ import React from 'react';
90
2
  function renderTableRow(props) {
91
- // eslint-disable-next-line react-hooks/rules-of-hooks
92
- const readOnly = useReadOnly();
93
- if (readOnly) {
94
- const {
95
- attributes,
96
- children,
97
- element
98
- } = props;
99
-
100
- // eslint-disable-next-line react-hooks/rules-of-hooks
101
- const editor = useSlateStatic();
102
- const rowPath = findPath(editor, element);
103
- if (!rowPath) return null;
104
- const pathLength = rowPath.length;
105
- const rowIndex = rowPath[pathLength - 1];
106
- const {
107
- style = {}
108
- } = element;
109
- const height = getRowHeight(element, rowIndex);
110
- return /*#__PURE__*/React.createElement("div", Object.assign({}, attributes, {
111
- className: classnames('table-row', attributes.className),
112
- style: _objectSpread(_objectSpread(_objectSpread({}, attributes.style), style), {}, {
113
- height,
114
- maxHeight: 'fit-content'
115
- }),
116
- "data-id": element.id
117
- }), children);
118
- }
119
- return /*#__PURE__*/React.createElement(TableRow, props);
3
+ const {
4
+ children
5
+ } = props;
6
+ return /*#__PURE__*/React.createElement(React.Fragment, null, children);
120
7
  }
121
8
  export default renderTableRow;
@@ -6,7 +6,7 @@ import { useTableRootContext } from '../hooks';
6
6
  import { TABLE_CELL_MIN_WIDTH } from '../../constants';
7
7
  import { getTableColumns, updateColumnWidth } from '../../helpers';
8
8
  import { eventStopPropagation, getMouseDownInfo, getMouseMoveInfo, registerResizeEvents, unregisterResizeEvents } from '../../../../../utils/mouse-event';
9
- const ResizeHandler = _ref => {
9
+ const ColumnResizeHandler = _ref => {
10
10
  let {
11
11
  column,
12
12
  left: initLeft,
@@ -115,4 +115,4 @@ const ResizeHandler = _ref => {
115
115
  className: "table-cell-width-just-color-tip"
116
116
  }));
117
117
  };
118
- export default ResizeHandler;
118
+ export default ColumnResizeHandler;
@@ -1,10 +1,12 @@
1
1
  import React from 'react';
2
2
  import { useSlateStatic } from '@seafile/slate-react';
3
3
  import FirstColumnResizeHandler from './first-column-left-resize-handler';
4
- import ResizeHandler from './resize-handler';
4
+ import ColumnResizeHandler from './column-resize-handler';
5
+ import RowResizeHandler from './row-resize-handler';
5
6
  import { getNode, findPath } from '../../../../core';
6
7
  import { useResizeHandlersContext } from '../hooks';
7
- import { getTableColumns } from '../../helpers';
8
+ import { getRowHeight, getTableColumns } from '../../helpers';
9
+ import { TABLE_ROW_STYLE } from '../../constants';
8
10
  const ResizeHandlers = _ref => {
9
11
  let {
10
12
  element
@@ -15,8 +17,19 @@ const ResizeHandlers = _ref => {
15
17
  if (!tablePath) return null;
16
18
  const table = getNode(editor, tablePath);
17
19
  if (!table) return null;
20
+ const rows = element.children;
21
+ let rowBottom = 0;
18
22
  let columnLeft = 0;
19
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(FirstColumnResizeHandler, {
23
+ return /*#__PURE__*/React.createElement(React.Fragment, null, rows.map((row, index) => {
24
+ rowBottom += getRowHeight(row, index);
25
+ return /*#__PURE__*/React.createElement(RowResizeHandler, {
26
+ key: index,
27
+ row: row,
28
+ rowBottom: rowBottom,
29
+ index: index,
30
+ element: element
31
+ });
32
+ }), /*#__PURE__*/React.createElement(FirstColumnResizeHandler, {
20
33
  key: "column-0-left",
21
34
  column: columns[0],
22
35
  left: 0,
@@ -24,7 +37,7 @@ const ResizeHandlers = _ref => {
24
37
  element: element
25
38
  }), columns.map((column, columnIndex) => {
26
39
  columnLeft = columnLeft + column.width;
27
- return /*#__PURE__*/React.createElement(ResizeHandler, {
40
+ return /*#__PURE__*/React.createElement(ColumnResizeHandler, {
28
41
  key: columnIndex,
29
42
  column: column,
30
43
  left: columnLeft,
@@ -0,0 +1,89 @@
1
+ import React, { useEffect, useState, useRef, useCallback } from 'react';
2
+ import classnames from 'classnames';
3
+ import { useSlateStatic } from '@seafile/slate-react';
4
+ import { ReactEditor } from '@seafile/slate-react';
5
+ import { TABLE_ROW_MIN_HEIGHT } from '../../constants';
6
+ import { getRowHeight, updateTableRowHeight } from '../../helpers';
7
+ import { eventStopPropagation, getMouseDownInfo, getMouseMoveInfo, registerResizeEvents, unregisterResizeEvents } from '../../../../../utils/mouse-event';
8
+ import { useScrollContext } from '../../../../../hooks/use-scroll-context';
9
+ const RowResizeHandler = _ref => {
10
+ let {
11
+ row,
12
+ index,
13
+ rowBottom: initRowBottom,
14
+ element
15
+ } = _ref;
16
+ const editor = useSlateStatic();
17
+ const [rowBottom, setRowBottom] = useState(initRowBottom);
18
+ const [isResizing, setIsResizing] = useState(false);
19
+ const [mouseDownInfo, setMouseDownInfo] = useState({});
20
+ const [style, setStyle] = useState({});
21
+ const rowHeight = getRowHeight(row, index);
22
+ const tableRow = useRef(rowHeight);
23
+ const [height, setHeight] = useState(rowHeight);
24
+ const scrollContent = useScrollContext();
25
+ const onMouseDown = useCallback(event => {
26
+ eventStopPropagation(event);
27
+ const mouseDownInfo = getMouseDownInfo(event, scrollContent.current);
28
+ setMouseDownInfo(mouseDownInfo);
29
+ setStyle({
30
+ top: initRowBottom - 2.5
31
+ });
32
+ setIsResizing(true);
33
+
34
+ // eslint-disable-next-line react-hooks/exhaustive-deps
35
+ }, []);
36
+ useEffect(() => {
37
+ if (!isResizing) return;
38
+ const onMouseMove = event => {
39
+ eventStopPropagation(event);
40
+ const mouseMoveInfo = getMouseMoveInfo(event, mouseDownInfo, scrollContent.current);
41
+ const newHeight = tableRow.current + mouseMoveInfo.displacementY;
42
+ const validHeight = Math.max(TABLE_ROW_MIN_HEIGHT, newHeight);
43
+ setHeight(validHeight);
44
+ setStyle({
45
+ top: rowBottom - tableRow.current + validHeight - 2.5
46
+ });
47
+ };
48
+ const onMouseUp = event => {
49
+ eventStopPropagation(event);
50
+ setIsResizing(false);
51
+ tableRow.current = height;
52
+ updateTableRowHeight(editor, row, height);
53
+ };
54
+ registerResizeEvents({
55
+ 'mousemove': onMouseMove,
56
+ 'mouseup': onMouseUp
57
+ });
58
+ return () => {
59
+ unregisterResizeEvents({
60
+ 'mousemove': onMouseMove,
61
+ 'mouseup': onMouseUp
62
+ });
63
+ };
64
+
65
+ // eslint-disable-next-line react-hooks/exhaustive-deps
66
+ }, [isResizing, mouseDownInfo, rowBottom, element, height]);
67
+ useEffect(() => {
68
+ const cell = row.children.filter(cell => !cell.is_combined && (!cell.rowspan || cell.rowspan == 1))[0];
69
+ if (!cell) return;
70
+ const rowDom = ReactEditor.toDOMNode(editor, cell);
71
+ if (!rowDom) return;
72
+ tableRow.current = rowDom.clientHeight;
73
+ setRowBottom(initRowBottom - height + tableRow.current);
74
+ // eslint-disable-next-line react-hooks/exhaustive-deps
75
+ }, [row, initRowBottom]);
76
+ return /*#__PURE__*/React.createElement("div", {
77
+ className: classnames('table-row-height-just position-absolute', {
78
+ 'resizing': isResizing
79
+ }),
80
+ contentEditable: false,
81
+ style: isResizing ? style : {
82
+ top: rowBottom - 2.5
83
+ },
84
+ onMouseDown: onMouseDown
85
+ }, /*#__PURE__*/React.createElement("div", {
86
+ className: "table-row-height-just-color-tip"
87
+ }));
88
+ };
89
+ export default RowResizeHandler;
@@ -28,24 +28,23 @@ const RowHeader = _ref => {
28
28
  const currentCell = getSelectedNodeByType(editor, ELEMENT_TYPE.TABLE_CELL);
29
29
  const currentCellPath = currentCell ? findPath(editor, currentCell, [-1, -1]) : [-1, -1];
30
30
  const pathLength = currentCellPath.length;
31
+ const updateRowHeight = () => {
32
+ const cell = row.children.filter(cell => !cell.is_combined && (!cell.rowspan || cell.rowspan == 1))[0];
33
+ if (!cell) return;
34
+ const rowDom = ReactEditor.toDOMNode(editor, cell);
35
+ if (!rowDom) return;
36
+ if (rowHeight === rowDom.clientHeight) return;
37
+ setRowHeight(rowDom.clientHeight);
38
+ };
31
39
  useEffect(() => {
32
40
  if (elementHasImage(row)) {
33
- const time = setTimeout(() => {
34
- // There is a delay in image loading
35
- const rowDom = ReactEditor.toDOMNode(editor, row);
36
- if (!rowDom) return;
37
- if (rowHeight === rowDom.clientHeight) return;
38
- setRowHeight(rowDom.clientHeight);
39
- }, 300);
41
+ // There is a delay in image loading
42
+ const time = setTimeout(updateRowHeight, 300);
40
43
  return () => {
41
44
  clearTimeout(time);
42
45
  };
43
46
  }
44
- const rowDom = ReactEditor.toDOMNode(editor, row);
45
- if (!rowDom) return;
46
- if (rowHeight === rowDom.clientHeight) return;
47
- setRowHeight(rowDom.clientHeight);
48
-
47
+ updateRowHeight();
49
48
  // eslint-disable-next-line react-hooks/exhaustive-deps
50
49
  }, [columns, row, index]);
51
50
  const onMouseMove = useCallback(event => {
@@ -90,7 +89,7 @@ const RowHeader = _ref => {
90
89
  'range-selected-tip': !isSelectedARow && isSelectedSomeRow || currentCellPath[pathLength - 2] === index
91
90
  }),
92
91
  style: {
93
- height: index === 0 ? rowHeight - 1 : rowHeight
92
+ height: rowHeight + 1
94
93
  },
95
94
  ref: rowHeaderRef,
96
95
  onClick: () => selectRange(index),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seafile/sdoc-editor",
3
- "version": "0.3.16",
3
+ "version": "0.3.17",
4
4
  "private": false,
5
5
  "description": "This is a sdoc editor",
6
6
  "main": "dist/index.js",
@@ -417,5 +417,11 @@
417
417
  "Freezed": "Freezed",
418
418
  "Callout": "Callout",
419
419
  "The_current_location_does_not_support_pasting": "The current location does not support pasting ",
420
- "Please_enter...": "Please enter..."
420
+ "Please_enter...": "Please enter...",
421
+ "Combine_cell": "Combine cells",
422
+ "Split_cell": "Split cell",
423
+ "Row_number": "Row number",
424
+ "Column_number": "Column number",
425
+ "The_maximum_row_number_is_{number}": "The maximum row number is {number}",
426
+ "The_maximum_column_number_is_{number}": "The maximum column number is {number}"
421
427
  }
@@ -417,5 +417,11 @@
417
417
  "Freezed": "已冻结",
418
418
  "Callout": "高亮块",
419
419
  "The_current_location_does_not_support_pasting": "当前位置不支持粘贴",
420
- "Please_enter...": "请输入..."
420
+ "Please_enter...": "请输入...",
421
+ "Combine_cell": "合并单元格",
422
+ "Split_cell": "拆分单元格",
423
+ "Row_number": "行数",
424
+ "Column_number": "列数",
425
+ "The_maximum_row_number_is_{number}": "最大行数为 {number}",
426
+ "The_maximum_column_number_is_{number}": "最大列数为 {number}"
421
427
  }