@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,15 +1,14 @@
1
- import React from 'react';
2
- import { Editor, Inline, Transforms } from 'slate';
3
- import { ReactEditor } from 'slate-react';
4
- import { jsx } from 'slate-hyperscript';
5
1
  import Functions from '@material-ui/icons/Functions';
2
+ import { Inline } from 'slate';
6
3
  import { MathPreview, MathToolbar } from '@pie-lib/math-toolbar';
7
4
  import { wrapMath, unWrapMath, mmlToLatex, renderMath } from '@pie-lib/math-rendering';
5
+ import React from 'react';
8
6
  import debug from 'debug';
7
+ import SlatePropTypes from 'slate-prop-types';
9
8
  import PropTypes from 'prop-types';
10
- import isEqual from 'lodash/isEqual';
11
9
 
12
- import { BLOCK_TAGS } from '../../new-serialization';
10
+ import { BLOCK_TAGS } from '../../serialization';
11
+ import isEqual from 'lodash/isEqual';
13
12
  const log = debug('@pie-lib:editable-html:plugins:math');
14
13
 
15
14
  const TEXT_NODE = 3;
@@ -26,35 +25,43 @@ function generateAdditionalKeys(keyData = []) {
26
25
  // eslint-disable-next-line react/display-name
27
26
  export const CustomToolbarComp = React.memo(
28
27
  (props) => {
29
- const { node, nodePath, onFocus, onBlur, onClick, editor } = props;
28
+ const { node, value, onFocus, onBlur, onClick } = props;
30
29
  const { pluginProps } = props || {};
31
30
  const { math } = pluginProps || {};
32
31
  const { keypadMode, customKeys, controlledKeypadMode = true } = math || {};
33
32
 
34
33
  const onDone = (latex) => {
35
34
  const update = {
36
- ...node.data,
37
- latex
35
+ ...node.data.toObject(),
36
+ latex,
38
37
  };
39
- editor.apply({
40
- type: 'set_node',
41
- path: nodePath,
42
- properties: {
43
- data: node.data
44
- },
45
- newProperties: { data: update }
46
- });
47
- ReactEditor.focus(editor);
48
- Transforms.move(editor, { distance: 1, unit: 'offset' });
38
+ const change = value.change().setNodeByKey(node.key, { data: update });
39
+
40
+ const nextText = value.document.getNextText(node.key);
41
+
42
+ change.moveFocusTo(nextText.key, 0).moveAnchorTo(nextText.key, 0);
43
+
44
+ props.onToolbarDone(change, false);
49
45
  };
50
46
 
51
- const latex = node.data.latex;
47
+ const onChange = (latex) => {
48
+ const update = {
49
+ ...node.data.toObject(),
50
+ latex,
51
+ };
52
+ const change = value.change().setNodeByKey(node.key, { data: update });
53
+ log('call onToolbarChange:', change);
54
+ props.onDataChange(node.key, update);
55
+ };
56
+
57
+ const latex = node.data.get('latex');
52
58
 
53
59
  return (
54
60
  <MathToolbar
55
61
  autoFocus
56
62
  additionalKeys={generateAdditionalKeys(customKeys)}
57
63
  latex={latex}
64
+ onChange={onChange}
58
65
  onDone={onDone}
59
66
  onBlur={onBlur}
60
67
  onFocus={onFocus}
@@ -73,33 +80,21 @@ export const CustomToolbarComp = React.memo(
73
80
  const keypadModeChanged = keypadMode !== keypadModeNext;
74
81
  const controlledKeypadModeChanged = controlledKeypadMode !== controlledKeypadModeNext;
75
82
 
76
- const equal = isEqual(node, nodeNext);
83
+ const equal = node.equals(nodeNext);
77
84
  return equal && !keypadModeChanged && !controlledKeypadModeChanged;
78
85
  },
79
86
  );
80
87
 
81
88
  CustomToolbarComp.propTypes = {
82
- editor: PropTypes.object,
83
- node: PropTypes.shape({
84
- type: PropTypes.string,
85
- children: PropTypes.array,
86
- data: PropTypes.object
87
- }).isRequired,
88
- value: PropTypes.arrayOf(
89
- PropTypes.shape({
90
- type: PropTypes.string,
91
- children: PropTypes.array,
92
- data: PropTypes.object
93
- })
94
- ).isRequired,
89
+ node: SlatePropTypes.node.isRequired,
90
+ value: SlatePropTypes.value,
95
91
  onToolbarDone: PropTypes.func,
92
+ onDataChange: PropTypes.func,
96
93
  onFocus: PropTypes.func,
97
94
  onClick: PropTypes.func,
98
95
  onBlur: PropTypes.func,
99
96
  };
100
97
 
101
- const mathTypes = ['math', 'mathml'];
102
-
103
98
  export default function MathPlugin(opts) {
104
99
  MathPlugin.mathMlOptions = {
105
100
  mmlOutput: opts.mmlOutput,
@@ -110,12 +105,13 @@ export default function MathPlugin(opts) {
110
105
  name: 'math',
111
106
  toolbar: {
112
107
  icon: <Functions />,
113
- onClick: editor => {
108
+ onClick: (value, onChange) => {
114
109
  log('[insertMath]');
115
110
  const math = inlineMath();
116
-
117
- editor.insertNode(math);
111
+ const change = value.change().insertInline(math);
112
+ onChange(change);
118
113
  },
114
+ supports: (node) => node && node.object === 'inline' && node.type === 'math',
119
115
  /**
120
116
  * Return a react component function
121
117
  * @param node {Slate.Node}
@@ -125,20 +121,6 @@ export default function MathPlugin(opts) {
125
121
  */
126
122
  CustomToolbarComp,
127
123
  },
128
- rules: editor => {
129
- const { isVoid, isInline } = editor;
130
-
131
- editor.isVoid = element => {
132
- return mathTypes.includes(element.type) ? true : isVoid(element);
133
- };
134
-
135
- editor.isInline = element => {
136
- return mathTypes.includes(element.type) ? true : isInline(element);
137
- };
138
-
139
- return editor;
140
- },
141
- supports: node => mathTypes.includes(node.type),
142
124
  schema: {
143
125
  document: { match: [{ type: 'math' }] },
144
126
  },
@@ -162,14 +144,9 @@ export default function MathPlugin(opts) {
162
144
  * Here for rendering mathml content
163
145
  */
164
146
  if (props.node.type === 'mathml') {
165
- const { data: { html } } = props.node;
166
-
167
- return (
168
- <span>
169
- <span {...props.attributes} contentEditable={false} dangerouslySetInnerHTML={{ __html: html }} />
170
- {props.children}
171
- </span>
172
- );
147
+ const html = props.node.data.get('html');
148
+
149
+ return <span {...props.attributes} dangerouslySetInnerHTML={{ __html: html }} />;
173
150
  }
174
151
  },
175
152
  };
@@ -183,16 +160,18 @@ MathPlugin.mathMlOptions = {};
183
160
 
184
161
  MathPlugin.propTypes = {
185
162
  attributes: PropTypes.object,
186
- node: PropTypes.node,
163
+ node: SlatePropTypes.node,
187
164
  };
188
165
 
189
- export const inlineMath = () => ({
190
- type: 'math',
191
- data: {
192
- latex: '',
193
- },
194
- children: [{ text: '' }],
195
- });
166
+ export const inlineMath = () =>
167
+ Inline.create({
168
+ object: 'inline',
169
+ type: 'math',
170
+ isVoid: true,
171
+ data: {
172
+ latex: '',
173
+ },
174
+ });
196
175
 
197
176
  const htmlDecode = (input) => {
198
177
  const doc = new DOMParser().parseFromString(input, 'text/html');
@@ -235,7 +214,7 @@ const lessThanHandling = (input) => {
235
214
  };
236
215
 
237
216
  export const serialization = {
238
- deserialize(el, children) {
217
+ deserialize(el) {
239
218
  const tagName = getTagName(el);
240
219
  /**
241
220
  * This is used for when there's a wrapper over the mathml element.
@@ -258,21 +237,26 @@ export const serialization = {
258
237
  const latex = htmlDecode(htmlToUse);
259
238
  const { unwrapped, wrapType } = unWrapMath(latex);
260
239
 
261
- return jsx('element', {
240
+ return {
241
+ object: 'inline',
262
242
  type: 'math',
243
+ isVoid: true,
244
+ nodes: [],
263
245
  data: {
264
246
  latex: unwrapped,
265
247
  wrapper: wrapType,
266
248
  },
267
- });
249
+ };
268
250
  }
269
251
 
270
- return jsx('element', {
252
+ return {
253
+ object: 'inline',
254
+ isVoid: true,
271
255
  type: 'mathml',
272
256
  data: {
273
257
  html: newHtml,
274
258
  },
275
- });
259
+ };
276
260
  }
277
261
 
278
262
  if (el.nodeType === TEXT_NODE) {
@@ -289,28 +273,31 @@ export const serialization = {
289
273
  const latex = htmlDecode(el.innerHTML);
290
274
  const { unwrapped, wrapType } = unWrapMath(latex);
291
275
  log('[deserialize]: noBrackets: ', unwrapped, wrapType);
292
-
293
- return jsx('element', {
276
+ return {
277
+ object: 'inline',
294
278
  type: 'math',
279
+ isVoid: true,
280
+ nodes: [],
295
281
  data: {
296
282
  latex: unwrapped,
297
283
  wrapper: wrapType,
298
284
  },
299
- });
285
+ };
300
286
  }
301
287
  },
302
288
  serialize(object) {
303
289
  if (object.type === 'math') {
304
- const { latex, wrapper } = object.data || {};
305
- log('[serialize] latex: ', latex);
306
- const decoded = htmlDecode(lessThanHandling(latex));
290
+ const l = object.data.get('latex');
291
+ const wrapper = object.data.get('wrapper');
292
+ log('[serialize] latex: ', l);
293
+ const decoded = htmlDecode(lessThanHandling(l));
307
294
 
308
295
  if (MathPlugin.mathMlOptions.mmlOutput) {
309
296
  const res = renderMath(`<span data-latex="" data-raw="${decoded}">${wrapMath(decoded, wrapper)}</span>`);
310
297
  const newLatex = mmlToLatex(res);
311
298
 
312
299
  // we need to remove all the spaces from the latex to be able to compare it
313
- const strippedL = latex.replace(/\s/g, '');
300
+ const strippedL = l.replace(/\s/g, '');
314
301
  const strippedNewL = newLatex.replace(/\s/g, '');
315
302
 
316
303
  // we check if the latex keeps his form after being converted to mathml and back to latex
@@ -319,8 +306,8 @@ export const serialization = {
319
306
  return <span data-type="mathml" dangerouslySetInnerHTML={{ __html: res }} />;
320
307
  } else {
321
308
  // if it doesn't we keep the latex version
322
- console.log('This latex can not be safely converted to mathml:', latex, 'so we will keep the latex version!!!');
323
- console.warn('This latex can not be safely converted to mathml:', latex, 'so we will keep the latex version!!!');
309
+ console.log('This latex can not be safely converted to mathml:', l, 'so we will keep the latex version!!!');
310
+ console.warn('This latex can not be safely converted to mathml:', l, 'so we will keep the latex version!!!');
324
311
  }
325
312
  }
326
313
 
@@ -335,7 +322,7 @@ export const serialization = {
335
322
  * Here for rendering mathml content
336
323
  */
337
324
  if (object.type === 'mathml') {
338
- const { html } = object.data || {};
325
+ const html = object.data.get('html');
339
326
 
340
327
  return <span data-type="mathml" dangerouslySetInnerHTML={{ __html: html }} />;
341
328
  }
@@ -1,14 +1,10 @@
1
1
  import React from 'react';
2
2
  import ReactDOM from 'react-dom';
3
- import { Node as SlateNode, Transforms } from 'slate';
4
- import { jsx } from 'slate-hyperscript';
5
- import { ReactEditor } from 'slate-react';
3
+ import { Inline } from 'slate';
6
4
  import TheatersIcon from '@material-ui/icons/Theaters';
7
5
  import VolumeUpIcon from '@material-ui/icons/VolumeUp';
8
- import get from 'lodash/get';
9
- import omit from 'lodash/omit';
10
-
11
6
  import debug from 'debug';
7
+
12
8
  import MediaDialog from './media-dialog';
13
9
  import MediaToolbar from './media-toolbar';
14
10
  import MediaWrapper from './media-wrapper';
@@ -53,41 +49,17 @@ export const insertDialog = (props) => {
53
49
  document.body.appendChild(newEl);
54
50
  };
55
51
 
56
- const getNodeBy = (editor, callback) => {
57
- const descendants = SlateNode.descendants(editor, {
58
- reverse: true
59
- });
60
-
61
- for (const [descendant, descendantPath] of descendants) {
62
- if (callback(descendant, descendantPath)) {
63
- return [descendant, descendantPath];
64
- }
65
- }
66
- };
67
-
68
- const moveFocusAfterMedia = (editor, node) => {
69
- if (!editor || !node) {
70
- return;
71
- }
72
-
73
- setTimeout(() => {
74
- ReactEditor.focus(editor);
75
- Transforms.move(editor, { distance: 1, unit: 'offset' });
76
- }, 0);
77
- };
78
-
79
52
  const types = ['audio', 'video'];
80
53
 
81
54
  export default function MediaPlugin(type, opts) {
82
55
  const toolbar = {
83
56
  icon: type === 'audio' ? <VolumeUpIcon /> : <TheatersIcon />,
84
- onClick: editor => {
57
+ onClick: (value, onChange) => {
85
58
  log('[toolbar] onClick');
86
- const inline = {
59
+ const inline = Inline.create({
87
60
  type: type,
88
61
  isVoid: true,
89
62
  data: {
90
- newMedia: true,
91
63
  editing: false,
92
64
  ends: undefined,
93
65
  height: undefined,
@@ -97,121 +69,85 @@ export default function MediaPlugin(type, opts) {
97
69
  url: undefined,
98
70
  width: undefined,
99
71
  },
100
- children: [{ text: '' }],
101
- };
102
-
103
- editor.insertNode(inline);
72
+ });
104
73
 
74
+ const change = value.change().insertInline(inline);
75
+ onChange(change);
105
76
  insertDialog({
106
77
  type,
107
78
  opts,
108
79
  callback: (val, data) => {
109
- const nodePath = ReactEditor.findPath(editor, inline);
80
+ const nodeIsThere = change.value.document.findDescendant((d) => d.key === inline.key);
110
81
 
111
- if (inline) {
82
+ if (nodeIsThere) {
112
83
  if (!val) {
113
- editor.apply({
114
- type: 'remove_node',
115
- path: nodePath
116
- });
84
+ const c = change.removeNodeByKey(inline.key);
85
+ onChange(c, () => opts.focus());
117
86
  } else {
118
- editor.apply({
119
- type: 'set_node',
120
- path: nodePath,
121
- properties: {
122
- data: inline.data
123
- },
124
- newProperties: {
125
- data: {
126
- ...data,
127
- newMedia: false
128
- }
129
- }
130
- });
87
+ const c = change.setNodeByKey(inline.key, { data });
88
+ onChange(c, () => opts.focus('beginning', nodeIsThere));
131
89
  }
90
+ } else {
91
+ opts.focus();
132
92
  }
133
-
134
- moveFocusAfterMedia(editor, inline);
135
- }
93
+ },
136
94
  });
137
- }
95
+ },
96
+ supports: (node) => node.object === 'inline' && node.type === type,
138
97
  };
139
98
 
140
99
  return {
141
100
  name: type,
142
101
  toolbar,
143
- rules: editor => {
144
- const { isVoid, isInline } = editor;
145
-
146
- editor.isVoid = element => {
147
- return ['audio', 'video'].includes(element.type) ? true : isVoid(element);
148
- };
149
-
150
- editor.isInline = element => {
151
- return ['audio', 'video'].includes(element.type) ? true : isInline(element);
152
- };
102
+ deleteNode: (e, node, value, onChange) => {
103
+ e.preventDefault();
104
+ const change = value.change().removeNodeByKey(node.key);
153
105
 
154
- return editor;
106
+ onChange(change);
155
107
  },
156
- supports: node => node.type === type,
157
108
  renderNode(props) {
158
109
  if (props.node.type === type) {
159
- const { node, editor } = props;
110
+ const { node, key } = props;
160
111
  const { data } = node;
161
- const { src, height, width, editing, tag, ...rest } = omit(data, ['newMedia', 'urlToUse']);
162
- const attributes = { ...rest, ...props.attributes };
163
- const handleEdit = event => {
164
- const nodeToEdit = ReactEditor.toSlateNode(editor, event.target);
165
- const nodePath = ReactEditor.findPath(editor, nodeToEdit);
166
-
167
- editor.apply({
168
- type: 'set_node',
169
- path: nodePath,
170
- properties: {
171
- data: node.data
112
+ const jsonData = data.toJSON();
113
+ const { src, height, width, editing, tag, ...rest } = jsonData;
114
+ const handleEdit = () => {
115
+ const change = opts.createChange();
116
+ const c = change.setNodeByKey(key, {
117
+ data: {
118
+ ...jsonData,
119
+ editing: true,
172
120
  },
173
- newProperties: {
174
- data: {
175
- ...data,
176
- editing: true
177
- }
178
- }
179
121
  });
180
122
 
181
- insertDialog({
182
- ...data,
183
- edit: true,
184
- type,
185
- opts,
186
- callback: (val, data) => {
187
- const nodePath = ReactEditor.findPath(editor, nodeToEdit);
188
-
189
- if (nodePath && val) {
190
- editor.apply({
191
- type: 'set_node',
192
- path: nodePath,
193
- properties: {
194
- data: node.data
195
- },
196
- newProperties: {
197
- data: {
198
- ...data,
199
- editing: true
200
- }
201
- }
202
- });
203
- }
204
- }
123
+ opts.onChange(c, () => {
124
+ insertDialog({
125
+ ...jsonData,
126
+ edit: true,
127
+ type,
128
+ opts,
129
+ callback: (val, data) => {
130
+ const { key } = node;
131
+
132
+ const nodeIsThere = change.value.document.findDescendant(
133
+ (d) => d.type === type && d.data.get('editing'),
134
+ );
135
+
136
+ if (nodeIsThere && val) {
137
+ const c = change.setNodeByKey(key, { data, editing: false });
138
+ opts.onChange(c, () => opts.focus('beginning', nodeIsThere));
139
+ } else {
140
+ opts.focus();
141
+ }
142
+ },
143
+ });
205
144
  });
206
145
  };
207
- const handleDelete = event => {
208
- const nodeToEdit = ReactEditor.toSlateNode(editor, event.target);
209
- const nodePath = ReactEditor.findPath(editor, nodeToEdit);
146
+ const handleDelete = () => {
147
+ const change = opts.createChange();
148
+ const c = change.removeNodeByKey(node.key);
210
149
 
211
- editor.apply({
212
- type: 'remove_node',
213
- path: nodePath
214
- });
150
+ opts.onChange(c);
215
151
  };
216
152
  const style = {};
217
153
 
@@ -225,35 +161,62 @@ export default function MediaPlugin(type, opts) {
225
161
 
226
162
  if (tag === 'audio') {
227
163
  return (
228
- <MediaWrapper data-type={type} width={style.width} attributes={attributes}>
164
+ <MediaWrapper editor data-type={type} width={style.width} {...rest}>
229
165
  <audio controls="controls">
230
166
  <source type="audio/mp3" src={src} />
231
167
  </audio>
232
168
  <MediaToolbar hideEdit onRemove={handleDelete} />
233
- {props.children}
234
169
  </MediaWrapper>
235
170
  );
236
171
  }
237
172
 
238
173
  return (
239
- <MediaWrapper data-type={type} width={style.width} attributes={attributes}>
240
- <div contentEditable={false}>
241
- <iframe
242
- frameBorder="0"
243
- allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
244
- allowFullScreen
245
- src={src}
246
- editing={editing ? 1 : 0}
247
- {...rest}
248
- {...style}
249
- />
250
- <MediaToolbar onEdit={handleEdit} onRemove={handleDelete} />
251
- </div>
252
- {props.children}
174
+ <MediaWrapper editor data-type={type} width={style.width} {...rest}>
175
+ <iframe
176
+ frameBorder="0"
177
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
178
+ allowFullScreen
179
+ src={src}
180
+ editing={editing ? 1 : 0}
181
+ {...rest}
182
+ {...style}
183
+ />
184
+ <MediaToolbar onEdit={handleEdit} onRemove={handleDelete} />
253
185
  </MediaWrapper>
254
186
  );
255
187
  }
256
188
  },
189
+ normalizeNode: (node) => {
190
+ const textNodeMap = {};
191
+ const updateNodesArray = [];
192
+ let index = 0;
193
+
194
+ if (node.object !== 'document') return;
195
+
196
+ node.findDescendant((d) => {
197
+ if (d.object === 'text') {
198
+ textNodeMap[index] = d;
199
+ }
200
+
201
+ const isMedia = types.indexOf(d.type) >= 0;
202
+
203
+ if (isMedia) {
204
+ if (index > 0 && textNodeMap[index - 1] && textNodeMap[index - 1].text === '') {
205
+ updateNodesArray.push(textNodeMap[index - 1]);
206
+ }
207
+ }
208
+
209
+ index++;
210
+ });
211
+
212
+ if (!updateNodesArray.length) return;
213
+
214
+ return (change) => {
215
+ change.withoutNormalization(() => {
216
+ updateNodesArray.forEach((n) => change.insertTextByKey(n.key, 0, ' '));
217
+ });
218
+ };
219
+ },
257
220
  };
258
221
  }
259
222
 
@@ -280,8 +243,10 @@ export const serialization = {
280
243
  const width = parseInt(el.getAttribute('width'), 10) || null;
281
244
  const height = parseInt(el.getAttribute('height'), 10) || null;
282
245
 
283
- const out = jsx('element', {
284
- type,
246
+ const out = {
247
+ object: 'inline',
248
+ type: type,
249
+ isVoid: true,
285
250
  data: {
286
251
  tag,
287
252
  src: src || el.getAttribute('src'),
@@ -291,23 +256,29 @@ export const serialization = {
291
256
  starts,
292
257
  title,
293
258
  width,
294
- url
295
- }
296
- });
297
-
259
+ url,
260
+ },
261
+ };
298
262
  log('return object: ', out);
299
263
  return out;
300
264
  },
301
- serialize(object) {
265
+ serialize(object /*, children*/) {
302
266
  const typeIndex = types.indexOf(object.type);
303
267
 
304
- if (typeIndex < 0) {
305
- return;
306
- }
268
+ if (typeIndex < 0) return;
307
269
 
308
270
  const type = types[typeIndex];
309
271
 
310
- const { editing, tag, ends, src, starts, title, width, height, url } = object.data || {};
272
+ const { data } = object;
273
+ const editing = data.get('editing');
274
+ const tag = data.get('tag');
275
+ const ends = data.get('ends');
276
+ const src = data.get('src');
277
+ const starts = data.get('starts');
278
+ const title = data.get('title');
279
+ const width = data.get('width');
280
+ const height = data.get('height');
281
+ const url = data.get('url');
311
282
  const style = {};
312
283
 
313
284
  if (width) {