@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.
- package/CHANGELOG.json +1 -1
- package/CHANGELOG.md +81 -0
- package/LICENSE.md +5 -0
- package/lib/editor.js +410 -543
- package/lib/editor.js.map +1 -1
- package/lib/index.js +200 -101
- package/lib/index.js.map +1 -1
- package/lib/parse-html.js +5 -6
- package/lib/parse-html.js.map +1 -1
- package/lib/plugins/characters/custom-popper.js +12 -2
- package/lib/plugins/characters/custom-popper.js.map +1 -1
- package/lib/plugins/characters/index.js +71 -19
- package/lib/plugins/characters/index.js.map +1 -1
- package/lib/plugins/characters/utils.js.map +1 -1
- package/lib/plugins/html/icons/index.js +38 -0
- package/lib/plugins/html/icons/index.js.map +1 -0
- package/lib/plugins/html/index.js +75 -0
- package/lib/plugins/html/index.js.map +1 -0
- package/lib/plugins/image/alt-dialog.js +26 -0
- package/lib/plugins/image/alt-dialog.js.map +1 -1
- package/lib/plugins/image/component.js +124 -90
- package/lib/plugins/image/component.js.map +1 -1
- package/lib/plugins/image/image-toolbar.js +45 -7
- package/lib/plugins/image/image-toolbar.js.map +1 -1
- package/lib/plugins/image/index.js +91 -113
- package/lib/plugins/image/index.js.map +1 -1
- package/lib/plugins/image/insert-image-handler.js +54 -72
- package/lib/plugins/image/insert-image-handler.js.map +1 -1
- package/lib/plugins/index.js +71 -31
- package/lib/plugins/index.js.map +1 -1
- package/lib/plugins/list/index.js +129 -58
- package/lib/plugins/list/index.js.map +1 -1
- package/lib/plugins/math/index.js +152 -118
- package/lib/plugins/math/index.js.map +1 -1
- package/lib/plugins/media/index.js +185 -168
- package/lib/plugins/media/index.js.map +1 -1
- package/lib/plugins/media/media-dialog.js +197 -110
- package/lib/plugins/media/media-dialog.js.map +1 -1
- package/lib/plugins/media/media-toolbar.js +24 -4
- package/lib/plugins/media/media-toolbar.js.map +1 -1
- package/lib/plugins/media/media-wrapper.js +65 -23
- package/lib/plugins/media/media-wrapper.js.map +1 -1
- package/lib/plugins/respArea/drag-in-the-blank/choice.js +50 -10
- package/lib/plugins/respArea/drag-in-the-blank/choice.js.map +1 -1
- package/lib/plugins/respArea/drag-in-the-blank/index.js +22 -9
- package/lib/plugins/respArea/drag-in-the-blank/index.js.map +1 -1
- package/lib/plugins/respArea/explicit-constructed-response/index.js +9 -4
- package/lib/plugins/respArea/explicit-constructed-response/index.js.map +1 -1
- package/lib/plugins/respArea/icons/index.js +18 -1
- package/lib/plugins/respArea/icons/index.js.map +1 -1
- package/lib/plugins/respArea/index.js +133 -122
- package/lib/plugins/respArea/index.js.map +1 -1
- package/lib/plugins/respArea/inline-dropdown/index.js +10 -4
- package/lib/plugins/respArea/inline-dropdown/index.js.map +1 -1
- package/lib/plugins/respArea/utils.js +33 -15
- package/lib/plugins/respArea/utils.js.map +1 -1
- package/lib/plugins/table/icons/index.js +7 -0
- package/lib/plugins/table/icons/index.js.map +1 -1
- package/lib/plugins/table/index.js +279 -390
- package/lib/plugins/table/index.js.map +1 -1
- package/lib/plugins/table/table-toolbar.js +47 -14
- package/lib/plugins/table/table-toolbar.js.map +1 -1
- package/lib/plugins/toolbar/default-toolbar.js +63 -51
- package/lib/plugins/toolbar/default-toolbar.js.map +1 -1
- package/lib/plugins/toolbar/done-button.js +9 -1
- package/lib/plugins/toolbar/done-button.js.map +1 -1
- package/lib/plugins/toolbar/editor-and-toolbar.js +140 -83
- package/lib/plugins/toolbar/editor-and-toolbar.js.map +1 -1
- package/lib/plugins/toolbar/index.js +5 -0
- package/lib/plugins/toolbar/index.js.map +1 -1
- package/lib/plugins/toolbar/toolbar-buttons.js +39 -8
- package/lib/plugins/toolbar/toolbar-buttons.js.map +1 -1
- package/lib/plugins/toolbar/toolbar.js +261 -225
- package/lib/plugins/toolbar/toolbar.js.map +1 -1
- package/lib/plugins/utils.js +16 -19
- package/lib/plugins/utils.js.map +1 -1
- package/lib/serialization.js +70 -11
- package/lib/serialization.js.map +1 -1
- package/lib/theme.js.map +1 -1
- package/package.json +18 -17
- package/src/editor.jsx +139 -434
- package/src/index.jsx +96 -62
- package/src/plugins/characters/index.jsx +17 -12
- package/src/plugins/html/icons/index.jsx +19 -0
- package/src/plugins/html/index.jsx +68 -0
- package/src/plugins/image/component.jsx +38 -60
- package/src/plugins/image/index.jsx +42 -95
- package/src/plugins/image/insert-image-handler.js +27 -62
- package/src/plugins/index.jsx +39 -21
- package/src/plugins/list/index.jsx +90 -62
- package/src/plugins/math/index.jsx +70 -93
- package/src/plugins/media/index.jsx +117 -146
- package/src/plugins/media/media-dialog.js +9 -10
- package/src/plugins/media/media-wrapper.jsx +27 -29
- package/src/plugins/respArea/drag-in-the-blank/index.jsx +4 -5
- package/src/plugins/respArea/explicit-constructed-response/index.jsx +1 -2
- package/src/plugins/respArea/index.jsx +84 -114
- package/src/plugins/respArea/inline-dropdown/index.jsx +2 -3
- package/src/plugins/respArea/utils.jsx +28 -23
- package/src/plugins/table/index.jsx +214 -334
- package/src/plugins/table/table-toolbar.jsx +4 -3
- package/src/plugins/toolbar/default-toolbar.jsx +30 -48
- package/src/plugins/toolbar/editor-and-toolbar.jsx +114 -114
- package/src/plugins/toolbar/toolbar.jsx +224 -254
- package/src/plugins/utils.js +0 -16
- package/src/serialization.jsx +1 -1
- package/lib/components.js +0 -92
- package/lib/components.js.map +0 -1
- package/lib/new-serialization.js +0 -280
- package/lib/new-serialization.js.map +0 -1
- package/lib/plugins/hotKeys/index.js +0 -60
- package/lib/plugins/hotKeys/index.js.map +0 -1
- package/lib/test-serializer.js +0 -138
- package/lib/test-serializer.js.map +0 -1
- package/src/components.js +0 -135
- package/src/new-serialization.jsx +0 -310
- package/src/plugins/hotKeys/index.js +0 -54
- 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 '../../
|
|
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,
|
|
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,
|
|
35
|
+
...node.data.toObject(),
|
|
37
36
|
latex,
|
|
38
37
|
};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
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 =
|
|
83
|
+
const equal = node.equals(nodeNext);
|
|
77
84
|
return equal && !keypadModeChanged && !controlledKeypadModeChanged;
|
|
78
85
|
},
|
|
79
86
|
);
|
|
80
87
|
|
|
81
88
|
CustomToolbarComp.propTypes = {
|
|
82
|
-
|
|
83
|
-
|
|
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: (
|
|
108
|
+
onClick: (value, onChange) => {
|
|
114
109
|
log('[insertMath]');
|
|
115
110
|
const math = inlineMath();
|
|
116
|
-
|
|
117
|
-
|
|
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,16 +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
|
|
166
|
-
|
|
167
|
-
} =
|
|
168
|
-
|
|
169
|
-
return (
|
|
170
|
-
<span>
|
|
171
|
-
<span {...props.attributes} contentEditable={false} dangerouslySetInnerHTML={{ __html: html }} />
|
|
172
|
-
{props.children}
|
|
173
|
-
</span>
|
|
174
|
-
);
|
|
147
|
+
const html = props.node.data.get('html');
|
|
148
|
+
|
|
149
|
+
return <span {...props.attributes} dangerouslySetInnerHTML={{ __html: html }} />;
|
|
175
150
|
}
|
|
176
151
|
},
|
|
177
152
|
};
|
|
@@ -185,16 +160,18 @@ MathPlugin.mathMlOptions = {};
|
|
|
185
160
|
|
|
186
161
|
MathPlugin.propTypes = {
|
|
187
162
|
attributes: PropTypes.object,
|
|
188
|
-
node:
|
|
163
|
+
node: SlatePropTypes.node,
|
|
189
164
|
};
|
|
190
165
|
|
|
191
|
-
export const inlineMath = () =>
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
166
|
+
export const inlineMath = () =>
|
|
167
|
+
Inline.create({
|
|
168
|
+
object: 'inline',
|
|
169
|
+
type: 'math',
|
|
170
|
+
isVoid: true,
|
|
171
|
+
data: {
|
|
172
|
+
latex: '',
|
|
173
|
+
},
|
|
174
|
+
});
|
|
198
175
|
|
|
199
176
|
const htmlDecode = (input) => {
|
|
200
177
|
const doc = new DOMParser().parseFromString(input, 'text/html');
|
|
@@ -237,7 +214,7 @@ const lessThanHandling = (input) => {
|
|
|
237
214
|
};
|
|
238
215
|
|
|
239
216
|
export const serialization = {
|
|
240
|
-
deserialize(el
|
|
217
|
+
deserialize(el) {
|
|
241
218
|
const tagName = getTagName(el);
|
|
242
219
|
/**
|
|
243
220
|
* This is used for when there's a wrapper over the mathml element.
|
|
@@ -260,21 +237,26 @@ export const serialization = {
|
|
|
260
237
|
const latex = htmlDecode(htmlToUse);
|
|
261
238
|
const { unwrapped, wrapType } = unWrapMath(latex);
|
|
262
239
|
|
|
263
|
-
return
|
|
240
|
+
return {
|
|
241
|
+
object: 'inline',
|
|
264
242
|
type: 'math',
|
|
243
|
+
isVoid: true,
|
|
244
|
+
nodes: [],
|
|
265
245
|
data: {
|
|
266
246
|
latex: unwrapped,
|
|
267
247
|
wrapper: wrapType,
|
|
268
248
|
},
|
|
269
|
-
}
|
|
249
|
+
};
|
|
270
250
|
}
|
|
271
251
|
|
|
272
|
-
return
|
|
252
|
+
return {
|
|
253
|
+
object: 'inline',
|
|
254
|
+
isVoid: true,
|
|
273
255
|
type: 'mathml',
|
|
274
256
|
data: {
|
|
275
257
|
html: newHtml,
|
|
276
258
|
},
|
|
277
|
-
}
|
|
259
|
+
};
|
|
278
260
|
}
|
|
279
261
|
|
|
280
262
|
if (el.nodeType === TEXT_NODE) {
|
|
@@ -291,28 +273,31 @@ export const serialization = {
|
|
|
291
273
|
const latex = htmlDecode(el.innerHTML);
|
|
292
274
|
const { unwrapped, wrapType } = unWrapMath(latex);
|
|
293
275
|
log('[deserialize]: noBrackets: ', unwrapped, wrapType);
|
|
294
|
-
|
|
295
|
-
|
|
276
|
+
return {
|
|
277
|
+
object: 'inline',
|
|
296
278
|
type: 'math',
|
|
279
|
+
isVoid: true,
|
|
280
|
+
nodes: [],
|
|
297
281
|
data: {
|
|
298
282
|
latex: unwrapped,
|
|
299
283
|
wrapper: wrapType,
|
|
300
284
|
},
|
|
301
|
-
}
|
|
285
|
+
};
|
|
302
286
|
}
|
|
303
287
|
},
|
|
304
288
|
serialize(object) {
|
|
305
289
|
if (object.type === 'math') {
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
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));
|
|
309
294
|
|
|
310
295
|
if (MathPlugin.mathMlOptions.mmlOutput) {
|
|
311
296
|
const res = renderMath(`<span data-latex="" data-raw="${decoded}">${wrapMath(decoded, wrapper)}</span>`);
|
|
312
297
|
const newLatex = mmlToLatex(res);
|
|
313
298
|
|
|
314
299
|
// we need to remove all the spaces from the latex to be able to compare it
|
|
315
|
-
const strippedL =
|
|
300
|
+
const strippedL = l.replace(/\s/g, '');
|
|
316
301
|
const strippedNewL = newLatex.replace(/\s/g, '');
|
|
317
302
|
|
|
318
303
|
// we check if the latex keeps his form after being converted to mathml and back to latex
|
|
@@ -321,16 +306,8 @@ export const serialization = {
|
|
|
321
306
|
return <span data-type="mathml" dangerouslySetInnerHTML={{ __html: res }} />;
|
|
322
307
|
} else {
|
|
323
308
|
// if it doesn't we keep the latex version
|
|
324
|
-
console.log(
|
|
325
|
-
|
|
326
|
-
latex,
|
|
327
|
-
'so we will keep the latex version!!!',
|
|
328
|
-
);
|
|
329
|
-
console.warn(
|
|
330
|
-
'This latex can not be safely converted to mathml:',
|
|
331
|
-
latex,
|
|
332
|
-
'so we will keep the latex version!!!',
|
|
333
|
-
);
|
|
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!!!');
|
|
334
311
|
}
|
|
335
312
|
}
|
|
336
313
|
|
|
@@ -345,7 +322,7 @@ export const serialization = {
|
|
|
345
322
|
* Here for rendering mathml content
|
|
346
323
|
*/
|
|
347
324
|
if (object.type === 'mathml') {
|
|
348
|
-
const
|
|
325
|
+
const html = object.data.get('html');
|
|
349
326
|
|
|
350
327
|
return <span data-type="mathml" dangerouslySetInnerHTML={{ __html: html }} />;
|
|
351
328
|
}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import ReactDOM from 'react-dom';
|
|
3
|
-
import {
|
|
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 omit from 'lodash/omit';
|
|
9
|
-
|
|
10
6
|
import debug from 'debug';
|
|
7
|
+
|
|
11
8
|
import MediaDialog from './media-dialog';
|
|
12
9
|
import MediaToolbar from './media-toolbar';
|
|
13
10
|
import MediaWrapper from './media-wrapper';
|
|
@@ -52,41 +49,17 @@ export const insertDialog = (props) => {
|
|
|
52
49
|
document.body.appendChild(newEl);
|
|
53
50
|
};
|
|
54
51
|
|
|
55
|
-
const getNodeBy = (editor, callback) => {
|
|
56
|
-
const descendants = SlateNode.descendants(editor, {
|
|
57
|
-
reverse: true,
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
for (const [descendant, descendantPath] of descendants) {
|
|
61
|
-
if (callback(descendant, descendantPath)) {
|
|
62
|
-
return [descendant, descendantPath];
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const moveFocusAfterMedia = (editor, node) => {
|
|
68
|
-
if (!editor || !node) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
setTimeout(() => {
|
|
73
|
-
ReactEditor.focus(editor);
|
|
74
|
-
Transforms.move(editor, { distance: 1, unit: 'offset' });
|
|
75
|
-
}, 0);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
52
|
const types = ['audio', 'video'];
|
|
79
53
|
|
|
80
54
|
export default function MediaPlugin(type, opts) {
|
|
81
55
|
const toolbar = {
|
|
82
56
|
icon: type === 'audio' ? <VolumeUpIcon /> : <TheatersIcon />,
|
|
83
|
-
onClick: (
|
|
57
|
+
onClick: (value, onChange) => {
|
|
84
58
|
log('[toolbar] onClick');
|
|
85
|
-
const inline = {
|
|
59
|
+
const inline = Inline.create({
|
|
86
60
|
type: type,
|
|
87
61
|
isVoid: true,
|
|
88
62
|
data: {
|
|
89
|
-
newMedia: true,
|
|
90
63
|
editing: false,
|
|
91
64
|
ends: undefined,
|
|
92
65
|
height: undefined,
|
|
@@ -96,164 +69,154 @@ export default function MediaPlugin(type, opts) {
|
|
|
96
69
|
url: undefined,
|
|
97
70
|
width: undefined,
|
|
98
71
|
},
|
|
99
|
-
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
editor.insertNode(inline);
|
|
72
|
+
});
|
|
103
73
|
|
|
74
|
+
const change = value.change().insertInline(inline);
|
|
75
|
+
onChange(change);
|
|
104
76
|
insertDialog({
|
|
105
77
|
type,
|
|
106
78
|
opts,
|
|
107
79
|
callback: (val, data) => {
|
|
108
|
-
const
|
|
80
|
+
const nodeIsThere = change.value.document.findDescendant((d) => d.key === inline.key);
|
|
109
81
|
|
|
110
|
-
if (
|
|
82
|
+
if (nodeIsThere) {
|
|
111
83
|
if (!val) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
path: nodePath,
|
|
115
|
-
});
|
|
84
|
+
const c = change.removeNodeByKey(inline.key);
|
|
85
|
+
onChange(c, () => opts.focus());
|
|
116
86
|
} else {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
path: nodePath,
|
|
120
|
-
properties: {
|
|
121
|
-
data: inline.data,
|
|
122
|
-
},
|
|
123
|
-
newProperties: {
|
|
124
|
-
data: {
|
|
125
|
-
...data,
|
|
126
|
-
newMedia: false,
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
});
|
|
87
|
+
const c = change.setNodeByKey(inline.key, { data });
|
|
88
|
+
onChange(c, () => opts.focus('beginning', nodeIsThere));
|
|
130
89
|
}
|
|
90
|
+
} else {
|
|
91
|
+
opts.focus();
|
|
131
92
|
}
|
|
132
|
-
|
|
133
|
-
moveFocusAfterMedia(editor, inline);
|
|
134
93
|
},
|
|
135
94
|
});
|
|
136
95
|
},
|
|
96
|
+
supports: (node) => node.object === 'inline' && node.type === type,
|
|
137
97
|
};
|
|
138
98
|
|
|
139
99
|
return {
|
|
140
100
|
name: type,
|
|
141
101
|
toolbar,
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
editor.isVoid = (element) => {
|
|
146
|
-
return ['audio', 'video'].includes(element.type) ? true : isVoid(element);
|
|
147
|
-
};
|
|
102
|
+
deleteNode: (e, node, value, onChange) => {
|
|
103
|
+
e.preventDefault();
|
|
104
|
+
const change = value.change().removeNodeByKey(node.key);
|
|
148
105
|
|
|
149
|
-
|
|
150
|
-
return ['audio', 'video'].includes(element.type) ? true : isInline(element);
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
return editor;
|
|
106
|
+
onChange(change);
|
|
154
107
|
},
|
|
155
|
-
supports: (node) => node.type === type,
|
|
156
108
|
renderNode(props) {
|
|
157
109
|
if (props.node.type === type) {
|
|
158
|
-
const { node,
|
|
110
|
+
const { node, key } = props;
|
|
159
111
|
const { data } = node;
|
|
160
|
-
const
|
|
161
|
-
const
|
|
162
|
-
const handleEdit = (
|
|
163
|
-
const
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
path: nodePath,
|
|
169
|
-
properties: {
|
|
170
|
-
data: node.data,
|
|
171
|
-
},
|
|
172
|
-
newProperties: {
|
|
173
|
-
data: {
|
|
174
|
-
...data,
|
|
175
|
-
editing: true,
|
|
176
|
-
},
|
|
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,
|
|
177
120
|
},
|
|
178
121
|
});
|
|
179
122
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
type
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
},
|
|
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
|
+
});
|
|
204
144
|
});
|
|
205
145
|
};
|
|
206
|
-
const handleDelete = (
|
|
207
|
-
const
|
|
208
|
-
const
|
|
146
|
+
const handleDelete = () => {
|
|
147
|
+
const change = opts.createChange();
|
|
148
|
+
const c = change.removeNodeByKey(node.key);
|
|
209
149
|
|
|
210
|
-
|
|
211
|
-
type: 'remove_node',
|
|
212
|
-
path: nodePath,
|
|
213
|
-
});
|
|
150
|
+
opts.onChange(c);
|
|
214
151
|
};
|
|
215
|
-
const
|
|
152
|
+
const style = {};
|
|
216
153
|
|
|
217
154
|
if (width) {
|
|
218
|
-
|
|
155
|
+
style.width = `${width}px`;
|
|
219
156
|
}
|
|
220
157
|
|
|
221
158
|
if (height) {
|
|
222
|
-
|
|
159
|
+
style.height = `${height}px`;
|
|
223
160
|
}
|
|
224
161
|
|
|
225
|
-
computedProps.editing = editing ? 1 : 0;
|
|
226
|
-
|
|
227
162
|
if (tag === 'audio') {
|
|
228
163
|
return (
|
|
229
|
-
<MediaWrapper data-type={type} width={
|
|
164
|
+
<MediaWrapper editor data-type={type} width={style.width} {...rest}>
|
|
230
165
|
<audio controls="controls">
|
|
231
166
|
<source type="audio/mp3" src={src} />
|
|
232
167
|
</audio>
|
|
233
168
|
<MediaToolbar hideEdit onRemove={handleDelete} />
|
|
234
|
-
{props.children}
|
|
235
169
|
</MediaWrapper>
|
|
236
170
|
);
|
|
237
171
|
}
|
|
238
172
|
|
|
239
173
|
return (
|
|
240
|
-
<MediaWrapper data-type={type} width={
|
|
241
|
-
<
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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 =
|
|
284
|
-
|
|
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'),
|
|
@@ -293,21 +258,27 @@ export const serialization = {
|
|
|
293
258
|
width,
|
|
294
259
|
url,
|
|
295
260
|
},
|
|
296
|
-
}
|
|
297
|
-
|
|
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 {
|
|
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) {
|