@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.
- 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 +140 -450
- package/src/index.jsx +96 -62
- package/src/plugins/characters/index.jsx +18 -14
- package/src/plugins/html/icons/index.jsx +19 -0
- package/src/plugins/html/index.jsx +68 -0
- package/src/plugins/image/component.jsx +41 -67
- package/src/plugins/image/index.jsx +43 -108
- package/src/plugins/image/insert-image-handler.js +27 -62
- package/src/plugins/index.jsx +39 -21
- package/src/plugins/list/index.jsx +91 -66
- package/src/plugins/math/index.jsx +71 -84
- package/src/plugins/media/index.jsx +118 -147
- 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 +7 -10
- package/src/plugins/respArea/explicit-constructed-response/index.jsx +2 -3
- package/src/plugins/respArea/index.jsx +90 -138
- package/src/plugins/respArea/inline-dropdown/index.jsx +2 -3
- package/src/plugins/respArea/utils.jsx +28 -23
- package/src/plugins/table/index.jsx +216 -340
- package/src/plugins/table/table-toolbar.jsx +5 -9
- package/src/plugins/toolbar/default-toolbar.jsx +31 -51
- package/src/plugins/toolbar/editor-and-toolbar.jsx +114 -121
- package/src/plugins/toolbar/toolbar.jsx +224 -258
- package/src/plugins/utils.js +2 -19
- 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,
|
|
37
|
-
latex
|
|
35
|
+
...node.data.toObject(),
|
|
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,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
|
|
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:
|
|
163
|
+
node: SlatePropTypes.node,
|
|
187
164
|
};
|
|
188
165
|
|
|
189
|
-
export const inlineMath = () =>
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
305
|
-
|
|
306
|
-
|
|
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 =
|
|
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:',
|
|
323
|
-
console.warn('This latex can not be safely converted to mathml:',
|
|
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
|
|
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 {
|
|
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:
|
|
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
|
-
|
|
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
|
|
80
|
+
const nodeIsThere = change.value.document.findDescendant((d) => d.key === inline.key);
|
|
110
81
|
|
|
111
|
-
if (
|
|
82
|
+
if (nodeIsThere) {
|
|
112
83
|
if (!val) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
path: nodePath
|
|
116
|
-
});
|
|
84
|
+
const c = change.removeNodeByKey(inline.key);
|
|
85
|
+
onChange(c, () => opts.focus());
|
|
117
86
|
} else {
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
144
|
-
|
|
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
|
-
|
|
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,
|
|
110
|
+
const { node, key } = props;
|
|
160
111
|
const { data } = node;
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
const handleEdit =
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
type
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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 =
|
|
208
|
-
const
|
|
209
|
-
const
|
|
146
|
+
const handleDelete = () => {
|
|
147
|
+
const change = opts.createChange();
|
|
148
|
+
const c = change.removeNodeByKey(node.key);
|
|
210
149
|
|
|
211
|
-
|
|
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}
|
|
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}
|
|
240
|
-
<
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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 =
|
|
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'),
|
|
@@ -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 {
|
|
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) {
|