@pie-lib/editable-html 9.5.13 → 10.0.0-beta.1

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 (144) hide show
  1. package/CHANGELOG.md +0 -302
  2. package/lib/components.js +116 -0
  3. package/lib/components.js.map +1 -0
  4. package/lib/editor.js +418 -103
  5. package/lib/editor.js.map +1 -1
  6. package/lib/index.js +101 -155
  7. package/lib/index.js.map +1 -1
  8. package/lib/new-serialization.js +320 -0
  9. package/lib/new-serialization.js.map +1 -0
  10. package/lib/old-serialization.js +330 -0
  11. package/lib/parse-html.js +1 -1
  12. package/lib/parse-html.js.map +1 -1
  13. package/lib/plugins/characters/custom-popper.js +1 -1
  14. package/lib/plugins/characters/custom-popper.js.map +1 -1
  15. package/lib/plugins/characters/index.js +21 -19
  16. package/lib/plugins/characters/index.js.map +1 -1
  17. package/lib/plugins/characters/utils.js +1 -1
  18. package/lib/plugins/characters/utils.js.map +1 -1
  19. package/lib/plugins/hotKeys/index.js +67 -0
  20. package/lib/plugins/hotKeys/index.js.map +1 -0
  21. package/lib/plugins/image/alt-dialog.js +1 -6
  22. package/lib/plugins/image/alt-dialog.js.map +1 -1
  23. package/lib/plugins/image/component.js +70 -53
  24. package/lib/plugins/image/component.js.map +1 -1
  25. package/lib/plugins/image/image-toolbar.js +7 -9
  26. package/lib/plugins/image/image-toolbar.js.map +1 -1
  27. package/lib/plugins/image/index.js +83 -27
  28. package/lib/plugins/image/index.js.map +1 -1
  29. package/lib/plugins/image/insert-image-handler.js +72 -33
  30. package/lib/plugins/image/insert-image-handler.js.map +1 -1
  31. package/lib/plugins/index.js +23 -41
  32. package/lib/plugins/index.js.map +1 -1
  33. package/lib/plugins/list/index.js +64 -100
  34. package/lib/plugins/list/index.js.map +1 -1
  35. package/lib/plugins/math/index.js +86 -60
  36. package/lib/plugins/math/index.js.map +1 -1
  37. package/lib/plugins/media/index.js +202 -132
  38. package/lib/plugins/media/index.js.map +1 -1
  39. package/lib/plugins/media/media-dialog.js +17 -16
  40. package/lib/plugins/media/media-dialog.js.map +1 -1
  41. package/lib/plugins/media/media-toolbar.js +3 -3
  42. package/lib/plugins/media/media-toolbar.js.map +1 -1
  43. package/lib/plugins/media/media-wrapper.js +21 -58
  44. package/lib/plugins/media/media-wrapper.js.map +1 -1
  45. package/lib/plugins/respArea/drag-in-the-blank/choice.js +3 -3
  46. package/lib/plugins/respArea/drag-in-the-blank/choice.js.map +1 -1
  47. package/lib/plugins/respArea/drag-in-the-blank/index.js +3 -2
  48. package/lib/plugins/respArea/drag-in-the-blank/index.js.map +1 -1
  49. package/lib/plugins/respArea/explicit-constructed-response/index.js +3 -2
  50. package/lib/plugins/respArea/explicit-constructed-response/index.js.map +1 -1
  51. package/lib/plugins/respArea/icons/index.js +13 -15
  52. package/lib/plugins/respArea/icons/index.js.map +1 -1
  53. package/lib/plugins/respArea/index.js +87 -53
  54. package/lib/plugins/respArea/index.js.map +1 -1
  55. package/lib/plugins/respArea/inline-dropdown/index.js +4 -3
  56. package/lib/plugins/respArea/inline-dropdown/index.js.map +1 -1
  57. package/lib/plugins/respArea/utils.js +17 -20
  58. package/lib/plugins/respArea/utils.js.map +1 -1
  59. package/lib/plugins/table/icons/index.js +1 -1
  60. package/lib/plugins/table/icons/index.js.map +1 -1
  61. package/lib/plugins/table/index.js +381 -212
  62. package/lib/plugins/table/index.js.map +1 -1
  63. package/lib/plugins/table/table-toolbar.js +5 -6
  64. package/lib/plugins/table/table-toolbar.js.map +1 -1
  65. package/lib/plugins/toolbar/default-toolbar.js +55 -11
  66. package/lib/plugins/toolbar/default-toolbar.js.map +1 -1
  67. package/lib/plugins/toolbar/done-button.js +1 -1
  68. package/lib/plugins/toolbar/done-button.js.map +1 -1
  69. package/lib/plugins/toolbar/editor-and-toolbar.js +186 -232
  70. package/lib/plugins/toolbar/editor-and-toolbar.js.map +1 -1
  71. package/lib/plugins/toolbar/index.js +1 -2
  72. package/lib/plugins/toolbar/index.js.map +1 -1
  73. package/lib/plugins/toolbar/toolbar-buttons.js +1 -1
  74. package/lib/plugins/toolbar/toolbar-buttons.js.map +1 -1
  75. package/lib/plugins/toolbar/toolbar.js +253 -239
  76. package/lib/plugins/toolbar/toolbar.js.map +1 -1
  77. package/lib/plugins/utils.js +27 -2
  78. package/lib/plugins/utils.js.map +1 -1
  79. package/lib/serialization.js +1 -1
  80. package/lib/serialization.js.map +1 -1
  81. package/lib/slate-editor.js +302 -0
  82. package/lib/test-serializer.js +189 -0
  83. package/lib/test-serializer.js.map +1 -0
  84. package/lib/theme.js +1 -1
  85. package/lib/theme.js.map +1 -1
  86. package/package.json +18 -14
  87. package/playground/image/data.js +20 -20
  88. package/playground/image/index.html +22 -20
  89. package/playground/image/index.jsx +12 -10
  90. package/playground/index.html +25 -23
  91. package/playground/mathquill/index.html +23 -20
  92. package/playground/mathquill/index.jsx +18 -22
  93. package/playground/prod-test/index.html +24 -20
  94. package/playground/prod-test/index.jsx +5 -3
  95. package/playground/schema-override/data.js +10 -10
  96. package/playground/schema-override/image-plugin.jsx +3 -4
  97. package/playground/schema-override/index.html +21 -19
  98. package/playground/schema-override/index.jsx +13 -14
  99. package/playground/serialization/data.js +10 -10
  100. package/playground/serialization/image-plugin.jsx +3 -4
  101. package/playground/serialization/index.html +22 -20
  102. package/playground/table-examples.html +5 -8
  103. package/playground/webpack.config.js +10 -10
  104. package/src/components.js +135 -0
  105. package/src/editor.jsx +478 -141
  106. package/src/index.jsx +71 -95
  107. package/src/new-serialization.jsx +291 -0
  108. package/src/parse-html.js +1 -1
  109. package/src/plugins/characters/custom-popper.js +7 -7
  110. package/src/plugins/characters/index.jsx +33 -34
  111. package/src/plugins/characters/utils.js +81 -81
  112. package/src/plugins/hotKeys/index.js +54 -0
  113. package/src/plugins/image/alt-dialog.jsx +4 -5
  114. package/src/plugins/image/component.jsx +106 -89
  115. package/src/plugins/image/image-toolbar.jsx +27 -19
  116. package/src/plugins/image/index.jsx +75 -43
  117. package/src/plugins/image/insert-image-handler.js +62 -27
  118. package/src/plugins/index.jsx +23 -41
  119. package/src/plugins/list/index.jsx +70 -95
  120. package/src/plugins/math/index.jsx +102 -82
  121. package/src/plugins/media/index.jsx +159 -124
  122. package/src/plugins/media/media-dialog.js +98 -71
  123. package/src/plugins/media/media-toolbar.jsx +8 -8
  124. package/src/plugins/media/media-wrapper.jsx +29 -30
  125. package/src/plugins/respArea/drag-in-the-blank/choice.jsx +21 -19
  126. package/src/plugins/respArea/drag-in-the-blank/index.jsx +14 -11
  127. package/src/plugins/respArea/explicit-constructed-response/index.jsx +7 -6
  128. package/src/plugins/respArea/icons/index.jsx +11 -14
  129. package/src/plugins/respArea/index.jsx +92 -52
  130. package/src/plugins/respArea/inline-dropdown/index.jsx +9 -8
  131. package/src/plugins/respArea/utils.jsx +26 -35
  132. package/src/plugins/table/icons/index.jsx +17 -11
  133. package/src/plugins/table/index.jsx +288 -231
  134. package/src/plugins/table/table-toolbar.jsx +15 -11
  135. package/src/plugins/toolbar/default-toolbar.jsx +65 -19
  136. package/src/plugins/toolbar/done-button.jsx +4 -4
  137. package/src/plugins/toolbar/editor-and-toolbar.jsx +150 -145
  138. package/src/plugins/toolbar/index.jsx +2 -3
  139. package/src/plugins/toolbar/toolbar-buttons.jsx +11 -11
  140. package/src/plugins/toolbar/toolbar.jsx +244 -221
  141. package/src/plugins/utils.js +21 -4
  142. package/src/serialization.jsx +32 -32
  143. package/src/test-serializer.js +139 -0
  144. package/src/test-serializer.js.rej +20 -0
@@ -1,10 +1,14 @@
1
1
  import React from 'react';
2
2
  import ReactDOM from 'react-dom';
3
- import { Inline } from 'slate';
3
+ import { Node as SlateNode, Transforms } from 'slate';
4
+ import { jsx } from 'slate-hyperscript';
5
+ import { ReactEditor } from 'slate-react';
4
6
  import TheatersIcon from '@material-ui/icons/Theaters';
5
7
  import VolumeUpIcon from '@material-ui/icons/VolumeUp';
6
- import debug from 'debug';
8
+ import get from 'lodash/get';
9
+ import omit from 'lodash/omit';
7
10
 
11
+ import debug from 'debug';
8
12
  import MediaDialog from './media-dialog';
9
13
  import MediaToolbar from './media-toolbar';
10
14
  import MediaWrapper from './media-wrapper';
@@ -14,10 +18,10 @@ const log = debug('@pie-lib:editable-html:plugins:image');
14
18
  const removeDialogs = () => {
15
19
  const prevDialogs = document.querySelectorAll('.insert-media-dialog');
16
20
 
17
- prevDialogs.forEach((s) => s.remove());
21
+ prevDialogs.forEach(s => s.remove());
18
22
  };
19
23
 
20
- export const insertDialog = (props) => {
24
+ export const insertDialog = props => {
21
25
  const newEl = document.createElement('div');
22
26
  const { type, callback, opts, ...rest } = props;
23
27
  const initialBodyOverflow = document.body.style.overflow;
@@ -49,17 +53,41 @@ export const insertDialog = (props) => {
49
53
  document.body.appendChild(newEl);
50
54
  };
51
55
 
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
+
52
79
  const types = ['audio', 'video'];
53
80
 
54
81
  export default function MediaPlugin(type, opts) {
55
82
  const toolbar = {
56
83
  icon: type === 'audio' ? <VolumeUpIcon /> : <TheatersIcon />,
57
- onClick: (value, onChange) => {
84
+ onClick: editor => {
58
85
  log('[toolbar] onClick');
59
- const inline = Inline.create({
86
+ const inline = {
60
87
  type: type,
61
88
  isVoid: true,
62
89
  data: {
90
+ newMedia: true,
63
91
  editing: false,
64
92
  ends: undefined,
65
93
  height: undefined,
@@ -67,87 +95,129 @@ export default function MediaPlugin(type, opts) {
67
95
  starts: undefined,
68
96
  src: undefined,
69
97
  url: undefined,
70
- width: undefined,
98
+ width: undefined
71
99
  },
72
- });
100
+ children: [{ text: '' }]
101
+ };
102
+
103
+ editor.insertNode(inline);
73
104
 
74
- const change = value.change().insertInline(inline);
75
- onChange(change);
76
105
  insertDialog({
77
106
  type,
78
107
  opts,
79
108
  callback: (val, data) => {
80
- const nodeIsThere = change.value.document.findDescendant((d) => d.key === inline.key);
109
+ const [node, nodePath] = getNodeBy(
110
+ editor,
111
+ descendant => descendant.type === type && get(descendant, 'data.newMedia')
112
+ );
81
113
 
82
- if (nodeIsThere) {
114
+ if (node) {
83
115
  if (!val) {
84
- const c = change.removeNodeByKey(inline.key);
85
- onChange(c, () => opts.focus());
116
+ editor.apply({
117
+ type: 'remove_node',
118
+ path: nodePath
119
+ });
86
120
  } else {
87
- const c = change.setNodeByKey(inline.key, { data });
88
- onChange(c, () => opts.focus('beginning', nodeIsThere));
121
+ editor.apply({
122
+ type: 'set_node',
123
+ path: nodePath,
124
+ properties: {
125
+ data: node.data
126
+ },
127
+ newProperties: {
128
+ data: {
129
+ ...data,
130
+ newMedia: false
131
+ }
132
+ }
133
+ });
89
134
  }
90
- } else {
91
- opts.focus();
92
135
  }
93
- },
136
+
137
+ moveFocusAfterMedia(editor, node);
138
+ }
94
139
  });
95
- },
96
- supports: (node) => node.object === 'inline' && node.type === type,
140
+ }
97
141
  };
98
142
 
99
143
  return {
100
144
  name: type,
101
145
  toolbar,
102
- deleteNode: (e, node, value, onChange) => {
103
- e.preventDefault();
104
- const change = value.change().removeNodeByKey(node.key);
146
+ rules: editor => {
147
+ const { isVoid, isInline } = editor;
148
+
149
+ editor.isVoid = element => {
150
+ return ['audio', 'video'].includes(element.type) ? true : isVoid(element);
151
+ };
152
+
153
+ editor.isInline = element => {
154
+ return ['audio', 'video'].includes(element.type) ? true : isInline(element);
155
+ };
105
156
 
106
- onChange(change);
157
+ return editor;
107
158
  },
159
+ supports: node => node.type === type,
108
160
  renderNode(props) {
109
161
  if (props.node.type === type) {
110
- const { node, key } = props;
162
+ const { node, editor } = props;
111
163
  const { data } = node;
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,
164
+ const { src, height, width, editing, tag, ...rest } = omit(data, ['newMedia', 'urlToUse']);
165
+ const attributes = { ...rest, ...props.attributes };
166
+ const handleEdit = event => {
167
+ const nodeToEdit = ReactEditor.toSlateNode(editor, event.target);
168
+ const nodePath = ReactEditor.findPath(editor, nodeToEdit);
169
+
170
+ editor.apply({
171
+ type: 'set_node',
172
+ path: nodePath,
173
+ properties: {
174
+ data: node.data
120
175
  },
176
+ newProperties: {
177
+ data: {
178
+ ...data,
179
+ editing: true
180
+ }
181
+ }
121
182
  });
122
183
 
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
- });
184
+ insertDialog({
185
+ ...data,
186
+ edit: true,
187
+ type,
188
+ opts,
189
+ callback: (val, data) => {
190
+ const [nodeIsThere, nodePath] = getNodeBy(
191
+ editor,
192
+ descendant => descendant.type === type && get(descendant, 'data.editing')
193
+ );
194
+
195
+ if (nodeIsThere && val) {
196
+ editor.apply({
197
+ type: 'set_node',
198
+ path: nodePath,
199
+ properties: {
200
+ data: node.data
201
+ },
202
+ newProperties: {
203
+ data: {
204
+ ...data,
205
+ editing: true
206
+ }
207
+ }
208
+ });
209
+ }
210
+ }
144
211
  });
145
212
  };
146
- const handleDelete = () => {
147
- const change = opts.createChange();
148
- const c = change.removeNodeByKey(node.key);
213
+ const handleDelete = event => {
214
+ const nodeToEdit = ReactEditor.toSlateNode(editor, event.target);
215
+ const nodePath = ReactEditor.findPath(editor, nodeToEdit);
149
216
 
150
- opts.onChange(c);
217
+ editor.apply({
218
+ type: 'remove_node',
219
+ path: nodePath
220
+ });
151
221
  };
152
222
  const style = {};
153
223
 
@@ -161,62 +231,35 @@ export default function MediaPlugin(type, opts) {
161
231
 
162
232
  if (tag === 'audio') {
163
233
  return (
164
- <MediaWrapper editor data-type={type} width={style.width} {...rest}>
234
+ <MediaWrapper data-type={type} width={style.width} attributes={attributes}>
165
235
  <audio controls="controls">
166
236
  <source type="audio/mp3" src={src} />
167
237
  </audio>
168
238
  <MediaToolbar hideEdit onRemove={handleDelete} />
239
+ {props.children}
169
240
  </MediaWrapper>
170
241
  );
171
242
  }
172
243
 
173
244
  return (
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} />
245
+ <MediaWrapper data-type={type} width={style.width} attributes={attributes}>
246
+ <div contentEditable={false}>
247
+ <iframe
248
+ frameBorder="0"
249
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
250
+ allowFullScreen
251
+ src={src}
252
+ editing={editing ? 1 : 0}
253
+ {...rest}
254
+ {...style}
255
+ />
256
+ <MediaToolbar onEdit={handleEdit} onRemove={handleDelete} />
257
+ </div>
258
+ {props.children}
185
259
  </MediaWrapper>
186
260
  );
187
261
  }
188
262
  },
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
- },
220
263
  };
221
264
  }
222
265
 
@@ -243,10 +286,8 @@ export const serialization = {
243
286
  const width = parseInt(el.getAttribute('width'), 10) || null;
244
287
  const height = parseInt(el.getAttribute('height'), 10) || null;
245
288
 
246
- const out = {
247
- object: 'inline',
248
- type: type,
249
- isVoid: true,
289
+ const out = jsx('element', {
290
+ type,
250
291
  data: {
251
292
  tag,
252
293
  src: src || el.getAttribute('src'),
@@ -256,29 +297,23 @@ export const serialization = {
256
297
  starts,
257
298
  title,
258
299
  width,
259
- url,
260
- },
261
- };
300
+ url
301
+ }
302
+ });
303
+
262
304
  log('return object: ', out);
263
305
  return out;
264
306
  },
265
- serialize(object /*, children*/) {
307
+ serialize(object) {
266
308
  const typeIndex = types.indexOf(object.type);
267
309
 
268
- if (typeIndex < 0) return;
310
+ if (typeIndex < 0) {
311
+ return;
312
+ }
269
313
 
270
314
  const type = types[typeIndex];
271
315
 
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');
316
+ const { editing, tag, ends, src, starts, title, width, height, url } = object.data || {};
282
317
  const style = {};
283
318
 
284
319
  if (width) {
@@ -294,11 +329,11 @@ export const serialization = {
294
329
  'data-ends': ends,
295
330
  'data-starts': starts,
296
331
  'data-title': title,
297
- 'data-url': url,
332
+ 'data-url': url
298
333
  };
299
334
  const props = {
300
335
  ...style,
301
- src,
336
+ src
302
337
  };
303
338
 
304
339
  if (tag === 'audio') {
@@ -320,5 +355,5 @@ export const serialization = {
320
355
  {...props}
321
356
  />
322
357
  );
323
- },
358
+ }
324
359
  };