@pie-lib/editable-html 7.17.4-next.53 → 7.17.4-next.556
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 +150 -0
- package/CHANGELOG.md +421 -0
- package/lib/editor.js +390 -172
- package/lib/editor.js.map +1 -1
- package/lib/index.js +66 -53
- package/lib/index.js.map +1 -1
- package/lib/parse-html.js.map +1 -1
- package/lib/plugins/characters/custom-popper.js +73 -0
- package/lib/plugins/characters/custom-popper.js.map +1 -0
- package/lib/plugins/characters/index.js +285 -0
- package/lib/plugins/characters/index.js.map +1 -0
- package/lib/plugins/characters/utils.js +381 -0
- package/lib/plugins/characters/utils.js.map +1 -0
- package/lib/plugins/image/alt-dialog.js +119 -0
- package/lib/plugins/image/alt-dialog.js.map +1 -0
- package/lib/plugins/image/component.js +253 -77
- package/lib/plugins/image/component.js.map +1 -1
- package/lib/plugins/image/image-toolbar.js +95 -61
- package/lib/plugins/image/image-toolbar.js.map +1 -1
- package/lib/plugins/image/index.js +62 -20
- package/lib/plugins/image/index.js.map +1 -1
- package/lib/plugins/image/insert-image-handler.js +9 -15
- package/lib/plugins/image/insert-image-handler.js.map +1 -1
- package/lib/plugins/index.js +20 -12
- package/lib/plugins/index.js.map +1 -1
- package/lib/plugins/list/index.js +82 -14
- package/lib/plugins/list/index.js.map +1 -1
- package/lib/plugins/math/index.js +50 -55
- package/lib/plugins/math/index.js.map +1 -1
- package/lib/plugins/media/index.js +71 -27
- package/lib/plugins/media/index.js.map +1 -1
- package/lib/plugins/media/media-dialog.js +248 -72
- package/lib/plugins/media/media-dialog.js.map +1 -1
- package/lib/plugins/media/media-toolbar.js +24 -30
- package/lib/plugins/media/media-toolbar.js.map +1 -1
- package/lib/plugins/media/media-wrapper.js +28 -35
- package/lib/plugins/media/media-wrapper.js.map +1 -1
- package/lib/plugins/respArea/drag-in-the-blank/choice.js +68 -46
- package/lib/plugins/respArea/drag-in-the-blank/choice.js.map +1 -1
- package/lib/plugins/respArea/drag-in-the-blank/index.js +12 -12
- package/lib/plugins/respArea/drag-in-the-blank/index.js.map +1 -1
- package/lib/plugins/respArea/explicit-constructed-response/index.js +10 -9
- package/lib/plugins/respArea/explicit-constructed-response/index.js.map +1 -1
- package/lib/plugins/respArea/icons/index.js +11 -11
- package/lib/plugins/respArea/icons/index.js.map +1 -1
- package/lib/plugins/respArea/index.js +58 -42
- package/lib/plugins/respArea/index.js.map +1 -1
- package/lib/plugins/respArea/inline-dropdown/index.js +8 -8
- package/lib/plugins/respArea/inline-dropdown/index.js.map +1 -1
- package/lib/plugins/respArea/utils.js +5 -5
- package/lib/plugins/respArea/utils.js.map +1 -1
- package/lib/plugins/table/icons/index.js +12 -12
- package/lib/plugins/table/icons/index.js.map +1 -1
- package/lib/plugins/table/index.js +83 -27
- package/lib/plugins/table/index.js.map +1 -1
- package/lib/plugins/table/table-toolbar.js +41 -50
- package/lib/plugins/table/table-toolbar.js.map +1 -1
- package/lib/plugins/toolbar/default-toolbar.js +19 -13
- package/lib/plugins/toolbar/default-toolbar.js.map +1 -1
- package/lib/plugins/toolbar/done-button.js +5 -5
- package/lib/plugins/toolbar/done-button.js.map +1 -1
- package/lib/plugins/toolbar/editor-and-toolbar.js +51 -44
- package/lib/plugins/toolbar/editor-and-toolbar.js.map +1 -1
- package/lib/plugins/toolbar/index.js +5 -5
- package/lib/plugins/toolbar/index.js.map +1 -1
- package/lib/plugins/toolbar/toolbar-buttons.js +49 -52
- package/lib/plugins/toolbar/toolbar-buttons.js.map +1 -1
- package/lib/plugins/toolbar/toolbar.js +64 -62
- package/lib/plugins/toolbar/toolbar.js.map +1 -1
- package/lib/plugins/utils.js +1 -1
- package/lib/plugins/utils.js.map +1 -1
- package/lib/serialization.js +32 -9
- package/lib/serialization.js.map +1 -1
- package/lib/theme.js.map +1 -1
- package/package.json +7 -6
- package/src/editor.jsx +224 -26
- package/src/index.jsx +22 -5
- package/src/plugins/characters/custom-popper.js +48 -0
- package/src/plugins/characters/index.jsx +268 -0
- package/src/plugins/characters/utils.js +447 -0
- package/src/plugins/image/alt-dialog.jsx +69 -0
- package/src/plugins/image/component.jsx +204 -21
- package/src/plugins/image/image-toolbar.jsx +68 -22
- package/src/plugins/image/index.jsx +47 -9
- package/src/plugins/index.jsx +4 -1
- package/src/plugins/list/index.jsx +67 -5
- package/src/plugins/math/index.jsx +31 -37
- package/src/plugins/media/index.jsx +49 -6
- package/src/plugins/media/media-dialog.js +261 -89
- package/src/plugins/respArea/drag-in-the-blank/choice.jsx +28 -1
- package/src/plugins/respArea/explicit-constructed-response/index.jsx +3 -3
- package/src/plugins/respArea/index.jsx +50 -31
- package/src/plugins/table/index.jsx +63 -14
- package/src/plugins/toolbar/default-toolbar.jsx +20 -2
- package/src/plugins/toolbar/editor-and-toolbar.jsx +35 -4
- package/src/plugins/toolbar/toolbar-buttons.jsx +13 -2
- package/src/plugins/toolbar/toolbar.jsx +18 -3
- package/src/serialization.jsx +19 -3
package/lib/theme.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"file":"theme.js","names":["primary"],"sources":["../src/theme.js"],"sourcesContent":["export const primary = '#304ffe';\n"],"mappings":";;;;;;AAAO,IAAMA,OAAO,GAAG,SAAhB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie-lib/editable-html",
|
|
3
|
-
"version": "7.17.4-next.
|
|
3
|
+
"version": "7.17.4-next.556+f3344f79",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
"@material-ui/icons": "^3.0.2",
|
|
12
12
|
"@material-ui/styles": "^3.0.0-alpha.10",
|
|
13
13
|
"@pie-lib/drag": "^1.1.52",
|
|
14
|
-
"@pie-lib/math-rendering": "^2.
|
|
15
|
-
"@pie-lib/math-toolbar": "^1.
|
|
16
|
-
"@pie-lib/render-ui": "^4.
|
|
14
|
+
"@pie-lib/math-rendering": "^2.5.0",
|
|
15
|
+
"@pie-lib/math-toolbar": "^1.11.0",
|
|
16
|
+
"@pie-lib/render-ui": "^4.13.4",
|
|
17
17
|
"change-case": "^3.0.2",
|
|
18
18
|
"classnames": "^2.2.6",
|
|
19
19
|
"debug": "^4.1.1",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"slate-edit-list": "^0.11.3",
|
|
30
30
|
"slate-edit-table": "^0.17.0",
|
|
31
31
|
"slate-html-serializer": "^0.6.12",
|
|
32
|
+
"slate-plain-serializer": "^0.5.26",
|
|
32
33
|
"slate-prop-types": "^0.4.38",
|
|
33
34
|
"slate-react": "^0.14.3",
|
|
34
35
|
"slate-schema-violations": "^0.1.39",
|
|
@@ -36,7 +37,7 @@
|
|
|
36
37
|
"to-style": "^1.3.3"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
|
-
"@pie-framework/mathquill": "^1.1.
|
|
40
|
+
"@pie-framework/mathquill": "^1.1.3",
|
|
40
41
|
"react": "^16.8.1",
|
|
41
42
|
"react-dom": "^16.9.0"
|
|
42
43
|
},
|
|
@@ -46,6 +47,6 @@
|
|
|
46
47
|
"publishConfig": {
|
|
47
48
|
"access": "public"
|
|
48
49
|
},
|
|
49
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "f3344f79f8507c03371c2e1c957b8a9e9f2c266f",
|
|
50
51
|
"scripts": {}
|
|
51
52
|
}
|
package/src/editor.jsx
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import { Editor as SlateEditor, findNode } from 'slate-react';
|
|
1
|
+
import { Editor as SlateEditor, findNode, getEventRange, getEventTransfer } from 'slate-react';
|
|
2
2
|
import SlateTypes from 'slate-prop-types';
|
|
3
3
|
|
|
4
4
|
import isEqual from 'lodash/isEqual';
|
|
5
5
|
import * as serialization from './serialization';
|
|
6
6
|
import PropTypes from 'prop-types';
|
|
7
7
|
import React from 'react';
|
|
8
|
-
import { Value, Block } from 'slate';
|
|
8
|
+
import { Value, Block, Inline } from 'slate';
|
|
9
9
|
import { buildPlugins, ALL_PLUGINS, DEFAULT_PLUGINS } from './plugins';
|
|
10
10
|
import debug from 'debug';
|
|
11
11
|
import { withStyles } from '@material-ui/core/styles';
|
|
12
12
|
import classNames from 'classnames';
|
|
13
13
|
import { color } from '@pie-lib/render-ui';
|
|
14
|
+
import Plain from 'slate-plain-serializer';
|
|
15
|
+
|
|
16
|
+
import { getBase64 } from './serialization';
|
|
14
17
|
|
|
15
18
|
export { ALL_PLUGINS, DEFAULT_PLUGINS, serialization };
|
|
16
19
|
|
|
@@ -24,6 +27,14 @@ const defaultToolbarOpts = {
|
|
|
24
27
|
doneOn: 'blur'
|
|
25
28
|
};
|
|
26
29
|
|
|
30
|
+
const defaultResponseAreaProps = {
|
|
31
|
+
options: {},
|
|
32
|
+
respAreaToolbar: () => {},
|
|
33
|
+
onHandleAreaChange: () => {}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const defaultLanguageCharactersProps = [];
|
|
37
|
+
|
|
27
38
|
const createToolbarOpts = toolbarOpts => {
|
|
28
39
|
return {
|
|
29
40
|
...defaultToolbarOpts,
|
|
@@ -43,6 +54,11 @@ export class Editor extends React.Component {
|
|
|
43
54
|
focus: PropTypes.func.isRequired,
|
|
44
55
|
value: SlateTypes.value.isRequired,
|
|
45
56
|
imageSupport: PropTypes.object,
|
|
57
|
+
uploadSoundSupport: PropTypes.shape({
|
|
58
|
+
add: PropTypes.func,
|
|
59
|
+
delete: PropTypes.func
|
|
60
|
+
}),
|
|
61
|
+
charactersLimit: PropTypes.number,
|
|
46
62
|
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
47
63
|
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
48
64
|
minHeight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
@@ -50,6 +66,7 @@ export class Editor extends React.Component {
|
|
|
50
66
|
classes: PropTypes.object.isRequired,
|
|
51
67
|
highlightShape: PropTypes.bool,
|
|
52
68
|
disabled: PropTypes.bool,
|
|
69
|
+
spellCheck: PropTypes.bool,
|
|
53
70
|
nonEmpty: PropTypes.bool,
|
|
54
71
|
disableUnderline: PropTypes.bool,
|
|
55
72
|
autoWidthToolbar: PropTypes.bool,
|
|
@@ -63,8 +80,15 @@ export class Editor extends React.Component {
|
|
|
63
80
|
]),
|
|
64
81
|
options: PropTypes.object,
|
|
65
82
|
respAreaToolbar: PropTypes.func,
|
|
66
|
-
|
|
83
|
+
onHandleAreaChange: PropTypes.func
|
|
67
84
|
}),
|
|
85
|
+
languageCharactersProps: PropTypes.arrayOf(
|
|
86
|
+
PropTypes.shape({
|
|
87
|
+
language: PropTypes.string,
|
|
88
|
+
characterIcon: PropTypes.string,
|
|
89
|
+
characters: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string))
|
|
90
|
+
})
|
|
91
|
+
),
|
|
68
92
|
toolbarOpts: PropTypes.shape({
|
|
69
93
|
position: PropTypes.oneOf(['bottom', 'top']),
|
|
70
94
|
alignment: PropTypes.oneOf(['left', 'right']),
|
|
@@ -80,15 +104,19 @@ export class Editor extends React.Component {
|
|
|
80
104
|
new Error(`Invalid values: ${values}, values must be one of [${ALL_PLUGINS.join(',')}]`)
|
|
81
105
|
);
|
|
82
106
|
}),
|
|
83
|
-
className: PropTypes.string
|
|
107
|
+
className: PropTypes.string,
|
|
108
|
+
maxImageWidth: PropTypes.number,
|
|
109
|
+
maxImageHeight: PropTypes.number
|
|
84
110
|
};
|
|
85
111
|
|
|
86
112
|
static defaultProps = {
|
|
87
113
|
disableUnderline: true,
|
|
88
114
|
onFocus: () => {},
|
|
89
115
|
onBlur: () => {},
|
|
116
|
+
onKeyDown: () => {},
|
|
90
117
|
toolbarOpts: defaultToolbarOpts,
|
|
91
|
-
|
|
118
|
+
responseAreaProps: defaultResponseAreaProps,
|
|
119
|
+
languageCharactersProps: defaultLanguageCharactersProps
|
|
92
120
|
};
|
|
93
121
|
|
|
94
122
|
constructor(props) {
|
|
@@ -102,6 +130,15 @@ export class Editor extends React.Component {
|
|
|
102
130
|
props.onChange(this.state.value, true);
|
|
103
131
|
};
|
|
104
132
|
|
|
133
|
+
this.handlePlugins(this.props);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
handlePlugins = props => {
|
|
137
|
+
const normalizedResponseAreaProps = {
|
|
138
|
+
...defaultResponseAreaProps,
|
|
139
|
+
...props.responseAreaProps
|
|
140
|
+
};
|
|
141
|
+
|
|
105
142
|
this.plugins = buildPlugins(props.activePlugins, {
|
|
106
143
|
math: {
|
|
107
144
|
onClick: this.onMathClick,
|
|
@@ -110,25 +147,27 @@ export class Editor extends React.Component {
|
|
|
110
147
|
},
|
|
111
148
|
image: {
|
|
112
149
|
onDelete:
|
|
113
|
-
|
|
114
|
-
|
|
150
|
+
props.imageSupport &&
|
|
151
|
+
props.imageSupport.delete &&
|
|
115
152
|
((src, done) => {
|
|
116
|
-
|
|
153
|
+
props.imageSupport.delete(src, e => {
|
|
117
154
|
done(e, this.state.value);
|
|
118
155
|
});
|
|
119
156
|
}),
|
|
120
157
|
insertImageRequested:
|
|
121
|
-
|
|
158
|
+
props.imageSupport &&
|
|
122
159
|
(getHandler => {
|
|
123
160
|
/**
|
|
124
161
|
* The handler is the object through which the outer context
|
|
125
162
|
* communicates file upload events like: fileChosen, cancel, progress
|
|
126
163
|
*/
|
|
127
164
|
const handler = getHandler(() => this.state.value);
|
|
128
|
-
|
|
165
|
+
props.imageSupport.add(handler);
|
|
129
166
|
}),
|
|
130
167
|
onFocus: this.onPluginFocus,
|
|
131
|
-
onBlur: this.onPluginBlur
|
|
168
|
+
onBlur: this.onPluginBlur,
|
|
169
|
+
maxImageWidth: this.props.maxImageWidth,
|
|
170
|
+
maxImageHeight: this.props.maxImageHeight
|
|
132
171
|
},
|
|
133
172
|
toolbar: {
|
|
134
173
|
/**
|
|
@@ -138,13 +177,13 @@ export class Editor extends React.Component {
|
|
|
138
177
|
disableUnderline: props.disableUnderline,
|
|
139
178
|
autoWidth: props.autoWidthToolbar,
|
|
140
179
|
onDone: () => {
|
|
141
|
-
const { nonEmpty } =
|
|
180
|
+
const { nonEmpty } = props;
|
|
142
181
|
|
|
143
182
|
log('[onDone]');
|
|
144
183
|
this.setState({ toolbarInFocus: false, focusedNode: null });
|
|
145
184
|
this.editor.blur();
|
|
146
185
|
|
|
147
|
-
if (nonEmpty && this.state.value.startText
|
|
186
|
+
if (nonEmpty && this.state.value.startText?.text?.length === 0) {
|
|
148
187
|
this.resetValue(true).then(() => {
|
|
149
188
|
this.onEditingDone();
|
|
150
189
|
});
|
|
@@ -164,9 +203,12 @@ export class Editor extends React.Component {
|
|
|
164
203
|
}
|
|
165
204
|
},
|
|
166
205
|
responseArea: {
|
|
167
|
-
type:
|
|
168
|
-
options:
|
|
169
|
-
|
|
206
|
+
type: normalizedResponseAreaProps.type,
|
|
207
|
+
options: normalizedResponseAreaProps.options,
|
|
208
|
+
maxResponseAreas: normalizedResponseAreaProps.maxResponseAreas,
|
|
209
|
+
respAreaToolbar: normalizedResponseAreaProps.respAreaToolbar,
|
|
210
|
+
onHandleAreaChange: normalizedResponseAreaProps.onHandleAreaChange,
|
|
211
|
+
error: normalizedResponseAreaProps.error,
|
|
170
212
|
onFocus: () => {
|
|
171
213
|
log('[table:onFocus]...');
|
|
172
214
|
this.onPluginFocus();
|
|
@@ -176,13 +218,15 @@ export class Editor extends React.Component {
|
|
|
176
218
|
this.onPluginBlur();
|
|
177
219
|
}
|
|
178
220
|
},
|
|
221
|
+
languageCharacters: props.languageCharactersProps,
|
|
179
222
|
media: {
|
|
180
223
|
focus: this.focus,
|
|
181
224
|
createChange: () => this.state.value.change(),
|
|
182
|
-
onChange: this.onChange
|
|
225
|
+
onChange: this.onChange,
|
|
226
|
+
uploadSoundSupport: props.uploadSoundSupport
|
|
183
227
|
}
|
|
184
228
|
});
|
|
185
|
-
}
|
|
229
|
+
};
|
|
186
230
|
|
|
187
231
|
componentDidMount() {
|
|
188
232
|
// onRef is needed to get the ref of the component because we export it using withStyles
|
|
@@ -216,6 +260,21 @@ export class Editor extends React.Component {
|
|
|
216
260
|
toolbarOpts: newToolbarOpts
|
|
217
261
|
});
|
|
218
262
|
}
|
|
263
|
+
|
|
264
|
+
if (!isEqual(nextProps.languageCharactersProps, this.props.languageCharactersProps)) {
|
|
265
|
+
this.handlePlugins(nextProps);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
componentDidUpdate() {
|
|
270
|
+
// The cursor is on a zero width element and when that is placed near void elements, it is not visible
|
|
271
|
+
// so we increase the width to at least 2px in order for the user to see it
|
|
272
|
+
const zeroWidthEls = document.querySelectorAll('[data-slate-zero-width="z"]');
|
|
273
|
+
|
|
274
|
+
Array.from(zeroWidthEls).forEach(el => {
|
|
275
|
+
el.style.minWidth = '2px';
|
|
276
|
+
el.style.display = 'inline-block';
|
|
277
|
+
});
|
|
219
278
|
}
|
|
220
279
|
|
|
221
280
|
onPluginBlur = e => {
|
|
@@ -277,7 +336,7 @@ export class Editor extends React.Component {
|
|
|
277
336
|
}
|
|
278
337
|
|
|
279
338
|
if (doneOn === 'blur') {
|
|
280
|
-
if (nonEmpty && this.state.value.startText
|
|
339
|
+
if (nonEmpty && this.state.value.startText?.text?.length === 0) {
|
|
281
340
|
this.resetValue(true).then(() => {
|
|
282
341
|
this.onEditingDone();
|
|
283
342
|
resolve();
|
|
@@ -298,7 +357,10 @@ export class Editor extends React.Component {
|
|
|
298
357
|
log('[onBlur] node: ', node);
|
|
299
358
|
|
|
300
359
|
return new Promise(resolve => {
|
|
301
|
-
this.setState(
|
|
360
|
+
this.setState(
|
|
361
|
+
{ preBlurValue: this.state.value, focusedNode: !node ? null : node },
|
|
362
|
+
this.handleBlur.bind(this, resolve)
|
|
363
|
+
);
|
|
302
364
|
this.props.onBlur(event);
|
|
303
365
|
});
|
|
304
366
|
};
|
|
@@ -307,6 +369,8 @@ export class Editor extends React.Component {
|
|
|
307
369
|
const editorDOM = document.querySelector(`[data-key="${this.state.value.document.key}"]`);
|
|
308
370
|
|
|
309
371
|
setTimeout(() => {
|
|
372
|
+
const { value: stateValue } = this.state;
|
|
373
|
+
|
|
310
374
|
if (!this.wrapperRef) {
|
|
311
375
|
return;
|
|
312
376
|
}
|
|
@@ -314,13 +378,17 @@ export class Editor extends React.Component {
|
|
|
314
378
|
const editorElement =
|
|
315
379
|
!editorDOM || document.activeElement.closest(`[class*="${editorDOM.className}"]`);
|
|
316
380
|
const toolbarElement =
|
|
317
|
-
!this.toolbarRef ||
|
|
381
|
+
!this.toolbarRef ||
|
|
382
|
+
document.activeElement.closest(`[class*="${this.toolbarRef.className}"]`);
|
|
318
383
|
const isInCurrentComponent =
|
|
319
384
|
this.wrapperRef.contains(editorElement) || this.wrapperRef.contains(toolbarElement);
|
|
320
385
|
|
|
321
386
|
if (!isInCurrentComponent) {
|
|
322
387
|
editorDOM.removeEventListener('blur', this.handleDomBlur);
|
|
323
|
-
|
|
388
|
+
|
|
389
|
+
if (stateValue.isFocused) {
|
|
390
|
+
this.onBlur(e);
|
|
391
|
+
}
|
|
324
392
|
}
|
|
325
393
|
}, 50);
|
|
326
394
|
};
|
|
@@ -412,7 +480,20 @@ export class Editor extends React.Component {
|
|
|
412
480
|
|
|
413
481
|
onChange = (change, done) => {
|
|
414
482
|
log('[onChange]');
|
|
415
|
-
|
|
483
|
+
|
|
484
|
+
const { value } = change;
|
|
485
|
+
const { charactersLimit } = this.props;
|
|
486
|
+
|
|
487
|
+
if (
|
|
488
|
+
value &&
|
|
489
|
+
value.document &&
|
|
490
|
+
value.document.text &&
|
|
491
|
+
value.document.text.length > charactersLimit
|
|
492
|
+
) {
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
this.setState({ value }, () => {
|
|
416
497
|
log('[onChange], call done()');
|
|
417
498
|
|
|
418
499
|
if (done) {
|
|
@@ -421,6 +502,14 @@ export class Editor extends React.Component {
|
|
|
421
502
|
});
|
|
422
503
|
};
|
|
423
504
|
|
|
505
|
+
getFocusedValue = () => {
|
|
506
|
+
if (this.state.value.isFocused) {
|
|
507
|
+
return this.state.value;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return this.state.preBlurValue;
|
|
511
|
+
};
|
|
512
|
+
|
|
424
513
|
UNSAFE_componentWillReceiveProps(props) {
|
|
425
514
|
if (!props.value.document.equals(this.props.value.document)) {
|
|
426
515
|
this.setState({
|
|
@@ -505,9 +594,102 @@ export class Editor extends React.Component {
|
|
|
505
594
|
this.props.focus(position, node);
|
|
506
595
|
};
|
|
507
596
|
|
|
597
|
+
onDropPaste = async (event, change, dropContext) => {
|
|
598
|
+
const editor = change.editor;
|
|
599
|
+
const transfer = getEventTransfer(event);
|
|
600
|
+
const file = transfer.files && transfer.files[0];
|
|
601
|
+
|
|
602
|
+
const type = transfer.type;
|
|
603
|
+
const fragment = transfer.fragment;
|
|
604
|
+
const text = transfer.text;
|
|
605
|
+
|
|
606
|
+
if (
|
|
607
|
+
file &&
|
|
608
|
+
(file.type === 'image/jpeg' || file.type === 'image/jpg' || file.type === 'image/png')
|
|
609
|
+
) {
|
|
610
|
+
if (!this.props.imageSupport) {
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
try {
|
|
614
|
+
log('[onDropPaste]');
|
|
615
|
+
const src = await getBase64(file);
|
|
616
|
+
const inline = Inline.create({
|
|
617
|
+
type: 'image',
|
|
618
|
+
isVoid: true,
|
|
619
|
+
data: {
|
|
620
|
+
loading: false,
|
|
621
|
+
src
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
if (dropContext) {
|
|
626
|
+
this.focus();
|
|
627
|
+
} else {
|
|
628
|
+
const range = getEventRange(event, editor);
|
|
629
|
+
if (range) {
|
|
630
|
+
change.select(range);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const ch = change.insertInline(inline);
|
|
635
|
+
this.onChange(ch);
|
|
636
|
+
} catch (err) {
|
|
637
|
+
log('[onDropPaste] error: ', err);
|
|
638
|
+
}
|
|
639
|
+
} else if (type === 'fragment') {
|
|
640
|
+
change.insertFragment(fragment);
|
|
641
|
+
} else if (type === 'text' || type === 'html') {
|
|
642
|
+
if (!text) {
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
const {
|
|
646
|
+
value: { document, selection, startBlock }
|
|
647
|
+
} = change;
|
|
648
|
+
|
|
649
|
+
if (startBlock.isVoid) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const defaultBlock = startBlock;
|
|
654
|
+
const defaultMarks = document.getInsertMarksAtRange(selection);
|
|
655
|
+
const frag = Plain.deserialize(text, {
|
|
656
|
+
defaultBlock,
|
|
657
|
+
defaultMarks
|
|
658
|
+
}).document;
|
|
659
|
+
change.insertFragment(frag);
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
renderPlaceholder = props => {
|
|
664
|
+
const { editor } = props;
|
|
665
|
+
const { document } = editor.value;
|
|
666
|
+
|
|
667
|
+
if (!editor.props.placeholder || document.text !== '' || document.nodes.size !== 1) {
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
return (
|
|
672
|
+
<span
|
|
673
|
+
contentEditable={false}
|
|
674
|
+
style={{
|
|
675
|
+
display: 'inline-block',
|
|
676
|
+
width: 'fit-content', // for centering the placeholder if text-align is set to center
|
|
677
|
+
maxWidth: '100%',
|
|
678
|
+
whiteSpace: 'nowrap',
|
|
679
|
+
opacity: '0.33',
|
|
680
|
+
pointerEvents: 'none',
|
|
681
|
+
userSelect: 'none'
|
|
682
|
+
}}
|
|
683
|
+
>
|
|
684
|
+
{editor.props.placeholder}
|
|
685
|
+
</span>
|
|
686
|
+
);
|
|
687
|
+
};
|
|
688
|
+
|
|
508
689
|
render() {
|
|
509
690
|
const {
|
|
510
691
|
disabled,
|
|
692
|
+
spellCheck,
|
|
511
693
|
highlightShape,
|
|
512
694
|
classes,
|
|
513
695
|
className,
|
|
@@ -515,6 +697,7 @@ export class Editor extends React.Component {
|
|
|
515
697
|
pluginProps,
|
|
516
698
|
onKeyDown
|
|
517
699
|
} = this.props;
|
|
700
|
+
|
|
518
701
|
const { value, focusedNode, toolbarOpts } = this.state;
|
|
519
702
|
|
|
520
703
|
log('[render] value: ', value);
|
|
@@ -550,13 +733,22 @@ export class Editor extends React.Component {
|
|
|
550
733
|
focus={this.focus}
|
|
551
734
|
onKeyDown={onKeyDown}
|
|
552
735
|
onChange={this.onChange}
|
|
736
|
+
getFocusedValue={this.getFocusedValue}
|
|
553
737
|
onBlur={this.onBlur}
|
|
738
|
+
onDrop={(event, editor) => this.onDropPaste(event, editor, true)}
|
|
739
|
+
onPaste={(event, editor) => this.onDropPaste(event, editor)}
|
|
554
740
|
onFocus={this.onFocus}
|
|
555
741
|
onEditingDone={this.onEditingDone}
|
|
556
742
|
focusedNode={focusedNode}
|
|
557
743
|
normalize={this.normalize}
|
|
558
744
|
readOnly={disabled}
|
|
559
|
-
|
|
745
|
+
spellCheck={spellCheck}
|
|
746
|
+
className={classNames(
|
|
747
|
+
{
|
|
748
|
+
[classes.noPadding]: toolbarOpts && toolbarOpts.noBorder
|
|
749
|
+
},
|
|
750
|
+
classes.slateEditor
|
|
751
|
+
)}
|
|
560
752
|
style={{
|
|
561
753
|
minHeight: sizeStyle.minHeight,
|
|
562
754
|
height: sizeStyle.height,
|
|
@@ -565,6 +757,7 @@ export class Editor extends React.Component {
|
|
|
565
757
|
pluginProps={pluginProps}
|
|
566
758
|
toolbarOpts={toolbarOpts}
|
|
567
759
|
placeholder={placeholder}
|
|
760
|
+
renderPlaceholder={this.renderPlaceholder}
|
|
568
761
|
onDataChange={this.changeData}
|
|
569
762
|
/>
|
|
570
763
|
</div>
|
|
@@ -587,7 +780,7 @@ const styles = {
|
|
|
587
780
|
color: color.text(),
|
|
588
781
|
backgroundColor: color.background()
|
|
589
782
|
},
|
|
590
|
-
'& tr': {
|
|
783
|
+
'& table:not([border="1"]) tr': {
|
|
591
784
|
borderTop: '1px solid #dfe2e5'
|
|
592
785
|
// TODO perhaps secondary color for background, for now disable
|
|
593
786
|
// '&:nth-child(2n)': {
|
|
@@ -595,13 +788,18 @@ const styles = {
|
|
|
595
788
|
// }
|
|
596
789
|
},
|
|
597
790
|
'& td, th': {
|
|
598
|
-
border: '1px solid #dfe2e5',
|
|
599
791
|
padding: '.6em 1em',
|
|
600
792
|
textAlign: 'center'
|
|
793
|
+
},
|
|
794
|
+
'& table:not([border="1"]) td, th': {
|
|
795
|
+
border: '1px solid #dfe2e5'
|
|
601
796
|
}
|
|
602
797
|
},
|
|
603
798
|
toolbarOnTop: {
|
|
604
799
|
marginTop: '45px'
|
|
800
|
+
},
|
|
801
|
+
noPadding: {
|
|
802
|
+
padding: '0 !important'
|
|
605
803
|
}
|
|
606
804
|
};
|
|
607
805
|
|
package/src/index.jsx
CHANGED
|
@@ -17,6 +17,17 @@ export { htmlToValue, valueToHtml, Editor, DEFAULT_PLUGINS, ALL_PLUGINS };
|
|
|
17
17
|
* compare it. TODO: This is an interim fix, we'll need to strip back `Editor` and look how best to maintain the
|
|
18
18
|
* `markup` api whilst avoiding the serialization mismatch. We should be making better use of schemas w/ normalize.
|
|
19
19
|
*/
|
|
20
|
+
|
|
21
|
+
const reduceMultipleBrs = markup => {
|
|
22
|
+
try {
|
|
23
|
+
return markup.replace(/(<br\s*\/?>){3,}/gi, '<br>');
|
|
24
|
+
} catch (e) {
|
|
25
|
+
console.log("Couldn't remove <br/> tags: ", e);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return markup;
|
|
29
|
+
};
|
|
30
|
+
|
|
20
31
|
export default class EditableHtml extends React.Component {
|
|
21
32
|
static propTypes = {
|
|
22
33
|
onChange: PropTypes.func.isRequired,
|
|
@@ -44,10 +55,10 @@ export default class EditableHtml extends React.Component {
|
|
|
44
55
|
return;
|
|
45
56
|
}
|
|
46
57
|
|
|
47
|
-
const v = htmlToValue(props.markup);
|
|
48
|
-
const current = htmlToValue(this.props.markup);
|
|
58
|
+
const v = htmlToValue(reduceMultipleBrs(props.markup));
|
|
59
|
+
const current = htmlToValue(reduceMultipleBrs(this.props.markup));
|
|
49
60
|
|
|
50
|
-
if (!v.equals(current)) {
|
|
61
|
+
if (v.equals && !v.equals(current)) {
|
|
51
62
|
this.setState({ value: v });
|
|
52
63
|
}
|
|
53
64
|
}
|
|
@@ -84,9 +95,9 @@ export default class EditableHtml extends React.Component {
|
|
|
84
95
|
c.focus();
|
|
85
96
|
|
|
86
97
|
if (position === 'end' && lastText) {
|
|
87
|
-
c.moveFocusTo(lastText.key, lastText.text
|
|
98
|
+
c.moveFocusTo(lastText.key, lastText.text?.length).moveAnchorTo(
|
|
88
99
|
lastText.key,
|
|
89
|
-
lastText.text
|
|
100
|
+
lastText.text?.length
|
|
90
101
|
);
|
|
91
102
|
}
|
|
92
103
|
|
|
@@ -105,6 +116,12 @@ export default class EditableHtml extends React.Component {
|
|
|
105
116
|
|
|
106
117
|
render() {
|
|
107
118
|
const { value } = this.state;
|
|
119
|
+
const { toolbarOpts, error } = this.props;
|
|
120
|
+
|
|
121
|
+
if (toolbarOpts) {
|
|
122
|
+
toolbarOpts.error = error;
|
|
123
|
+
}
|
|
124
|
+
|
|
108
125
|
const props = {
|
|
109
126
|
...this.props,
|
|
110
127
|
markup: null,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
3
|
+
import Popper from '@material-ui/core/Popper';
|
|
4
|
+
import Typography from '@material-ui/core/Typography';
|
|
5
|
+
|
|
6
|
+
const styles = () => ({
|
|
7
|
+
popover: {
|
|
8
|
+
background: '#fff',
|
|
9
|
+
padding: '10px',
|
|
10
|
+
pointerEvents: 'none',
|
|
11
|
+
zIndex: 99999
|
|
12
|
+
},
|
|
13
|
+
paper: {
|
|
14
|
+
padding: 20,
|
|
15
|
+
height: 'auto',
|
|
16
|
+
width: 'auto'
|
|
17
|
+
},
|
|
18
|
+
typography: {
|
|
19
|
+
fontSize: 50,
|
|
20
|
+
textAlign: 'center'
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const CustomPopper = withStyles(styles)(({ classes, children, ...props }) => (
|
|
25
|
+
<Popper
|
|
26
|
+
id="mouse-over-popover"
|
|
27
|
+
open
|
|
28
|
+
className={classes.popover}
|
|
29
|
+
classes={{
|
|
30
|
+
paper: classes.paper
|
|
31
|
+
}}
|
|
32
|
+
anchorOrigin={{
|
|
33
|
+
vertical: 'bottom',
|
|
34
|
+
horizontal: 'left'
|
|
35
|
+
}}
|
|
36
|
+
transformOrigin={{
|
|
37
|
+
vertical: 'top',
|
|
38
|
+
horizontal: 'left'
|
|
39
|
+
}}
|
|
40
|
+
disableRestoreFocus
|
|
41
|
+
disableAutoFocus
|
|
42
|
+
{...props}
|
|
43
|
+
>
|
|
44
|
+
<Typography classes={{ root: classes.typography }}>{children}</Typography>
|
|
45
|
+
</Popper>
|
|
46
|
+
));
|
|
47
|
+
|
|
48
|
+
export default CustomPopper;
|