@pie-lib/editable-html 10.0.0-beta.6 → 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 +140 -450
  82. package/src/index.jsx +96 -62
  83. package/src/plugins/characters/index.jsx +18 -14
  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 +41 -67
  87. package/src/plugins/image/index.jsx +43 -108
  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 +91 -66
  91. package/src/plugins/math/index.jsx +71 -84
  92. package/src/plugins/media/index.jsx +118 -147
  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 +7 -10
  96. package/src/plugins/respArea/explicit-constructed-response/index.jsx +2 -3
  97. package/src/plugins/respArea/index.jsx +90 -138
  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 +216 -340
  101. package/src/plugins/table/table-toolbar.jsx +5 -9
  102. package/src/plugins/toolbar/default-toolbar.jsx +31 -51
  103. package/src/plugins/toolbar/editor-and-toolbar.jsx +114 -121
  104. package/src/plugins/toolbar/toolbar.jsx +224 -258
  105. package/src/plugins/utils.js +2 -19
  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
- }),
38
- children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired
37
+ node: SlatePropTypes.node,
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
- minWidth: '25px'
62
- }
63
- });
64
-
65
- const TableCell = React.forwardRef(props => {
66
- const classes = useCellStyles();
67
- const { node } = props;
68
- const Tag = get(node, 'data.header') ? 'th' : 'td';
52
+ minWidth: '25px',
53
+ },
54
+ }))((props) => {
55
+ const Tag = props.node.data.get('header') ? 'th' : 'td';
69
56
 
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
- };
88
+ change.collapseToStartOf(addedTable);
113
89
 
114
- const moveToBeginningOfTable = editor => {
115
- const [tableBlock, tablePath] = getAncestorByType(editor, 'table');
116
- let firstTdPath;
90
+ const update = addedTable.data.remove('newTable');
117
91
 
118
- for (const [descendant, descendantPath] of SlateNode.descendants(tableBlock, { reverse: true })) {
119
- if (descendant.type === 'td') {
120
- firstTdPath = descendantPath;
121
- }
122
- }
123
-
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
- }
96
+ const core = EditTable({
97
+ typeContent: 'div',
98
+ });
160
99
 
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
- }
100
+ // fix outdated schema
173
101
 
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
- }
102
+ if (core.schema && core.schema.blocks) {
103
+ Object.keys(core.schema.blocks).forEach((key) => {
104
+ const block = core.schema.blocks[key];
188
105
 
189
- // Fall back to the original `normalizeNode` to enforce other constraints.
190
- normalizeNode(entry);
191
- };
106
+ if (block.parent) {
107
+ if (block.nodes[0].types) {
108
+ block.nodes[0] = {
109
+ type: block.nodes[0].types[0],
110
+ };
111
+ }
192
112
 
193
- return editor;
194
- }
195
- };
113
+ if (block.nodes[0].objects) {
114
+ block.nodes[0] = {
115
+ object: block.nodes[0].objects[0],
116
+ };
117
+ }
196
118
 
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;
201
-
202
- for (let i = 0; i < rowLength; i++) {
203
- const tableRow = { type: 'tr', children: [] };
204
-
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
- onChange={c => onToolbarDone(c, false)}
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
- const Node = props => {
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;
@@ -453,17 +341,17 @@ const attributesToMap = (el) => (acc, attribute) => {
453
341
  return acc;
454
342
  };
455
343
 
456
- 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
- );
344
+ const dataToAttributes = (data) => {
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,82 +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
417
  return (
543
418
  <table {...attributes}>
544
- {children}
419
+ <tbody>{children}</tbody>
545
420
  </table>
546
421
  );
547
422
  }
548
- case 'tbody': {
549
- return <tbody>{children}</tbody>;
550
- }
551
- case 'tr': {
423
+
424
+ case 'table_row': {
552
425
  return <tr>{children}</tr>;
553
426
  }
554
- case 'td': {
555
- const attributes = dataToAttributes(object.data);
556
- return <td {...attributes}>{children}</td>;
557
- }
558
- case 'th': {
427
+
428
+ case 'table_cell': {
559
429
  const attributes = dataToAttributes(object.data);
560
- 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
+ }
561
437
  }
562
438
  }
563
439
  },