@pie-lib/editable-html 10.0.0-beta.7 → 10.0.0

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 (118) hide show
  1. package/CHANGELOG.json +1 -1
  2. package/CHANGELOG.md +81 -0
  3. package/LICENSE.md +5 -0
  4. package/lib/editor.js +410 -543
  5. package/lib/editor.js.map +1 -1
  6. package/lib/index.js +200 -101
  7. package/lib/index.js.map +1 -1
  8. package/lib/parse-html.js +5 -6
  9. package/lib/parse-html.js.map +1 -1
  10. package/lib/plugins/characters/custom-popper.js +12 -2
  11. package/lib/plugins/characters/custom-popper.js.map +1 -1
  12. package/lib/plugins/characters/index.js +71 -19
  13. package/lib/plugins/characters/index.js.map +1 -1
  14. package/lib/plugins/characters/utils.js.map +1 -1
  15. package/lib/plugins/html/icons/index.js +38 -0
  16. package/lib/plugins/html/icons/index.js.map +1 -0
  17. package/lib/plugins/html/index.js +75 -0
  18. package/lib/plugins/html/index.js.map +1 -0
  19. package/lib/plugins/image/alt-dialog.js +26 -0
  20. package/lib/plugins/image/alt-dialog.js.map +1 -1
  21. package/lib/plugins/image/component.js +124 -90
  22. package/lib/plugins/image/component.js.map +1 -1
  23. package/lib/plugins/image/image-toolbar.js +45 -7
  24. package/lib/plugins/image/image-toolbar.js.map +1 -1
  25. package/lib/plugins/image/index.js +91 -113
  26. package/lib/plugins/image/index.js.map +1 -1
  27. package/lib/plugins/image/insert-image-handler.js +54 -72
  28. package/lib/plugins/image/insert-image-handler.js.map +1 -1
  29. package/lib/plugins/index.js +71 -31
  30. package/lib/plugins/index.js.map +1 -1
  31. package/lib/plugins/list/index.js +129 -58
  32. package/lib/plugins/list/index.js.map +1 -1
  33. package/lib/plugins/math/index.js +152 -118
  34. package/lib/plugins/math/index.js.map +1 -1
  35. package/lib/plugins/media/index.js +185 -168
  36. package/lib/plugins/media/index.js.map +1 -1
  37. package/lib/plugins/media/media-dialog.js +197 -110
  38. package/lib/plugins/media/media-dialog.js.map +1 -1
  39. package/lib/plugins/media/media-toolbar.js +24 -4
  40. package/lib/plugins/media/media-toolbar.js.map +1 -1
  41. package/lib/plugins/media/media-wrapper.js +65 -23
  42. package/lib/plugins/media/media-wrapper.js.map +1 -1
  43. package/lib/plugins/respArea/drag-in-the-blank/choice.js +50 -10
  44. package/lib/plugins/respArea/drag-in-the-blank/choice.js.map +1 -1
  45. package/lib/plugins/respArea/drag-in-the-blank/index.js +22 -9
  46. package/lib/plugins/respArea/drag-in-the-blank/index.js.map +1 -1
  47. package/lib/plugins/respArea/explicit-constructed-response/index.js +9 -4
  48. package/lib/plugins/respArea/explicit-constructed-response/index.js.map +1 -1
  49. package/lib/plugins/respArea/icons/index.js +18 -1
  50. package/lib/plugins/respArea/icons/index.js.map +1 -1
  51. package/lib/plugins/respArea/index.js +133 -122
  52. package/lib/plugins/respArea/index.js.map +1 -1
  53. package/lib/plugins/respArea/inline-dropdown/index.js +10 -4
  54. package/lib/plugins/respArea/inline-dropdown/index.js.map +1 -1
  55. package/lib/plugins/respArea/utils.js +33 -15
  56. package/lib/plugins/respArea/utils.js.map +1 -1
  57. package/lib/plugins/table/icons/index.js +7 -0
  58. package/lib/plugins/table/icons/index.js.map +1 -1
  59. package/lib/plugins/table/index.js +279 -390
  60. package/lib/plugins/table/index.js.map +1 -1
  61. package/lib/plugins/table/table-toolbar.js +47 -14
  62. package/lib/plugins/table/table-toolbar.js.map +1 -1
  63. package/lib/plugins/toolbar/default-toolbar.js +63 -51
  64. package/lib/plugins/toolbar/default-toolbar.js.map +1 -1
  65. package/lib/plugins/toolbar/done-button.js +9 -1
  66. package/lib/plugins/toolbar/done-button.js.map +1 -1
  67. package/lib/plugins/toolbar/editor-and-toolbar.js +140 -83
  68. package/lib/plugins/toolbar/editor-and-toolbar.js.map +1 -1
  69. package/lib/plugins/toolbar/index.js +5 -0
  70. package/lib/plugins/toolbar/index.js.map +1 -1
  71. package/lib/plugins/toolbar/toolbar-buttons.js +39 -8
  72. package/lib/plugins/toolbar/toolbar-buttons.js.map +1 -1
  73. package/lib/plugins/toolbar/toolbar.js +261 -225
  74. package/lib/plugins/toolbar/toolbar.js.map +1 -1
  75. package/lib/plugins/utils.js +16 -19
  76. package/lib/plugins/utils.js.map +1 -1
  77. package/lib/serialization.js +70 -11
  78. package/lib/serialization.js.map +1 -1
  79. package/lib/theme.js.map +1 -1
  80. package/package.json +18 -17
  81. package/src/editor.jsx +139 -434
  82. package/src/index.jsx +96 -62
  83. package/src/plugins/characters/index.jsx +17 -12
  84. package/src/plugins/html/icons/index.jsx +19 -0
  85. package/src/plugins/html/index.jsx +68 -0
  86. package/src/plugins/image/component.jsx +38 -60
  87. package/src/plugins/image/index.jsx +42 -95
  88. package/src/plugins/image/insert-image-handler.js +27 -62
  89. package/src/plugins/index.jsx +39 -21
  90. package/src/plugins/list/index.jsx +90 -62
  91. package/src/plugins/math/index.jsx +70 -93
  92. package/src/plugins/media/index.jsx +117 -146
  93. package/src/plugins/media/media-dialog.js +9 -10
  94. package/src/plugins/media/media-wrapper.jsx +27 -29
  95. package/src/plugins/respArea/drag-in-the-blank/index.jsx +4 -5
  96. package/src/plugins/respArea/explicit-constructed-response/index.jsx +1 -2
  97. package/src/plugins/respArea/index.jsx +84 -114
  98. package/src/plugins/respArea/inline-dropdown/index.jsx +2 -3
  99. package/src/plugins/respArea/utils.jsx +28 -23
  100. package/src/plugins/table/index.jsx +214 -334
  101. package/src/plugins/table/table-toolbar.jsx +4 -3
  102. package/src/plugins/toolbar/default-toolbar.jsx +30 -48
  103. package/src/plugins/toolbar/editor-and-toolbar.jsx +114 -114
  104. package/src/plugins/toolbar/toolbar.jsx +224 -254
  105. package/src/plugins/utils.js +0 -16
  106. package/src/serialization.jsx +1 -1
  107. package/lib/components.js +0 -92
  108. package/lib/components.js.map +0 -1
  109. package/lib/new-serialization.js +0 -280
  110. package/lib/new-serialization.js.map +0 -1
  111. package/lib/plugins/hotKeys/index.js +0 -60
  112. package/lib/plugins/hotKeys/index.js.map +0 -1
  113. package/lib/test-serializer.js +0 -138
  114. package/lib/test-serializer.js.map +0 -1
  115. package/src/components.js +0 -135
  116. package/src/new-serialization.jsx +0 -310
  117. package/src/plugins/hotKeys/index.js +0 -54
  118. package/src/test-serializer.js +0 -132
@@ -1,44 +1,44 @@
1
1
  import React from 'react';
2
- import { Node as SlateNode, Element as SlateElement, Editor, Transforms } from 'slate';
2
+ import EditTable from 'slate-edit-table';
3
+ import { Block } from 'slate';
3
4
  import debug from 'debug';
4
5
  import GridOn from '@material-ui/icons/GridOn';
5
6
  import TableToolbar from './table-toolbar';
6
7
  import PropTypes from 'prop-types';
7
- import { jsx } from 'slate-hyperscript';
8
- import { makeStyles } from '@material-ui/styles';
8
+ import SlatePropTypes from 'slate-prop-types';
9
+ import { withStyles } from '@material-ui/core/styles';
9
10
  import convert from 'react-attr-converter';
10
11
  import { object as toStyleObject } from 'to-style';
11
- import get from 'lodash/get';
12
- import omit from 'lodash/omit';
13
- import reduce from 'lodash/reduce';
14
12
 
15
13
  const log = debug('@pie-lib:editable-html:plugins:table');
16
14
 
17
- const Table = React.forwardRef((props) => {
18
- const nodeAttributes = omit(dataToAttributes(props.element.data), 'newTable');
19
- const attrs = omit(props.attributes, 'newTable');
15
+ const Table = withStyles(() => ({
16
+ table: {},
17
+ }))((props) => {
18
+ const nodeAttributes = dataToAttributes(props.node.data);
20
19
 
21
20
  return (
22
- <table {...attrs} {...nodeAttributes} onFocus={props.onFocus} onBlur={props.onBlur}>
23
- {props.children}
21
+ <table
22
+ className={props.classes.table}
23
+ {...props.attributes}
24
+ {...nodeAttributes}
25
+ onFocus={props.onFocus}
26
+ onBlur={props.onBlur}
27
+ >
28
+ <tbody>{props.children}</tbody>
24
29
  </table>
25
30
  );
26
31
  });
27
32
 
28
33
  Table.propTypes = {
29
34
  attributes: PropTypes.object,
30
- element: PropTypes.object,
31
35
  onFocus: PropTypes.func,
32
36
  onBlur: PropTypes.func,
33
- node: PropTypes.shape({
34
- type: PropTypes.string,
35
- children: PropTypes.array,
36
- data: PropTypes.object,
37
- }),
37
+ node: SlatePropTypes.node,
38
38
  children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
39
39
  };
40
40
 
41
- const TableRow = React.forwardRef((props) => <tr {...props.attributes}>{props.children}</tr>);
41
+ const TableRow = (props) => <tr {...props.attributes}>{props.children}</tr>;
42
42
 
43
43
  TableRow.propTypes = {
44
44
  attributes: PropTypes.object,
@@ -47,35 +47,22 @@ TableRow.propTypes = {
47
47
  children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
48
48
  };
49
49
 
50
- const TableBody = React.forwardRef((props) => <tbody {...props.attributes}>{props.children}</tbody>);
51
-
52
- TableBody.propTypes = {
53
- attributes: PropTypes.object,
54
- onFocus: PropTypes.func,
55
- onBlur: PropTypes.func,
56
- children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
57
- };
58
-
59
- const useCellStyles = makeStyles({
50
+ const TableCell = withStyles(() => ({
60
51
  td: {
61
52
  minWidth: '25px',
62
53
  },
63
- });
54
+ }))((props) => {
55
+ const Tag = props.node.data.get('header') ? 'th' : 'td';
64
56
 
65
- const TableCell = React.forwardRef((props) => {
66
- const classes = useCellStyles();
67
- const { node } = props;
68
- const Tag = get(node, 'data.header') ? 'th' : 'td';
69
-
70
- const nodeAttributes = dataToAttributes(props.element.data);
57
+ const nodeAttributes = dataToAttributes(props.node.data);
71
58
  delete nodeAttributes.header;
72
59
 
73
60
  return (
74
61
  <Tag
75
62
  {...props.attributes}
76
63
  {...nodeAttributes}
77
- colSpan={get(node, 'data.colspan')}
78
- className={classes[Tag]}
64
+ colSpan={props.node.data.get('colspan')}
65
+ className={props.classes[Tag]}
79
66
  onFocus={props.onFocus}
80
67
  onBlur={props.onBlur}
81
68
  >
@@ -85,163 +72,80 @@ const TableCell = React.forwardRef((props) => {
85
72
  });
86
73
 
87
74
  TableCell.propTypes = {
88
- node: PropTypes.object,
89
- element: PropTypes.object,
90
75
  attributes: PropTypes.object,
91
76
  onFocus: PropTypes.func,
92
77
  onBlur: PropTypes.func,
93
78
  children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
94
79
  };
95
80
 
96
- const getAncestorByType = (editor, type) => {
97
- if (!editor || !type) {
98
- return null;
99
- }
100
-
101
- const ancestors = SlateNode.ancestors(editor, Editor.path(editor, editor.selection), {
102
- reverse: true,
103
- });
81
+ export const moveFocusToBeginningOfTable = (change) => {
82
+ const addedTable = change.value.document.findDescendant((d) => !!d.data && !!d.data.get('newTable'));
104
83
 
105
- for (const [ancestor, ancestorPath] of ancestors) {
106
- if (ancestor.type === type) {
107
- return [ancestor, ancestorPath];
108
- }
84
+ if (!addedTable) {
85
+ return;
109
86
  }
110
87
 
111
- return null;
112
- };
113
-
114
- const moveToBeginningOfTable = (editor) => {
115
- const [tableBlock, tablePath] = getAncestorByType(editor, 'table');
116
- let firstTdPath;
88
+ change.collapseToStartOf(addedTable);
117
89
 
118
- for (const [descendant, descendantPath] of SlateNode.descendants(tableBlock, { reverse: true })) {
119
- if (descendant.type === 'td') {
120
- firstTdPath = descendantPath;
121
- }
122
- }
90
+ const update = addedTable.data.remove('newTable');
123
91
 
124
- Transforms.select(editor, [...tablePath, ...firstTdPath]);
92
+ change.setNodeByKey(addedTable.key, { data: update });
125
93
  };
126
94
 
127
- const TABLE_TYPES = ['tbody', 'tr', 'td', 'table'];
128
-
129
95
  export default (opts, toolbarPlugins /* : {toolbar: {}}[] */) => {
130
- const core = {
131
- utils: {},
132
- rules: (editor) => {
133
- const { normalizeNode } = editor;
134
-
135
- editor.normalizeNode = (entry) => {
136
- const [tableNode, tablePath] = entry;
137
- const tableParent = SlateNode.get(editor, tablePath.slice(0, -1));
138
-
139
- // If the element is a paragraph, ensure its children are valid.
140
- if (SlateElement.isElement(tableNode) && tableNode.type === 'table') {
141
- const emptyBlock = {
142
- type: 'paragraph',
143
- children: [{ text: '' }],
144
- };
145
- const tableIndex = tablePath.slice(-1)[0];
146
-
147
- // if table is the first element, we need to add a space before
148
- // so users can focus before the table
149
- if (tableIndex === 0) {
150
- const beforeTablePath = [...tablePath.slice(0, -1), 0];
151
-
152
- editor.apply({
153
- type: 'insert_node',
154
- path: beforeTablePath,
155
- node: emptyBlock,
156
- });
157
- editor.continueNormalization();
158
- return;
159
- }
160
-
161
- // if table is the last element, we add element after it
162
- if (tableParent.children.length - 1 === tableIndex) {
163
- const afterTablePath = [...tablePath.slice(0, -1), tableIndex + 1];
164
-
165
- editor.apply({
166
- type: 'insert_node',
167
- path: afterTablePath,
168
- node: emptyBlock,
169
- });
170
- editor.continueNormalization();
171
- return;
172
- }
173
-
174
- // if table does not have a tbody, we add it
175
- if (tableNode.children[0].type !== 'tbody') {
176
- const tBodyNode = { type: 'tbody', children: [] };
177
-
178
- Transforms.wrapNodes(editor, tBodyNode, {
179
- at: {
180
- anchor: { path: [...tablePath, 0], offset: 0 },
181
- focus: { path: [...tablePath, tableNode.children.length], offset: 0 },
182
- },
183
- });
184
- editor.continueNormalization();
185
- return;
186
- }
187
- }
96
+ const core = EditTable({
97
+ typeContent: 'div',
98
+ });
188
99
 
189
- // Fall back to the original `normalizeNode` to enforce other constraints.
190
- normalizeNode(entry);
191
- };
100
+ // fix outdated schema
192
101
 
193
- return editor;
194
- },
195
- };
102
+ if (core.schema && core.schema.blocks) {
103
+ Object.keys(core.schema.blocks).forEach((key) => {
104
+ const block = core.schema.blocks[key];
196
105
 
197
- core.utils.createTable = (row = 2, columns = 2) => {
198
- const tableRows = [];
199
- const rowLength = new Array(row).fill(0).length;
200
- const columnsLength = new Array(columns).fill(0).length;
106
+ if (block.parent) {
107
+ if (block.nodes[0].types) {
108
+ block.nodes[0] = {
109
+ type: block.nodes[0].types[0],
110
+ };
111
+ }
201
112
 
202
- for (let i = 0; i < rowLength; i++) {
203
- const tableRow = { type: 'tr', children: [] };
113
+ if (block.nodes[0].objects) {
114
+ block.nodes[0] = {
115
+ object: block.nodes[0].objects[0],
116
+ };
117
+ }
204
118
 
205
- for (let j = 0; j < columnsLength; j++) {
206
- tableRow.children.push({
207
- type: 'td',
208
- children: [
209
- {
210
- text: '',
211
- },
212
- ],
213
- });
119
+ block.parent = {
120
+ type: block.parent.types[0],
121
+ };
122
+ } else {
123
+ block.nodes[0] = { type: block.nodes[0].types[0] };
214
124
  }
125
+ });
126
+ }
215
127
 
216
- tableRows.push(tableRow);
217
- }
218
-
219
- return {
220
- type: 'table',
221
- children: [
222
- {
223
- type: 'tbody',
224
- children: tableRows,
225
- },
226
- ],
227
- };
128
+ core.utils.getTableBlock = (containerNode, key) => {
129
+ const node = containerNode.getDescendant(key);
130
+ const ancestors = containerNode.getAncestors(key).push(node);
131
+ return ancestors.findLast((p) => p.type === 'table');
228
132
  };
229
133
 
230
- core.utils.getTableBlock = (editor) => getAncestorByType(editor, 'table');
231
-
232
- core.utils.isSelectionInTable = (editor) => !!core.utils.getTableBlock(editor);
233
-
234
134
  core.utils.createTableWithOptions = (row, columns, extra) => {
235
135
  const createdTable = core.utils.createTable(row, columns);
236
- const newTable = { ...createdTable, ...extra };
136
+ const newTable = Block.create({
137
+ ...createdTable.toJSON(),
138
+ ...extra,
139
+ });
237
140
 
238
141
  return newTable;
239
142
  };
240
143
 
241
144
  core.toolbar = {
242
145
  icon: <GridOn />,
243
- onClick: (editor) => {
146
+ onClick: (value, onChange) => {
244
147
  log('insert table');
148
+ const change = value.change();
245
149
  const newTable = core.utils.createTableWithOptions(2, 2, {
246
150
  data: {
247
151
  border: '1',
@@ -249,145 +153,65 @@ export default (opts, toolbarPlugins /* : {toolbar: {}}[] */) => {
249
153
  },
250
154
  });
251
155
 
252
- editor.insertNode(newTable);
253
- moveToBeginningOfTable(editor, newTable);
156
+ change.insertBlock(newTable);
157
+
158
+ moveFocusToBeginningOfTable(change);
159
+ onChange(change);
254
160
  },
161
+ supports: (node, value) => node && node.object === 'block' && core.utils.isSelectionInTable(value),
255
162
  /**
256
163
  * Note - the node may not be a table node - it may be a node inside a table.
257
164
  */
258
- customToolbar: (node, nodePath, editor, onToolbarDone) => {
165
+ customToolbar: (node, value, onToolbarDone) => {
259
166
  log('[customToolbar] node.data: ', node.data);
260
167
 
261
- const [tableBlock] = core.utils.getTableBlock(editor);
168
+ const tableBlock = core.utils.getTableBlock(value.document, node?.key);
262
169
  log('[customToolbar] tableBlock: ', tableBlock);
263
170
 
264
- const hasBorder = () => get(tableBlock, 'data.border') !== '0';
171
+ const hasBorder = () => tableBlock.data.get('border') && tableBlock.data.get('border') !== '0';
265
172
  const addRow = () => {
266
- const [trNode, trPath] = getAncestorByType(editor, 'tr');
267
-
268
- log('[addRow]');
269
-
270
- if (trNode) {
271
- const newTr = { type: 'tr', children: [] };
272
- const columnsLength = trNode.children.length;
273
-
274
- for (let i = 0; i < columnsLength; i++) {
275
- newTr.children.push({
276
- type: 'td',
277
- children: [
278
- {
279
- text: '',
280
- },
281
- ],
282
- });
283
- }
284
-
285
- Transforms.insertNodes(editor, [newTr], { at: trPath });
286
- }
287
- };
288
-
289
- const removeRow = () => {
290
- const [tBodyNode, tBodyPath] = getAncestorByType(editor, 'tbody');
291
-
292
- log('[removeRow]');
293
-
294
- if (tBodyPath) {
295
- if (tBodyNode.children.length > 1) {
296
- const [, trPath] = getAncestorByType(editor, 'tr');
297
-
298
- log('[removeRow]');
299
-
300
- if (trPath) {
301
- Transforms.removeNodes(editor, { at: trPath });
302
- }
303
- }
304
- }
173
+ const change = core.changes.insertRow(value.change());
174
+ onToolbarDone(change, false);
305
175
  };
306
176
 
307
177
  const addColumn = () => {
308
- const [tBodyNode, tBodyPath] = getAncestorByType(editor, 'tbody');
309
-
310
- log('[addColumn]');
311
-
312
- if (tBodyNode) {
313
- const emptyTd = {
314
- type: 'td',
315
- children: [{ text: '' }],
316
- };
317
- const trElements = Editor.nodes(editor, {
318
- at: tBodyPath, // Path of Editor
319
- match: (node) => 'tr' === node.type,
320
- });
178
+ const change = core.changes.insertColumn(value.change());
179
+ onToolbarDone(change, false);
180
+ };
321
181
 
322
- for (const [trNode, nodePath] of trElements) {
323
- Transforms.insertNodes(editor, [emptyTd], {
324
- at: [...nodePath, trNode.children.length],
325
- });
326
- }
327
- }
182
+ const removeRow = () => {
183
+ const change = core.changes.removeRow(value.change());
184
+ onToolbarDone(change, false);
328
185
  };
329
186
 
330
187
  const removeColumn = () => {
331
- const [tBodyNode, tBodyPath] = getAncestorByType(editor, 'tbody');
332
-
333
- log('[addColumn]');
334
-
335
- if (tBodyNode) {
336
- const currentPath = Editor.path(editor, editor.selection);
337
- const columnIndex = currentPath[currentPath.length - 2];
338
- const trElements = Editor.nodes(editor, {
339
- at: tBodyPath, // Path of Editor
340
- match: (node) => 'tr' === node.type,
341
- });
342
-
343
- for (const [trNode, nodePath] of trElements) {
344
- if (trNode.children.length > 1) {
345
- Transforms.removeNodes(editor, { at: [...nodePath, columnIndex] });
346
- }
347
- }
348
- }
188
+ const change = core.changes.removeColumn(value.change());
189
+ onToolbarDone(change, false);
349
190
  };
350
191
 
351
192
  const removeTable = () => {
352
- const [tableNode, tablePath] = getAncestorByType(editor, 'table');
353
-
354
- editor.apply({
355
- type: 'remove_node',
356
- path: tablePath,
357
- node: tableNode,
358
- });
193
+ const change = core.changes.removeTable(value.change());
194
+ onToolbarDone(change, false);
359
195
  };
360
196
 
361
197
  const toggleBorder = () => {
362
198
  const { data } = tableBlock;
363
- const update = {
364
- ...data,
365
- border: hasBorder() ? '0' : '1',
366
- };
367
- const [, tablePath] = getAncestorByType(editor, 'table');
368
-
199
+ const update = data.set('border', hasBorder() ? '0' : '1');
369
200
  log('[toggleBorder] update: ', update);
370
-
371
- editor.apply({
372
- type: 'set_node',
373
- path: tablePath,
374
- properties: {
375
- data: node.data,
376
- },
377
- newProperties: { data: update },
378
- });
201
+ const change = value.change().setNodeByKey(tableBlock.key, { data: update });
202
+ onToolbarDone(change, false);
379
203
  };
380
204
 
381
205
  const onDone = () => {
382
206
  log('[onDone] call onToolbarDone...');
383
- onToolbarDone(true);
207
+ onToolbarDone(null, true);
384
208
  };
385
209
 
386
210
  const Tb = () => (
387
211
  <TableToolbar
388
- editor={editor}
389
212
  plugins={toolbarPlugins}
390
213
  onChange={(c) => onToolbarDone(c, false)}
214
+ value={value}
391
215
  onAddRow={addRow}
392
216
  onRemoveRow={removeRow}
393
217
  onAddColumn={addColumn}
@@ -402,17 +226,13 @@ export default (opts, toolbarPlugins /* : {toolbar: {}}[] */) => {
402
226
  },
403
227
  };
404
228
 
405
- core.supports = (node) => TABLE_TYPES.includes(node.type);
406
-
407
229
  const Node = (props) => {
408
230
  switch (props.node.type) {
409
231
  case 'table':
410
232
  return <Table {...props} onFocus={opts.onFocus} onBlur={opts.onBlur} />;
411
- case 'tbody':
412
- return <TableBody {...props} onFocus={opts.onFocus} onBlur={opts.onBlur} />;
413
- case 'tr':
233
+ case 'table_row':
414
234
  return <TableRow {...props} />;
415
- case 'td':
235
+ case 'table_cell':
416
236
  return <TableCell {...props} onFocus={opts.onFocus} onBlur={opts.onBlur} />;
417
237
  default:
418
238
  return null;
@@ -422,6 +242,74 @@ export default (opts, toolbarPlugins /* : {toolbar: {}}[] */) => {
422
242
  node: PropTypes.object,
423
243
  };
424
244
 
245
+ core.normalizeNode = (node) => {
246
+ if (node.object !== 'document') {
247
+ return;
248
+ }
249
+
250
+ const tableAdded = node.findDescendant((d) => d.data && d.data.get('newTable'));
251
+
252
+ if (!tableAdded) {
253
+ return;
254
+ }
255
+
256
+ const nodeToSearch = node.getParent(tableAdded.key) || node;
257
+ let shouldAddTextAfterNode = false;
258
+ const indexToNotHaveTableOn = nodeToSearch.nodes.size - 1;
259
+ const indexOfLastTable = nodeToSearch.nodes.findLastIndex((d) => d.type === 'table');
260
+
261
+ // if the last table in the document is of type table, we need to do the change
262
+ if (indexOfLastTable === indexToNotHaveTableOn) {
263
+ shouldAddTextAfterNode = true;
264
+ }
265
+
266
+ if (!shouldAddTextAfterNode) {
267
+ return;
268
+ }
269
+
270
+ return (change) => {
271
+ if (shouldAddTextAfterNode) {
272
+ const tableJSON = tableAdded.toJSON();
273
+
274
+ // we remove the table node because otherwise we can't add the empty block after it
275
+ // we need a block that contains text in order to do it
276
+ change.removeNodeByKey(tableAdded.key);
277
+
278
+ const newBlock = Block.create({
279
+ object: 'block',
280
+ type: 'div',
281
+ });
282
+
283
+ // we add an empty block but that it's going to be normalized
284
+ // because it will add the empty text to it like it should
285
+ change.insertBlock(newBlock);
286
+
287
+ change.withoutNormalization(() => {
288
+ // we do these changes without normalization
289
+
290
+ // we get the text previous to the new block added
291
+ const prevText = change.value.document.getPreviousText(newBlock.key);
292
+
293
+ if (prevText) {
294
+ // we move focus to the previous text
295
+ change.moveFocusTo(prevText.key, prevText.text?.length).moveAnchorTo(prevText.key, prevText.text?.length);
296
+ }
297
+
298
+ // we insert the table block between the first block with text and the last block with text
299
+ change.insertBlock({
300
+ ...tableJSON,
301
+ data: {
302
+ ...tableJSON.data,
303
+ newTable: true,
304
+ },
305
+ });
306
+
307
+ moveFocusToBeginningOfTable(change);
308
+ });
309
+ }
310
+ };
311
+ };
312
+
425
313
  core.renderNode = Node;
426
314
 
427
315
  return core;
@@ -454,16 +342,16 @@ const attributesToMap = (el) => (acc, attribute) => {
454
342
  };
455
343
 
456
344
  const dataToAttributes = (data) => {
457
- return reduce(
458
- data,
459
- (acc, v, name) => {
460
- if (v) {
461
- acc[convert(name)] = v;
462
- }
463
- return acc;
464
- },
465
- {},
466
- );
345
+ if (!data || !data.get) {
346
+ return {};
347
+ }
348
+
349
+ return data.reduce((acc, v, name) => {
350
+ if (v) {
351
+ acc[convert(name)] = v;
352
+ }
353
+ return acc;
354
+ }, {});
467
355
  };
468
356
 
469
357
  const attributes = ['border', 'cellpadding', 'cellspacing', 'class', 'style'];
@@ -482,78 +370,70 @@ export const serialization = {
482
370
  : el.children;
483
371
  const c = Array.from(children);
484
372
 
485
- return jsx(
486
- 'element',
487
- {
488
- type: 'table',
489
- data: attributes.reduce(attributesToMap(el), {}),
490
- },
491
- next(c),
492
- );
493
- }
494
- case 'tbody': {
495
- return jsx(
496
- 'element',
497
- {
498
- type: 'tbody',
499
- },
500
- next(el.childNodes),
501
- );
373
+ return {
374
+ object: 'block',
375
+ type: 'table',
376
+ nodes: next(c),
377
+ data: attributes.reduce(attributesToMap(el), {}),
378
+ };
502
379
  }
503
380
 
504
381
  case 'th': {
505
- return jsx(
506
- 'element',
507
- {
508
- type: 'th',
509
- data: cellAttributes.reduce(attributesToMap(el), { header: true }),
510
- },
511
- next(el.childNodes),
512
- );
382
+ return {
383
+ object: 'block',
384
+ type: 'table_cell',
385
+ nodes: next(el.childNodes),
386
+ data: cellAttributes.reduce(attributesToMap(el), { header: true }),
387
+ };
513
388
  }
514
389
 
515
390
  case 'tr': {
516
- return jsx(
517
- 'element',
518
- {
519
- type: 'tr',
520
- },
521
- next(Array.from(el.children)),
522
- );
391
+ return {
392
+ object: 'block',
393
+ type: 'table_row',
394
+ nodes: next(Array.from(el.children)),
395
+ };
523
396
  }
524
397
 
525
398
  case 'td': {
526
- return jsx(
527
- 'element',
528
- {
529
- type: 'td',
530
- data: cellAttributes.reduce(attributesToMap(el), { header: true }),
531
- },
532
- next(el.childNodes),
533
- );
399
+ return {
400
+ object: 'block',
401
+ type: 'table_cell',
402
+ nodes: next(Array.from(el.childNodes)),
403
+ data: cellAttributes.reduce(attributesToMap(el), { header: false }),
404
+ };
534
405
  }
535
406
  }
536
407
  },
537
408
  serialize(object, children) {
409
+ if (object.object !== 'block') {
410
+ return;
411
+ }
412
+
538
413
  switch (object.type) {
539
414
  case 'table': {
540
415
  const attributes = dataToAttributes(object.data);
541
416
 
542
- return <table {...attributes}>{children}</table>;
543
- }
544
- case 'tbody': {
545
- return <tbody>{children}</tbody>;
417
+ return (
418
+ <table {...attributes}>
419
+ <tbody>{children}</tbody>
420
+ </table>
421
+ );
546
422
  }
547
- case 'tr': {
423
+
424
+ case 'table_row': {
548
425
  return <tr>{children}</tr>;
549
426
  }
550
- case 'td': {
551
- const attributes = dataToAttributes(object.data);
552
- return <td {...attributes}>{children}</td>;
553
- }
554
- case 'th': {
427
+
428
+ case 'table_cell': {
555
429
  const attributes = dataToAttributes(object.data);
556
- return <th {...attributes}>{children}</th>;
430
+ delete attributes.header;
431
+
432
+ if (object.data.get('header')) {
433
+ return <th {...attributes}>{children}</th>;
434
+ } else {
435
+ return <td {...attributes}>{children}</td>;
436
+ }
557
437
  }
558
438
  }
559
439
  },