@seafile/sdoc-editor 0.3.15 → 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.
- package/dist/assets/css/simple-editor.css +1 -1
- package/dist/basic-sdk/extension/commons/insert-element-dialog/index.js +5 -1
- package/dist/basic-sdk/extension/constants/index.js +1 -1
- package/dist/basic-sdk/extension/constants/menus-config.js +6 -0
- package/dist/basic-sdk/extension/core/queries/index.js +5 -2
- package/dist/basic-sdk/extension/plugins/table/constants/index.js +1 -1
- package/dist/basic-sdk/extension/plugins/table/dialogs/index.js +3 -0
- package/dist/basic-sdk/extension/plugins/table/dialogs/split-cell-setting-dialog.js +99 -0
- package/dist/basic-sdk/extension/plugins/table/helpers.js +324 -1
- package/dist/basic-sdk/extension/plugins/table/menu/active-table-menu/combine-cells.js +32 -0
- package/dist/basic-sdk/extension/plugins/table/menu/active-table-menu/index.css +0 -4
- package/dist/basic-sdk/extension/plugins/table/menu/active-table-menu/index.js +6 -0
- package/dist/basic-sdk/extension/plugins/table/menu/table-context-menu/index.js +31 -4
- package/dist/basic-sdk/extension/plugins/table/render/index.css +1 -11
- package/dist/basic-sdk/extension/plugins/table/render/index.js +35 -7
- package/dist/basic-sdk/extension/plugins/table/render/render-cell.js +34 -15
- package/dist/basic-sdk/extension/plugins/table/render/render-row.js +5 -118
- package/dist/basic-sdk/extension/plugins/table/render/resize-handlers/{resize-handler.js → column-resize-handler.js} +2 -2
- package/dist/basic-sdk/extension/plugins/table/render/resize-handlers/index.js +17 -4
- package/dist/basic-sdk/extension/plugins/table/render/resize-handlers/row-resize-handler.js +89 -0
- package/dist/basic-sdk/extension/plugins/table/render/table-header/rows-header/row-header.js +12 -13
- package/package.json +1 -1
- package/public/locales/en/sdoc-editor.json +7 -1
- package/public/locales/zh_CN/sdoc-editor.json +7 -1
- /package/dist/basic-sdk/extension/plugins/table/{dialog → dialogs}/custom-table-size-dialog/index.css +0 -0
- /package/dist/basic-sdk/extension/plugins/table/{dialog → dialogs}/custom-table-size-dialog/index.js +0 -0
- /package/dist/basic-sdk/extension/plugins/table/{dialog → dialogs}/custom-table-size-dialog/number-input.js +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useState, useRef } from 'react';
|
|
2
|
-
import CustomTableSizeDialog from '../../plugins/table/
|
|
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',
|
|
@@ -218,14 +218,17 @@ export const getAboveBlockNode = (editor, options) => {
|
|
|
218
218
|
}));
|
|
219
219
|
};
|
|
220
220
|
export const getPrevNode = editor => {
|
|
221
|
-
const
|
|
221
|
+
const lowerNodeEntry = getAboveNode(editor, {
|
|
222
222
|
mode: 'lowest',
|
|
223
223
|
match: n => Element.isElement(n) && Editor.isBlock(editor, n)
|
|
224
224
|
});
|
|
225
|
-
const
|
|
225
|
+
const highNodeEntry = getAboveNode(editor, {
|
|
226
226
|
mode: 'highest',
|
|
227
227
|
match: n => Element.isElement(n) && Editor.isBlock(editor, n)
|
|
228
228
|
});
|
|
229
|
+
if (!lowerNodeEntry || !highNodeEntry) return null;
|
|
230
|
+
const [heightNode, heightPath] = highNodeEntry;
|
|
231
|
+
const [lowerNode, lowerPath] = lowerNodeEntry;
|
|
229
232
|
let prevNode = null;
|
|
230
233
|
try {
|
|
231
234
|
prevNode = Editor.previous(editor, {
|
|
@@ -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
|
-
|
|
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;
|
|
@@ -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,
|