@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,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,
|
|
37
|
+
...node.data.toObject(),
|
|
44
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,7 +103,7 @@ export default function ImagePlugin(opts) {
|
|
|
163
103
|
},
|
|
164
104
|
props,
|
|
165
105
|
);
|
|
166
|
-
return <ImageComponent {...all}
|
|
106
|
+
return <ImageComponent {...all} />;
|
|
167
107
|
}
|
|
168
108
|
},
|
|
169
109
|
normalizeNode: (node) => {
|
|
@@ -208,8 +148,10 @@ export const serialization = {
|
|
|
208
148
|
const width = parseInt(style.width.replace('px', ''), 10) || null;
|
|
209
149
|
const height = parseInt(style.height.replace('px', ''), 10) || null;
|
|
210
150
|
|
|
211
|
-
const out =
|
|
151
|
+
const out = {
|
|
152
|
+
object: 'inline',
|
|
212
153
|
type: 'image',
|
|
154
|
+
isVoid: true,
|
|
213
155
|
data: {
|
|
214
156
|
src: el.getAttribute('src'),
|
|
215
157
|
width,
|
|
@@ -219,7 +161,7 @@ export const serialization = {
|
|
|
219
161
|
alignment: el.getAttribute('alignment'),
|
|
220
162
|
alt: el.getAttribute('alt'),
|
|
221
163
|
},
|
|
222
|
-
}
|
|
164
|
+
};
|
|
223
165
|
log('return object: ', out);
|
|
224
166
|
return out;
|
|
225
167
|
},
|
|
@@ -227,9 +169,14 @@ export const serialization = {
|
|
|
227
169
|
if (object.type !== 'image') return;
|
|
228
170
|
|
|
229
171
|
const { data } = object;
|
|
230
|
-
const
|
|
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');
|
|
231
179
|
const style = {};
|
|
232
|
-
|
|
233
180
|
if (width) {
|
|
234
181
|
style.width = `${width}px`;
|
|
235
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,58 +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
|
-
|
|
59
|
+
if (block.parent) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
block.nodes[0] = { type: block.nodes[0].types[0] };
|
|
91
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
|
+
}
|
|
92
86
|
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
87
|
+
const ancestor = document.getCommonAncestor(startBlock.key, endBlock.key);
|
|
88
|
+
const startPath = ancestor.getPath(startBlock.key);
|
|
89
|
+
const endPath = ancestor.getPath(endBlock.key);
|
|
96
90
|
|
|
97
|
-
|
|
91
|
+
return ancestor.nodes.slice(startPath.get(0), endPath.get(0) + 1);
|
|
92
|
+
};
|
|
98
93
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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();
|
|
103
120
|
};
|
|
104
121
|
|
|
105
122
|
return core;
|
|
106
123
|
};
|
|
107
124
|
|
|
108
|
-
const LIST_TYPES = ['ol_list', 'ul_list', 'list_item'];
|
|
109
|
-
|
|
110
125
|
export default (options) => {
|
|
111
126
|
const { type, icon } = options;
|
|
112
127
|
|
|
113
128
|
const core = createEditList();
|
|
114
129
|
|
|
115
|
-
|
|
116
|
-
|
|
130
|
+
// eslint-disable-next-line react/display-name
|
|
117
131
|
core.renderNode = (props) => {
|
|
118
132
|
const { node, attributes, children } = props;
|
|
119
133
|
|
|
@@ -131,9 +145,23 @@ export default (options) => {
|
|
|
131
145
|
isMark: false,
|
|
132
146
|
type,
|
|
133
147
|
icon,
|
|
134
|
-
isActive:
|
|
135
|
-
|
|
136
|
-
|
|
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
|
+
}
|
|
137
165
|
},
|
|
138
166
|
};
|
|
139
167
|
|