@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,59 +1,45 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { jsx } from 'slate-hyperscript';
|
|
3
|
-
import debug from 'debug';
|
|
4
|
-
import get from 'lodash/get';
|
|
5
|
-
import { Node as SlateNode, Editor } from 'slate';
|
|
1
|
+
import { Data, Inline } from 'slate';
|
|
6
2
|
|
|
7
3
|
import Image from '@material-ui/icons/Image';
|
|
8
4
|
import ImageComponent from './component';
|
|
9
5
|
import ImageToolbar from './image-toolbar';
|
|
10
6
|
import InsertImageHandler from './insert-image-handler';
|
|
11
|
-
import
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import debug from 'debug';
|
|
12
9
|
|
|
13
10
|
const log = debug('@pie-lib:editable-html:plugins:image');
|
|
14
11
|
|
|
15
12
|
export default function ImagePlugin(opts) {
|
|
16
13
|
const toolbar = opts.insertImageRequested && {
|
|
17
14
|
icon: <Image />,
|
|
18
|
-
onClick:
|
|
15
|
+
onClick: (value, onChange) => {
|
|
19
16
|
log('[toolbar] onClick');
|
|
20
|
-
const inline = {
|
|
17
|
+
const inline = Inline.create({
|
|
21
18
|
type: 'image',
|
|
19
|
+
isVoid: true,
|
|
22
20
|
data: {
|
|
23
|
-
newImage: true,
|
|
24
21
|
loaded: false,
|
|
25
22
|
src: undefined,
|
|
26
23
|
},
|
|
27
|
-
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
editor.insertNode(inline);
|
|
31
|
-
|
|
32
|
-
// get the element just inserted
|
|
33
|
-
const [node, nodePath] = Editor.node(editor, editor.selection);
|
|
24
|
+
});
|
|
34
25
|
|
|
35
|
-
|
|
26
|
+
const change = value.change().insertInline(inline);
|
|
27
|
+
onChange(change);
|
|
28
|
+
opts.insertImageRequested((getValue) => new InsertImageHandler(inline, getValue, onChange));
|
|
36
29
|
},
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
30
|
+
supports: (node) => node.object === 'inline' && node.type === 'image',
|
|
31
|
+
customToolbar: (node, value, onToolbarDone) => {
|
|
32
|
+
const alignment = node.data.get('alignment');
|
|
33
|
+
const alt = node.data.get('alt');
|
|
34
|
+
const imageLoaded = node.data.get('loaded') !== false;
|
|
35
|
+
const onChange = (newValues, done) => {
|
|
42
36
|
const update = {
|
|
43
|
-
...node.data,
|
|
44
|
-
...newValues
|
|
37
|
+
...node.data.toObject(),
|
|
38
|
+
...newValues,
|
|
45
39
|
};
|
|
46
40
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
path: nodePath,
|
|
50
|
-
properties: {
|
|
51
|
-
data: node.data
|
|
52
|
-
},
|
|
53
|
-
newProperties: { data: update }
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
onToolbarDone(null, false);
|
|
41
|
+
const change = value.change().setNodeByKey(node.key, { data: update });
|
|
42
|
+
onToolbarDone(change, done);
|
|
57
43
|
};
|
|
58
44
|
|
|
59
45
|
const Tb = () => (
|
|
@@ -73,72 +59,26 @@ export default function ImagePlugin(opts) {
|
|
|
73
59
|
return {
|
|
74
60
|
name: 'image',
|
|
75
61
|
toolbar,
|
|
76
|
-
|
|
77
|
-
const { isVoid, isInline } = editor;
|
|
78
|
-
|
|
79
|
-
editor.isVoid = element => {
|
|
80
|
-
return element.type === 'image' ? true : isVoid(element);
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
editor.isInline = element => {
|
|
84
|
-
return element.type === 'image' ? true : isInline(element);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
return editor;
|
|
88
|
-
},
|
|
89
|
-
supports: node => node.type === 'image',
|
|
90
|
-
deleteNode: (e, node, nodePath, editor, onChange) => {
|
|
62
|
+
deleteNode: (e, node, value, onChange) => {
|
|
91
63
|
e.preventDefault();
|
|
92
|
-
|
|
93
64
|
if (opts.onDelete) {
|
|
94
|
-
const update = {
|
|
95
|
-
...node.data,
|
|
96
|
-
deleteStatus: 'pending'
|
|
97
|
-
};
|
|
65
|
+
const update = node.data.merge(Data.create({ deleteStatus: 'pending' }));
|
|
98
66
|
|
|
99
|
-
|
|
100
|
-
type: 'set_node',
|
|
101
|
-
path: nodePath,
|
|
102
|
-
properties: {
|
|
103
|
-
data: node.data
|
|
104
|
-
},
|
|
105
|
-
newProperties: { data: update }
|
|
106
|
-
});
|
|
67
|
+
let change = value.change().setNodeByKey(node.key, { data: update });
|
|
107
68
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
opts.onDelete(node.data.src, (err) => {
|
|
69
|
+
onChange(change);
|
|
70
|
+
opts.onDelete(node.data.get('src'), (err, v) => {
|
|
111
71
|
if (!err) {
|
|
112
|
-
|
|
113
|
-
type: 'remove_node',
|
|
114
|
-
path: nodePath
|
|
115
|
-
});
|
|
72
|
+
change = v.change().removeNodeByKey(node.key);
|
|
116
73
|
} else {
|
|
117
74
|
log('[error]: ', err);
|
|
118
|
-
|
|
119
|
-
type: 'set_node',
|
|
120
|
-
path: nodePath,
|
|
121
|
-
properties: {
|
|
122
|
-
data: node.data
|
|
123
|
-
},
|
|
124
|
-
newProperties: { data: { ...node.data, deleteStatus: 'failed' } },
|
|
125
|
-
});
|
|
75
|
+
change = v.change().setNodeByKey(node.key, node.data.merge(Data.create({ deleteStatus: 'failed' })));
|
|
126
76
|
}
|
|
127
|
-
|
|
128
|
-
editor.selection = null;
|
|
129
|
-
onChange(editor, () => {
|
|
130
|
-
setTimeout(() => ReactEditor.focus(editor), 50);
|
|
131
|
-
});
|
|
77
|
+
onChange(change);
|
|
132
78
|
});
|
|
133
79
|
} else {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
type: 'remove_node',
|
|
137
|
-
path: nodePath
|
|
138
|
-
});
|
|
139
|
-
onChange(editor, () => {
|
|
140
|
-
setTimeout(() => ReactEditor.focus(editor), 50);
|
|
141
|
-
});
|
|
80
|
+
let change = value.change().removeNodeByKey(node.key);
|
|
81
|
+
onChange(change);
|
|
142
82
|
}
|
|
143
83
|
},
|
|
144
84
|
stopReset: (value) => {
|
|
@@ -146,7 +86,7 @@ export default function ImagePlugin(opts) {
|
|
|
146
86
|
if (n.type !== 'image') {
|
|
147
87
|
return;
|
|
148
88
|
}
|
|
149
|
-
return n.data.loaded === false;
|
|
89
|
+
return n.data.get('loaded') === false;
|
|
150
90
|
});
|
|
151
91
|
/** don't reset if there is an image pending insertion */
|
|
152
92
|
return imgPendingInsertion !== undefined && imgPendingInsertion !== null;
|
|
@@ -163,11 +103,7 @@ export default function ImagePlugin(opts) {
|
|
|
163
103
|
},
|
|
164
104
|
props,
|
|
165
105
|
);
|
|
166
|
-
return
|
|
167
|
-
<ImageComponent {...all}>
|
|
168
|
-
{props.children}
|
|
169
|
-
</ImageComponent>
|
|
170
|
-
);
|
|
106
|
+
return <ImageComponent {...all} />;
|
|
171
107
|
}
|
|
172
108
|
},
|
|
173
109
|
normalizeNode: (node) => {
|
|
@@ -212,8 +148,10 @@ export const serialization = {
|
|
|
212
148
|
const width = parseInt(style.width.replace('px', ''), 10) || null;
|
|
213
149
|
const height = parseInt(style.height.replace('px', ''), 10) || null;
|
|
214
150
|
|
|
215
|
-
const out =
|
|
151
|
+
const out = {
|
|
152
|
+
object: 'inline',
|
|
216
153
|
type: 'image',
|
|
154
|
+
isVoid: true,
|
|
217
155
|
data: {
|
|
218
156
|
src: el.getAttribute('src'),
|
|
219
157
|
width,
|
|
@@ -223,7 +161,7 @@ export const serialization = {
|
|
|
223
161
|
alignment: el.getAttribute('alignment'),
|
|
224
162
|
alt: el.getAttribute('alt'),
|
|
225
163
|
},
|
|
226
|
-
}
|
|
164
|
+
};
|
|
227
165
|
log('return object: ', out);
|
|
228
166
|
return out;
|
|
229
167
|
},
|
|
@@ -231,17 +169,14 @@ export const serialization = {
|
|
|
231
169
|
if (object.type !== 'image') return;
|
|
232
170
|
|
|
233
171
|
const { data } = object;
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
width
|
|
242
|
-
} = data;
|
|
172
|
+
const src = data.get('src');
|
|
173
|
+
const width = data.get('width');
|
|
174
|
+
const height = data.get('height');
|
|
175
|
+
const alignment = data.get('alignment') || 'left';
|
|
176
|
+
const margin = data.get('margin');
|
|
177
|
+
const justifyContent = data.get('margin');
|
|
178
|
+
const alt = data.get('alt');
|
|
243
179
|
const style = {};
|
|
244
|
-
|
|
245
180
|
if (width) {
|
|
246
181
|
style.width = `${width}px`;
|
|
247
182
|
}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Data } from 'slate';
|
|
2
2
|
import debug from 'debug';
|
|
3
3
|
|
|
4
4
|
const log = debug('@pie-lib:editable-html:image:insert-image-handler');
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Handles user selection, insertion (or cancellation) of an image into the editor.
|
|
8
|
-
* @param {Block}
|
|
8
|
+
* @param {Block} placeholderBlock - a block that has been added to the editor as a place holder for the image
|
|
9
9
|
* @param {Function} getValue - a function to return the value of the editor
|
|
10
10
|
* @param {Function} onChange - callback to notify changes applied by the handler
|
|
11
11
|
* @param {Boolean} isPasted - a boolean that keeps track if the file is pasted
|
|
12
12
|
*/
|
|
13
13
|
class InsertImageHandler {
|
|
14
|
-
constructor(
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
14
|
+
constructor(placeholderBlock, getValue, onChange, isPasted = false) {
|
|
15
|
+
this.placeholderBlock = placeholderBlock;
|
|
16
|
+
this.getValue = getValue;
|
|
17
|
+
this.onChange = onChange;
|
|
18
18
|
this.isPasted = isPasted;
|
|
19
19
|
this.chosenFile = null;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
getPlaceholderInDocument(value) {
|
|
23
23
|
const { document } = value;
|
|
24
|
-
const directChild = document.getChild(this.
|
|
24
|
+
const directChild = document.getChild(this.placeholderBlock.key);
|
|
25
25
|
|
|
26
26
|
if (directChild) {
|
|
27
27
|
return directChild;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
const child = document.getDescendant(this.
|
|
30
|
+
const child = document.getDescendant(this.placeholderBlock.key);
|
|
31
31
|
|
|
32
32
|
if (child) {
|
|
33
33
|
return child;
|
|
@@ -39,10 +39,10 @@ class InsertImageHandler {
|
|
|
39
39
|
|
|
40
40
|
cancel() {
|
|
41
41
|
log('insert cancelled');
|
|
42
|
-
this.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
const c = this.getValue()
|
|
43
|
+
.change()
|
|
44
|
+
.removeNodeByKey(this.placeholderBlock.key);
|
|
45
|
+
this.onChange(c);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
done(err, src) {
|
|
@@ -51,30 +51,12 @@ class InsertImageHandler {
|
|
|
51
51
|
//eslint-disable-next-line
|
|
52
52
|
console.log(err);
|
|
53
53
|
} else {
|
|
54
|
-
this.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
properties: {
|
|
58
|
-
data: this.node.data
|
|
59
|
-
},
|
|
60
|
-
newProperties: {
|
|
61
|
-
data: {
|
|
62
|
-
src,
|
|
63
|
-
loaded: true,
|
|
64
|
-
percent: 100
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
const newData = {
|
|
69
|
-
...this.node.data,
|
|
70
|
-
src,
|
|
71
|
-
loaded: true,
|
|
72
|
-
percent: 100
|
|
73
|
-
};
|
|
54
|
+
const value = this.getValue();
|
|
55
|
+
const child = this.getPlaceholderInDocument(value);
|
|
56
|
+
const data = child.data.merge(Data.create({ loaded: true, src, percent: 100 }));
|
|
74
57
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
});
|
|
58
|
+
const change = value.change().setNodeByKey(this.placeholderBlock.key, { data });
|
|
59
|
+
this.onChange(change);
|
|
78
60
|
}
|
|
79
61
|
}
|
|
80
62
|
|
|
@@ -94,40 +76,23 @@ class InsertImageHandler {
|
|
|
94
76
|
log('[fileChosen] file: ', file);
|
|
95
77
|
const reader = new FileReader();
|
|
96
78
|
reader.onload = () => {
|
|
79
|
+
const value = this.getValue();
|
|
97
80
|
const dataURL = reader.result;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
properties: {
|
|
103
|
-
data: this.node.data
|
|
104
|
-
},
|
|
105
|
-
newProperties: {
|
|
106
|
-
data: {
|
|
107
|
-
...this.node.data,
|
|
108
|
-
src: dataURL
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
this.node = Object.assign({}, this.node, { data: { src: dataURL } });
|
|
81
|
+
const child = this.getPlaceholderInDocument(value);
|
|
82
|
+
const data = child.data.set('src', dataURL);
|
|
83
|
+
const change = value.change().setNodeByKey(this.placeholderBlock.key, { data });
|
|
84
|
+
this.onChange(change);
|
|
113
85
|
};
|
|
114
86
|
reader.readAsDataURL(file);
|
|
115
87
|
}
|
|
116
88
|
|
|
117
89
|
progress(percent, bytes, total) {
|
|
118
90
|
log('progress: ', percent, bytes, total);
|
|
119
|
-
|
|
120
|
-
this.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
data: this.node.data
|
|
125
|
-
},
|
|
126
|
-
newProperties: {
|
|
127
|
-
data: { ...this.node.data, percent }
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
this.node = Object.assign({}, this.node, { data: { percent } });
|
|
91
|
+
const value = this.getValue();
|
|
92
|
+
const child = this.getPlaceholderInDocument(value);
|
|
93
|
+
const data = child.data.set('percent', percent);
|
|
94
|
+
const change = value.change().setNodeByKey(this.placeholderBlock.key, { data });
|
|
95
|
+
this.onChange(change);
|
|
131
96
|
}
|
|
132
97
|
|
|
133
98
|
// Add a getter method to retrieve the chosen file
|
package/src/plugins/index.jsx
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import { Editor } from 'slate';
|
|
2
|
-
import { withHistory } from 'slate-history';
|
|
3
|
-
import { withReact } from 'slate-react';
|
|
4
|
-
|
|
5
1
|
import Bold from '@material-ui/icons/FormatBold';
|
|
6
|
-
//
|
|
2
|
+
//import Code from '@material-ui/icons/Code';
|
|
7
3
|
import BulletedListIcon from '@material-ui/icons/FormatListBulleted';
|
|
8
4
|
import NumberedListIcon from '@material-ui/icons/FormatListNumbered';
|
|
9
5
|
import ImagePlugin from './image';
|
|
@@ -21,13 +17,49 @@ import debug from 'debug';
|
|
|
21
17
|
import List from './list';
|
|
22
18
|
import TablePlugin from './table';
|
|
23
19
|
import RespAreaPlugin from './respArea';
|
|
24
|
-
import
|
|
20
|
+
import HtmlPlugin from './html';
|
|
25
21
|
|
|
26
22
|
const log = debug('@pie-lib:editable-html:plugins');
|
|
27
23
|
|
|
24
|
+
function MarkHotkey(options) {
|
|
25
|
+
const { type, key, icon, tag } = options;
|
|
26
|
+
|
|
27
|
+
// Return our "plugin" object, containing the `onKeyDown` handler.
|
|
28
|
+
return {
|
|
29
|
+
toolbar: {
|
|
30
|
+
isMark: true,
|
|
31
|
+
type,
|
|
32
|
+
icon,
|
|
33
|
+
onToggle: (change) => {
|
|
34
|
+
log('[onToggleMark] type: ', type);
|
|
35
|
+
return change.toggleMark(type);
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
renderMark(props) {
|
|
39
|
+
if (props.mark.type === type) {
|
|
40
|
+
const K = tag || type;
|
|
41
|
+
|
|
42
|
+
return <K>{props.children}</K>;
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
onKeyDown(event, change) {
|
|
46
|
+
// Check that the key pressed matches our `key` option.
|
|
47
|
+
if (!event.metaKey || event.key != key) return;
|
|
48
|
+
|
|
49
|
+
// Prevent the default characters from being inserted.
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
|
|
52
|
+
// Toggle the mark `type`.
|
|
53
|
+
change.toggleMark(type);
|
|
54
|
+
return true;
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
28
59
|
export const ALL_PLUGINS = [
|
|
29
60
|
'bold',
|
|
30
61
|
// 'code',
|
|
62
|
+
'html',
|
|
31
63
|
'italic',
|
|
32
64
|
'underline',
|
|
33
65
|
'strikethrough',
|
|
@@ -80,20 +112,6 @@ export const buildPlugins = (activePlugins, opts) => {
|
|
|
80
112
|
ToolbarPlugin(opts.toolbar),
|
|
81
113
|
SoftBreakPlugin({ shift: true }),
|
|
82
114
|
addIf('responseArea', respAreaPlugin),
|
|
115
|
+
addIf('html', HtmlPlugin(opts.html)),
|
|
83
116
|
]);
|
|
84
117
|
};
|
|
85
|
-
|
|
86
|
-
export const withPlugins = (editor, activePlugins) => {
|
|
87
|
-
editor.continueNormalization = () => {
|
|
88
|
-
Editor.setNormalizing(editor, true);
|
|
89
|
-
Editor.normalize(editor, { force: true });
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
activePlugins.forEach(plugin => {
|
|
93
|
-
if (typeof plugin.rules === 'function') {
|
|
94
|
-
plugin.rules(editor);
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
return withHistory(withReact(editor));
|
|
99
|
-
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { Data } from 'slate';
|
|
3
|
+
import Immutable from 'immutable';
|
|
2
4
|
import PropTypes from 'prop-types';
|
|
5
|
+
import EditList from 'slate-edit-list';
|
|
3
6
|
import debug from 'debug';
|
|
4
|
-
import { Editor, Element as SlateElement, Transforms } from 'slate';
|
|
5
|
-
import { jsx } from "slate-hyperscript";
|
|
6
7
|
|
|
7
8
|
const log = debug('@pie-lib:editable-html:plugins:list');
|
|
8
9
|
|
|
@@ -15,39 +16,22 @@ const b = (type, next, childNodes) => ({
|
|
|
15
16
|
export const serialization = {
|
|
16
17
|
deserialize(el, next) {
|
|
17
18
|
const name = el.tagName.toLowerCase();
|
|
18
|
-
const children = el.children.length ? Array.from(el.children) : el.childNodes;
|
|
19
19
|
|
|
20
20
|
if (name === 'li') {
|
|
21
|
-
return
|
|
22
|
-
'element',
|
|
23
|
-
{
|
|
24
|
-
type: 'li'
|
|
25
|
-
},
|
|
26
|
-
next(children)
|
|
27
|
-
);
|
|
21
|
+
return b('list_item', next, el.childNodes);
|
|
28
22
|
}
|
|
29
23
|
|
|
30
24
|
if (name === 'ul') {
|
|
31
|
-
return
|
|
32
|
-
'element',
|
|
33
|
-
{
|
|
34
|
-
type: 'ul'
|
|
35
|
-
},
|
|
36
|
-
next(children)
|
|
37
|
-
);
|
|
25
|
+
return b('ul_list', next, el.children.length ? Array.from(el.children) : el.childNodes);
|
|
38
26
|
}
|
|
39
27
|
|
|
40
28
|
if (name === 'ol') {
|
|
41
|
-
return
|
|
42
|
-
'element',
|
|
43
|
-
{
|
|
44
|
-
type: 'ol'
|
|
45
|
-
},
|
|
46
|
-
next(children)
|
|
47
|
-
);
|
|
29
|
+
return b('ol_list', next, el.children.length ? Array.from(el.children) : el.childNodes);
|
|
48
30
|
}
|
|
49
31
|
},
|
|
50
32
|
serialize(object, children) {
|
|
33
|
+
if (object.object !== 'block') return;
|
|
34
|
+
|
|
51
35
|
if (object.type === 'list_item') {
|
|
52
36
|
return <li>{children}</li>;
|
|
53
37
|
}
|
|
@@ -62,61 +46,88 @@ export const serialization = {
|
|
|
62
46
|
},
|
|
63
47
|
};
|
|
64
48
|
|
|
65
|
-
const isBlockActive = (editor, format) => {
|
|
66
|
-
const { selection } = editor;
|
|
67
|
-
if (!selection) return false;
|
|
68
|
-
|
|
69
|
-
const [match] = Array.from(
|
|
70
|
-
Editor.nodes(editor, {
|
|
71
|
-
at: Editor.unhangRange(editor, selection),
|
|
72
|
-
match: node => !Editor.isEditor(node) && SlateElement.isElement(node) && node.type === format
|
|
73
|
-
})
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
return !!match;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
49
|
const createEditList = () => {
|
|
80
|
-
const core = {
|
|
81
|
-
|
|
82
|
-
};
|
|
50
|
+
const core = EditList({
|
|
51
|
+
typeDefault: 'span',
|
|
52
|
+
});
|
|
83
53
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
54
|
+
// fix outdated schema
|
|
55
|
+
if (core.schema && core.schema.blocks) {
|
|
56
|
+
Object.keys(core.schema.blocks).forEach((key) => {
|
|
57
|
+
const block = core.schema.blocks[key];
|
|
87
58
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
split: true
|
|
59
|
+
if (block.parent) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
block.nodes[0] = { type: block.nodes[0].types[0] };
|
|
94
64
|
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* This override of the core.changes.wrapInList is needed because the version
|
|
69
|
+
* of immutable that we have does not support getting the element at a specific
|
|
70
|
+
* index with a square bracket (list[0]). We have to use the list.get function instead
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Returns the highest list of blocks that cover the current selection
|
|
75
|
+
*/
|
|
76
|
+
const getHighestSelectedBlocks = (value) => {
|
|
77
|
+
const range = value.selection;
|
|
78
|
+
const document = value.document;
|
|
79
|
+
|
|
80
|
+
const startBlock = document.getClosestBlock(range.startKey);
|
|
81
|
+
const endBlock = document.getClosestBlock(range.endKey);
|
|
82
|
+
|
|
83
|
+
if (startBlock === endBlock) {
|
|
84
|
+
return Immutable.List([startBlock]);
|
|
85
|
+
}
|
|
95
86
|
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
87
|
+
const ancestor = document.getCommonAncestor(startBlock.key, endBlock.key);
|
|
88
|
+
const startPath = ancestor.getPath(startBlock.key);
|
|
89
|
+
const endPath = ancestor.getPath(endBlock.key);
|
|
99
90
|
|
|
100
|
-
|
|
91
|
+
return ancestor.nodes.slice(startPath.get(0), endPath.get(0) + 1);
|
|
92
|
+
};
|
|
101
93
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Wrap the blocks in the current selection in a new list. Selected
|
|
96
|
+
* lists are merged together.
|
|
97
|
+
*/
|
|
98
|
+
core.changes.wrapInList = function(change, type, data) {
|
|
99
|
+
const selectedBlocks = getHighestSelectedBlocks(change.value);
|
|
100
|
+
|
|
101
|
+
// Wrap in container
|
|
102
|
+
change.wrapBlock({ type: type, data: Data.create(data) }, { normalize: false });
|
|
103
|
+
|
|
104
|
+
// Wrap in list items
|
|
105
|
+
selectedBlocks.forEach(function(node) {
|
|
106
|
+
if (core.utils.isList(node)) {
|
|
107
|
+
// Merge its items with the created list
|
|
108
|
+
node.nodes.forEach(function(_ref) {
|
|
109
|
+
const key = _ref.key;
|
|
110
|
+
return change.unwrapNodeByKey(key, { normalize: false });
|
|
111
|
+
});
|
|
112
|
+
} else if (node.type !== 'list_item') {
|
|
113
|
+
change.wrapBlockByKey(node.key, 'list_item', {
|
|
114
|
+
normalize: false,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return change.normalize();
|
|
106
120
|
};
|
|
107
121
|
|
|
108
122
|
return core;
|
|
109
123
|
};
|
|
110
124
|
|
|
111
|
-
const LIST_TYPES = ['ol_list', 'ul_list', 'list_item'];
|
|
112
|
-
|
|
113
125
|
export default (options) => {
|
|
114
126
|
const { type, icon } = options;
|
|
115
127
|
|
|
116
128
|
const core = createEditList();
|
|
117
129
|
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
// eslint-disable-next-line react/display-name
|
|
120
131
|
core.renderNode = (props) => {
|
|
121
132
|
const { node, attributes, children } = props;
|
|
122
133
|
|
|
@@ -134,10 +145,24 @@ export default (options) => {
|
|
|
134
145
|
isMark: false,
|
|
135
146
|
type,
|
|
136
147
|
icon,
|
|
137
|
-
isActive:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
148
|
+
isActive: (value, type) => {
|
|
149
|
+
if (!core.utils.isSelectionInList(value)) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
const current = core.utils.getCurrentList(value);
|
|
153
|
+
return current ? current.type === type : false;
|
|
154
|
+
},
|
|
155
|
+
onClick: (value, onChange) => {
|
|
156
|
+
log('[onClick]', value);
|
|
157
|
+
const inList = core.utils.isSelectionInList(value);
|
|
158
|
+
if (inList) {
|
|
159
|
+
const change = value.change().call(core.changes.unwrapList);
|
|
160
|
+
onChange(change);
|
|
161
|
+
} else {
|
|
162
|
+
const change = value.change().call(core.changes.wrapInList, type);
|
|
163
|
+
onChange(change);
|
|
164
|
+
}
|
|
165
|
+
},
|
|
141
166
|
};
|
|
142
167
|
|
|
143
168
|
core.renderNode.propTypes = {
|