@pie-lib/editable-html 7.17.4-next.53 → 7.17.4-next.549
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 +385 -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 +26 -25
- package/lib/plugins/media/index.js.map +1 -1
- package/lib/plugins/media/media-dialog.js +45 -56
- 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 +218 -25
- 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 +3 -0
- package/src/plugins/media/media-dialog.js +1 -1
- 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.549+902972ab",
|
|
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": "902972ab86b0fcd40ddd0e7c809efe9922605668",
|
|
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,7 @@ export class Editor extends React.Component {
|
|
|
43
54
|
focus: PropTypes.func.isRequired,
|
|
44
55
|
value: SlateTypes.value.isRequired,
|
|
45
56
|
imageSupport: PropTypes.object,
|
|
57
|
+
charactersLimit: PropTypes.number,
|
|
46
58
|
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
47
59
|
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
48
60
|
minHeight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
@@ -50,6 +62,7 @@ export class Editor extends React.Component {
|
|
|
50
62
|
classes: PropTypes.object.isRequired,
|
|
51
63
|
highlightShape: PropTypes.bool,
|
|
52
64
|
disabled: PropTypes.bool,
|
|
65
|
+
spellCheck: PropTypes.bool,
|
|
53
66
|
nonEmpty: PropTypes.bool,
|
|
54
67
|
disableUnderline: PropTypes.bool,
|
|
55
68
|
autoWidthToolbar: PropTypes.bool,
|
|
@@ -63,8 +76,15 @@ export class Editor extends React.Component {
|
|
|
63
76
|
]),
|
|
64
77
|
options: PropTypes.object,
|
|
65
78
|
respAreaToolbar: PropTypes.func,
|
|
66
|
-
|
|
79
|
+
onHandleAreaChange: PropTypes.func
|
|
67
80
|
}),
|
|
81
|
+
languageCharactersProps: PropTypes.arrayOf(
|
|
82
|
+
PropTypes.shape({
|
|
83
|
+
language: PropTypes.string,
|
|
84
|
+
characterIcon: PropTypes.string,
|
|
85
|
+
characters: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string))
|
|
86
|
+
})
|
|
87
|
+
),
|
|
68
88
|
toolbarOpts: PropTypes.shape({
|
|
69
89
|
position: PropTypes.oneOf(['bottom', 'top']),
|
|
70
90
|
alignment: PropTypes.oneOf(['left', 'right']),
|
|
@@ -80,15 +100,19 @@ export class Editor extends React.Component {
|
|
|
80
100
|
new Error(`Invalid values: ${values}, values must be one of [${ALL_PLUGINS.join(',')}]`)
|
|
81
101
|
);
|
|
82
102
|
}),
|
|
83
|
-
className: PropTypes.string
|
|
103
|
+
className: PropTypes.string,
|
|
104
|
+
maxImageWidth: PropTypes.number,
|
|
105
|
+
maxImageHeight: PropTypes.number
|
|
84
106
|
};
|
|
85
107
|
|
|
86
108
|
static defaultProps = {
|
|
87
109
|
disableUnderline: true,
|
|
88
110
|
onFocus: () => {},
|
|
89
111
|
onBlur: () => {},
|
|
112
|
+
onKeyDown: () => {},
|
|
90
113
|
toolbarOpts: defaultToolbarOpts,
|
|
91
|
-
|
|
114
|
+
responseAreaProps: defaultResponseAreaProps,
|
|
115
|
+
languageCharactersProps: defaultLanguageCharactersProps
|
|
92
116
|
};
|
|
93
117
|
|
|
94
118
|
constructor(props) {
|
|
@@ -102,6 +126,15 @@ export class Editor extends React.Component {
|
|
|
102
126
|
props.onChange(this.state.value, true);
|
|
103
127
|
};
|
|
104
128
|
|
|
129
|
+
this.handlePlugins(this.props);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
handlePlugins = props => {
|
|
133
|
+
const normalizedResponseAreaProps = {
|
|
134
|
+
...defaultResponseAreaProps,
|
|
135
|
+
...props.responseAreaProps
|
|
136
|
+
};
|
|
137
|
+
|
|
105
138
|
this.plugins = buildPlugins(props.activePlugins, {
|
|
106
139
|
math: {
|
|
107
140
|
onClick: this.onMathClick,
|
|
@@ -110,25 +143,27 @@ export class Editor extends React.Component {
|
|
|
110
143
|
},
|
|
111
144
|
image: {
|
|
112
145
|
onDelete:
|
|
113
|
-
|
|
114
|
-
|
|
146
|
+
props.imageSupport &&
|
|
147
|
+
props.imageSupport.delete &&
|
|
115
148
|
((src, done) => {
|
|
116
|
-
|
|
149
|
+
props.imageSupport.delete(src, e => {
|
|
117
150
|
done(e, this.state.value);
|
|
118
151
|
});
|
|
119
152
|
}),
|
|
120
153
|
insertImageRequested:
|
|
121
|
-
|
|
154
|
+
props.imageSupport &&
|
|
122
155
|
(getHandler => {
|
|
123
156
|
/**
|
|
124
157
|
* The handler is the object through which the outer context
|
|
125
158
|
* communicates file upload events like: fileChosen, cancel, progress
|
|
126
159
|
*/
|
|
127
160
|
const handler = getHandler(() => this.state.value);
|
|
128
|
-
|
|
161
|
+
props.imageSupport.add(handler);
|
|
129
162
|
}),
|
|
130
163
|
onFocus: this.onPluginFocus,
|
|
131
|
-
onBlur: this.onPluginBlur
|
|
164
|
+
onBlur: this.onPluginBlur,
|
|
165
|
+
maxImageWidth: this.props.maxImageWidth,
|
|
166
|
+
maxImageHeight: this.props.maxImageHeight
|
|
132
167
|
},
|
|
133
168
|
toolbar: {
|
|
134
169
|
/**
|
|
@@ -138,13 +173,13 @@ export class Editor extends React.Component {
|
|
|
138
173
|
disableUnderline: props.disableUnderline,
|
|
139
174
|
autoWidth: props.autoWidthToolbar,
|
|
140
175
|
onDone: () => {
|
|
141
|
-
const { nonEmpty } =
|
|
176
|
+
const { nonEmpty } = props;
|
|
142
177
|
|
|
143
178
|
log('[onDone]');
|
|
144
179
|
this.setState({ toolbarInFocus: false, focusedNode: null });
|
|
145
180
|
this.editor.blur();
|
|
146
181
|
|
|
147
|
-
if (nonEmpty && this.state.value.startText
|
|
182
|
+
if (nonEmpty && this.state.value.startText?.text?.length === 0) {
|
|
148
183
|
this.resetValue(true).then(() => {
|
|
149
184
|
this.onEditingDone();
|
|
150
185
|
});
|
|
@@ -164,9 +199,12 @@ export class Editor extends React.Component {
|
|
|
164
199
|
}
|
|
165
200
|
},
|
|
166
201
|
responseArea: {
|
|
167
|
-
type:
|
|
168
|
-
options:
|
|
169
|
-
|
|
202
|
+
type: normalizedResponseAreaProps.type,
|
|
203
|
+
options: normalizedResponseAreaProps.options,
|
|
204
|
+
maxResponseAreas: normalizedResponseAreaProps.maxResponseAreas,
|
|
205
|
+
respAreaToolbar: normalizedResponseAreaProps.respAreaToolbar,
|
|
206
|
+
onHandleAreaChange: normalizedResponseAreaProps.onHandleAreaChange,
|
|
207
|
+
error: normalizedResponseAreaProps.error,
|
|
170
208
|
onFocus: () => {
|
|
171
209
|
log('[table:onFocus]...');
|
|
172
210
|
this.onPluginFocus();
|
|
@@ -176,13 +214,14 @@ export class Editor extends React.Component {
|
|
|
176
214
|
this.onPluginBlur();
|
|
177
215
|
}
|
|
178
216
|
},
|
|
217
|
+
languageCharacters: props.languageCharactersProps,
|
|
179
218
|
media: {
|
|
180
219
|
focus: this.focus,
|
|
181
220
|
createChange: () => this.state.value.change(),
|
|
182
221
|
onChange: this.onChange
|
|
183
222
|
}
|
|
184
223
|
});
|
|
185
|
-
}
|
|
224
|
+
};
|
|
186
225
|
|
|
187
226
|
componentDidMount() {
|
|
188
227
|
// onRef is needed to get the ref of the component because we export it using withStyles
|
|
@@ -216,6 +255,21 @@ export class Editor extends React.Component {
|
|
|
216
255
|
toolbarOpts: newToolbarOpts
|
|
217
256
|
});
|
|
218
257
|
}
|
|
258
|
+
|
|
259
|
+
if (!isEqual(nextProps.languageCharactersProps, this.props.languageCharactersProps)) {
|
|
260
|
+
this.handlePlugins(nextProps);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
componentDidUpdate() {
|
|
265
|
+
// The cursor is on a zero width element and when that is placed near void elements, it is not visible
|
|
266
|
+
// so we increase the width to at least 2px in order for the user to see it
|
|
267
|
+
const zeroWidthEls = document.querySelectorAll('[data-slate-zero-width="z"]');
|
|
268
|
+
|
|
269
|
+
Array.from(zeroWidthEls).forEach(el => {
|
|
270
|
+
el.style.minWidth = '2px';
|
|
271
|
+
el.style.display = 'inline-block';
|
|
272
|
+
});
|
|
219
273
|
}
|
|
220
274
|
|
|
221
275
|
onPluginBlur = e => {
|
|
@@ -277,7 +331,7 @@ export class Editor extends React.Component {
|
|
|
277
331
|
}
|
|
278
332
|
|
|
279
333
|
if (doneOn === 'blur') {
|
|
280
|
-
if (nonEmpty && this.state.value.startText
|
|
334
|
+
if (nonEmpty && this.state.value.startText?.text?.length === 0) {
|
|
281
335
|
this.resetValue(true).then(() => {
|
|
282
336
|
this.onEditingDone();
|
|
283
337
|
resolve();
|
|
@@ -298,7 +352,10 @@ export class Editor extends React.Component {
|
|
|
298
352
|
log('[onBlur] node: ', node);
|
|
299
353
|
|
|
300
354
|
return new Promise(resolve => {
|
|
301
|
-
this.setState(
|
|
355
|
+
this.setState(
|
|
356
|
+
{ preBlurValue: this.state.value, focusedNode: !node ? null : node },
|
|
357
|
+
this.handleBlur.bind(this, resolve)
|
|
358
|
+
);
|
|
302
359
|
this.props.onBlur(event);
|
|
303
360
|
});
|
|
304
361
|
};
|
|
@@ -307,6 +364,8 @@ export class Editor extends React.Component {
|
|
|
307
364
|
const editorDOM = document.querySelector(`[data-key="${this.state.value.document.key}"]`);
|
|
308
365
|
|
|
309
366
|
setTimeout(() => {
|
|
367
|
+
const { value: stateValue } = this.state;
|
|
368
|
+
|
|
310
369
|
if (!this.wrapperRef) {
|
|
311
370
|
return;
|
|
312
371
|
}
|
|
@@ -314,13 +373,17 @@ export class Editor extends React.Component {
|
|
|
314
373
|
const editorElement =
|
|
315
374
|
!editorDOM || document.activeElement.closest(`[class*="${editorDOM.className}"]`);
|
|
316
375
|
const toolbarElement =
|
|
317
|
-
!this.toolbarRef ||
|
|
376
|
+
!this.toolbarRef ||
|
|
377
|
+
document.activeElement.closest(`[class*="${this.toolbarRef.className}"]`);
|
|
318
378
|
const isInCurrentComponent =
|
|
319
379
|
this.wrapperRef.contains(editorElement) || this.wrapperRef.contains(toolbarElement);
|
|
320
380
|
|
|
321
381
|
if (!isInCurrentComponent) {
|
|
322
382
|
editorDOM.removeEventListener('blur', this.handleDomBlur);
|
|
323
|
-
|
|
383
|
+
|
|
384
|
+
if (stateValue.isFocused) {
|
|
385
|
+
this.onBlur(e);
|
|
386
|
+
}
|
|
324
387
|
}
|
|
325
388
|
}, 50);
|
|
326
389
|
};
|
|
@@ -412,7 +475,20 @@ export class Editor extends React.Component {
|
|
|
412
475
|
|
|
413
476
|
onChange = (change, done) => {
|
|
414
477
|
log('[onChange]');
|
|
415
|
-
|
|
478
|
+
|
|
479
|
+
const { value } = change;
|
|
480
|
+
const { charactersLimit } = this.props;
|
|
481
|
+
|
|
482
|
+
if (
|
|
483
|
+
value &&
|
|
484
|
+
value.document &&
|
|
485
|
+
value.document.text &&
|
|
486
|
+
value.document.text.length > charactersLimit
|
|
487
|
+
) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
this.setState({ value }, () => {
|
|
416
492
|
log('[onChange], call done()');
|
|
417
493
|
|
|
418
494
|
if (done) {
|
|
@@ -421,6 +497,14 @@ export class Editor extends React.Component {
|
|
|
421
497
|
});
|
|
422
498
|
};
|
|
423
499
|
|
|
500
|
+
getFocusedValue = () => {
|
|
501
|
+
if (this.state.value.isFocused) {
|
|
502
|
+
return this.state.value;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return this.state.preBlurValue;
|
|
506
|
+
};
|
|
507
|
+
|
|
424
508
|
UNSAFE_componentWillReceiveProps(props) {
|
|
425
509
|
if (!props.value.document.equals(this.props.value.document)) {
|
|
426
510
|
this.setState({
|
|
@@ -505,9 +589,102 @@ export class Editor extends React.Component {
|
|
|
505
589
|
this.props.focus(position, node);
|
|
506
590
|
};
|
|
507
591
|
|
|
592
|
+
onDropPaste = async (event, change, dropContext) => {
|
|
593
|
+
const editor = change.editor;
|
|
594
|
+
const transfer = getEventTransfer(event);
|
|
595
|
+
const file = transfer.files && transfer.files[0];
|
|
596
|
+
|
|
597
|
+
const type = transfer.type;
|
|
598
|
+
const fragment = transfer.fragment;
|
|
599
|
+
const text = transfer.text;
|
|
600
|
+
|
|
601
|
+
if (
|
|
602
|
+
file &&
|
|
603
|
+
(file.type === 'image/jpeg' || file.type === 'image/jpg' || file.type === 'image/png')
|
|
604
|
+
) {
|
|
605
|
+
if (!this.props.imageSupport) {
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
try {
|
|
609
|
+
log('[onDropPaste]');
|
|
610
|
+
const src = await getBase64(file);
|
|
611
|
+
const inline = Inline.create({
|
|
612
|
+
type: 'image',
|
|
613
|
+
isVoid: true,
|
|
614
|
+
data: {
|
|
615
|
+
loading: false,
|
|
616
|
+
src
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
if (dropContext) {
|
|
621
|
+
this.focus();
|
|
622
|
+
} else {
|
|
623
|
+
const range = getEventRange(event, editor);
|
|
624
|
+
if (range) {
|
|
625
|
+
change.select(range);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const ch = change.insertInline(inline);
|
|
630
|
+
this.onChange(ch);
|
|
631
|
+
} catch (err) {
|
|
632
|
+
log('[onDropPaste] error: ', err);
|
|
633
|
+
}
|
|
634
|
+
} else if (type === 'fragment') {
|
|
635
|
+
change.insertFragment(fragment);
|
|
636
|
+
} else if (type === 'text' || type === 'html') {
|
|
637
|
+
if (!text) {
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
const {
|
|
641
|
+
value: { document, selection, startBlock }
|
|
642
|
+
} = change;
|
|
643
|
+
|
|
644
|
+
if (startBlock.isVoid) {
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const defaultBlock = startBlock;
|
|
649
|
+
const defaultMarks = document.getInsertMarksAtRange(selection);
|
|
650
|
+
const frag = Plain.deserialize(text, {
|
|
651
|
+
defaultBlock,
|
|
652
|
+
defaultMarks
|
|
653
|
+
}).document;
|
|
654
|
+
change.insertFragment(frag);
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
renderPlaceholder = props => {
|
|
659
|
+
const { editor } = props;
|
|
660
|
+
const { document } = editor.value;
|
|
661
|
+
|
|
662
|
+
if (!editor.props.placeholder || document.text !== '' || document.nodes.size !== 1) {
|
|
663
|
+
return false;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
return (
|
|
667
|
+
<span
|
|
668
|
+
contentEditable={false}
|
|
669
|
+
style={{
|
|
670
|
+
display: 'inline-block',
|
|
671
|
+
width: 'fit-content', // for centering the placeholder if text-align is set to center
|
|
672
|
+
maxWidth: '100%',
|
|
673
|
+
whiteSpace: 'nowrap',
|
|
674
|
+
opacity: '0.33',
|
|
675
|
+
pointerEvents: 'none',
|
|
676
|
+
userSelect: 'none'
|
|
677
|
+
}}
|
|
678
|
+
>
|
|
679
|
+
{editor.props.placeholder}
|
|
680
|
+
</span>
|
|
681
|
+
);
|
|
682
|
+
};
|
|
683
|
+
|
|
508
684
|
render() {
|
|
509
685
|
const {
|
|
510
686
|
disabled,
|
|
687
|
+
spellCheck,
|
|
511
688
|
highlightShape,
|
|
512
689
|
classes,
|
|
513
690
|
className,
|
|
@@ -515,6 +692,7 @@ export class Editor extends React.Component {
|
|
|
515
692
|
pluginProps,
|
|
516
693
|
onKeyDown
|
|
517
694
|
} = this.props;
|
|
695
|
+
|
|
518
696
|
const { value, focusedNode, toolbarOpts } = this.state;
|
|
519
697
|
|
|
520
698
|
log('[render] value: ', value);
|
|
@@ -550,13 +728,22 @@ export class Editor extends React.Component {
|
|
|
550
728
|
focus={this.focus}
|
|
551
729
|
onKeyDown={onKeyDown}
|
|
552
730
|
onChange={this.onChange}
|
|
731
|
+
getFocusedValue={this.getFocusedValue}
|
|
553
732
|
onBlur={this.onBlur}
|
|
733
|
+
onDrop={(event, editor) => this.onDropPaste(event, editor, true)}
|
|
734
|
+
onPaste={(event, editor) => this.onDropPaste(event, editor)}
|
|
554
735
|
onFocus={this.onFocus}
|
|
555
736
|
onEditingDone={this.onEditingDone}
|
|
556
737
|
focusedNode={focusedNode}
|
|
557
738
|
normalize={this.normalize}
|
|
558
739
|
readOnly={disabled}
|
|
559
|
-
|
|
740
|
+
spellCheck={spellCheck}
|
|
741
|
+
className={classNames(
|
|
742
|
+
{
|
|
743
|
+
[classes.noPadding]: toolbarOpts && toolbarOpts.noBorder
|
|
744
|
+
},
|
|
745
|
+
classes.slateEditor
|
|
746
|
+
)}
|
|
560
747
|
style={{
|
|
561
748
|
minHeight: sizeStyle.minHeight,
|
|
562
749
|
height: sizeStyle.height,
|
|
@@ -565,6 +752,7 @@ export class Editor extends React.Component {
|
|
|
565
752
|
pluginProps={pluginProps}
|
|
566
753
|
toolbarOpts={toolbarOpts}
|
|
567
754
|
placeholder={placeholder}
|
|
755
|
+
renderPlaceholder={this.renderPlaceholder}
|
|
568
756
|
onDataChange={this.changeData}
|
|
569
757
|
/>
|
|
570
758
|
</div>
|
|
@@ -587,7 +775,7 @@ const styles = {
|
|
|
587
775
|
color: color.text(),
|
|
588
776
|
backgroundColor: color.background()
|
|
589
777
|
},
|
|
590
|
-
'& tr': {
|
|
778
|
+
'& table:not([border="1"]) tr': {
|
|
591
779
|
borderTop: '1px solid #dfe2e5'
|
|
592
780
|
// TODO perhaps secondary color for background, for now disable
|
|
593
781
|
// '&:nth-child(2n)': {
|
|
@@ -595,13 +783,18 @@ const styles = {
|
|
|
595
783
|
// }
|
|
596
784
|
},
|
|
597
785
|
'& td, th': {
|
|
598
|
-
border: '1px solid #dfe2e5',
|
|
599
786
|
padding: '.6em 1em',
|
|
600
787
|
textAlign: 'center'
|
|
788
|
+
},
|
|
789
|
+
'& table:not([border="1"]) td, th': {
|
|
790
|
+
border: '1px solid #dfe2e5'
|
|
601
791
|
}
|
|
602
792
|
},
|
|
603
793
|
toolbarOnTop: {
|
|
604
794
|
marginTop: '45px'
|
|
795
|
+
},
|
|
796
|
+
noPadding: {
|
|
797
|
+
padding: '0 !important'
|
|
605
798
|
}
|
|
606
799
|
};
|
|
607
800
|
|
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;
|