@pie-lib/editable-html 7.17.4-next.50 → 7.17.4-next.501
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 +165 -0
- package/CHANGELOG.md +421 -0
- package/lib/editor.js +395 -174
- 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 +55 -45
- 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 +69 -63
- 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 +225 -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 +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 +39 -5
- package/src/plugins/toolbar/toolbar-buttons.jsx +13 -2
- package/src/plugins/toolbar/toolbar.jsx +22 -4
- package/src/serialization.jsx +19 -3
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;
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom';
|
|
3
|
+
import debug from 'debug';
|
|
4
|
+
import get from 'lodash/get';
|
|
5
|
+
|
|
6
|
+
import { PureToolbar } from '@pie-lib/math-toolbar';
|
|
7
|
+
|
|
8
|
+
import CustomPopper from './custom-popper';
|
|
9
|
+
import { insertSnackBar } from '../respArea/utils';
|
|
10
|
+
import { characterIcons, spanishConfig, specialConfig } from './utils';
|
|
11
|
+
const log = debug('@pie-lib:editable-html:plugins:characters');
|
|
12
|
+
|
|
13
|
+
const removePopOvers = () => {
|
|
14
|
+
const prevPopOvers = document.querySelectorAll('#mouse-over-popover');
|
|
15
|
+
|
|
16
|
+
log('[characters:removePopOvers]');
|
|
17
|
+
prevPopOvers.forEach(s => s.remove());
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const removeDialogs = () => {
|
|
21
|
+
const prevDialogs = document.querySelectorAll('.insert-character-dialog');
|
|
22
|
+
|
|
23
|
+
log('[characters:removeDialogs]');
|
|
24
|
+
prevDialogs.forEach(s => s.remove());
|
|
25
|
+
removePopOvers();
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const insertDialog = ({ editorDOM, value, callback, opts }) => {
|
|
29
|
+
const newEl = document.createElement('div');
|
|
30
|
+
|
|
31
|
+
log('[characters:insertDialog]');
|
|
32
|
+
|
|
33
|
+
removeDialogs();
|
|
34
|
+
|
|
35
|
+
newEl.className = 'insert-character-dialog';
|
|
36
|
+
|
|
37
|
+
let configToUse;
|
|
38
|
+
|
|
39
|
+
switch (true) {
|
|
40
|
+
case opts.language === 'spanish':
|
|
41
|
+
configToUse = spanishConfig;
|
|
42
|
+
break;
|
|
43
|
+
case opts.language === 'special':
|
|
44
|
+
configToUse = specialConfig;
|
|
45
|
+
break;
|
|
46
|
+
default:
|
|
47
|
+
configToUse = opts;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!configToUse.characters) {
|
|
51
|
+
insertSnackBar('No characters provided or language not recognized');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const layoutForCharacters = configToUse.characters.reduce(
|
|
56
|
+
(obj, arr) => {
|
|
57
|
+
if (arr.length >= obj.columns) {
|
|
58
|
+
obj.columns = arr.length;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return obj;
|
|
62
|
+
},
|
|
63
|
+
{ rows: configToUse.characters.length, columns: 0 }
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
let popoverEl;
|
|
67
|
+
|
|
68
|
+
const closePopOver = () => {
|
|
69
|
+
if (popoverEl) {
|
|
70
|
+
popoverEl.remove();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
removePopOvers();
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const renderPopOver = (event, el) => {
|
|
77
|
+
if (!event) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const infoStyle = { fontSize: '20px', lineHeight: '20px' };
|
|
82
|
+
|
|
83
|
+
closePopOver();
|
|
84
|
+
|
|
85
|
+
popoverEl = document.createElement('div');
|
|
86
|
+
ReactDOM.render(
|
|
87
|
+
<CustomPopper onClose={closePopOver} anchorEl={event.currentTarget}>
|
|
88
|
+
<div>{el.label}</div>
|
|
89
|
+
|
|
90
|
+
<div style={infoStyle}>{el.description}</div>
|
|
91
|
+
|
|
92
|
+
<div style={infoStyle}>{el.unicode}</div>
|
|
93
|
+
</CustomPopper>,
|
|
94
|
+
popoverEl
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
document.body.appendChild(newEl);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
let firstCallMade = false;
|
|
101
|
+
|
|
102
|
+
const listener = e => {
|
|
103
|
+
// this will be triggered right after setting it because
|
|
104
|
+
// this toolbar is added on the mousedown event
|
|
105
|
+
// so right after mouseup, the click will be triggered
|
|
106
|
+
if (firstCallMade) {
|
|
107
|
+
const focusIsInModals =
|
|
108
|
+
newEl.contains(e.target) || (popoverEl && popoverEl.contains(e.target));
|
|
109
|
+
const focusIsInEditor = editorDOM.contains(e.target);
|
|
110
|
+
|
|
111
|
+
if (!(focusIsInModals || focusIsInEditor)) {
|
|
112
|
+
handleClose();
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
firstCallMade = true;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const handleClose = () => {
|
|
120
|
+
callback(undefined, true);
|
|
121
|
+
newEl.remove();
|
|
122
|
+
closePopOver();
|
|
123
|
+
document.body.removeEventListener('click', listener);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const handleChange = val => {
|
|
127
|
+
if (typeof val === 'string') {
|
|
128
|
+
callback(val, true);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const el = (
|
|
133
|
+
<PureToolbar
|
|
134
|
+
autoFocus
|
|
135
|
+
noDecimal
|
|
136
|
+
hideInput
|
|
137
|
+
noLatexHandling
|
|
138
|
+
layoutForKeyPad={layoutForCharacters}
|
|
139
|
+
additionalKeys={configToUse.characters.reduce((arr, n) => {
|
|
140
|
+
arr = [
|
|
141
|
+
...arr,
|
|
142
|
+
...n.map(k => ({
|
|
143
|
+
name: get(k, 'name') || k,
|
|
144
|
+
write: get(k, 'write') || k,
|
|
145
|
+
label: get(k, 'label') || k,
|
|
146
|
+
category: 'character',
|
|
147
|
+
extraClass: 'character',
|
|
148
|
+
extraProps: {
|
|
149
|
+
...(k.extraProps || {}),
|
|
150
|
+
style: {
|
|
151
|
+
...(k.extraProps || {}).style,
|
|
152
|
+
border: '1px solid #000'
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
...(configToUse.hasPreview
|
|
156
|
+
? {
|
|
157
|
+
actions: { onMouseEnter: ev => renderPopOver(ev, k), onMouseLeave: closePopOver }
|
|
158
|
+
}
|
|
159
|
+
: {})
|
|
160
|
+
}))
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
return arr;
|
|
164
|
+
}, [])}
|
|
165
|
+
keypadMode="language"
|
|
166
|
+
onChange={handleChange}
|
|
167
|
+
onDone={handleClose}
|
|
168
|
+
/>
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
ReactDOM.render(el, newEl, () => {
|
|
172
|
+
const cursorItem = document.querySelector(`[data-key="${value.anchorKey}"]`);
|
|
173
|
+
|
|
174
|
+
if (cursorItem) {
|
|
175
|
+
const bodyRect = document.body.getBoundingClientRect();
|
|
176
|
+
const boundRect = cursorItem.getBoundingClientRect();
|
|
177
|
+
|
|
178
|
+
document.body.appendChild(newEl);
|
|
179
|
+
newEl.style.position = 'absolute';
|
|
180
|
+
newEl.style.top = `${boundRect.top + Math.abs(bodyRect.top) - newEl.offsetHeight - 10}px`;
|
|
181
|
+
newEl.style.zIndex = 99999;
|
|
182
|
+
|
|
183
|
+
const leftValue = `${boundRect.left +
|
|
184
|
+
Math.abs(bodyRect.left) +
|
|
185
|
+
cursorItem.offsetWidth +
|
|
186
|
+
10}px`;
|
|
187
|
+
|
|
188
|
+
const rightValue = `${boundRect.x}px`;
|
|
189
|
+
|
|
190
|
+
newEl.style.left = leftValue;
|
|
191
|
+
|
|
192
|
+
const leftAlignedWidth = newEl.offsetWidth;
|
|
193
|
+
|
|
194
|
+
newEl.style.left = 'unset';
|
|
195
|
+
newEl.style.right = rightValue;
|
|
196
|
+
|
|
197
|
+
const rightAlignedWidth = newEl.offsetWidth;
|
|
198
|
+
|
|
199
|
+
newEl.style.left = 'unset';
|
|
200
|
+
newEl.style.right = 'unset';
|
|
201
|
+
|
|
202
|
+
if (leftAlignedWidth >= rightAlignedWidth) {
|
|
203
|
+
newEl.style.left = leftValue;
|
|
204
|
+
} else {
|
|
205
|
+
newEl.style.right = rightValue;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
document.body.addEventListener('click', listener);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const CharacterIcon = ({ letter }) => (
|
|
214
|
+
<div
|
|
215
|
+
style={{
|
|
216
|
+
fontSize: '25px',
|
|
217
|
+
lineHeight: '15px'
|
|
218
|
+
}}
|
|
219
|
+
>
|
|
220
|
+
{letter}
|
|
221
|
+
</div>
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
export default function CharactersPlugin(opts) {
|
|
225
|
+
removeDialogs();
|
|
226
|
+
return {
|
|
227
|
+
name: 'characters',
|
|
228
|
+
toolbar: {
|
|
229
|
+
icon: <CharacterIcon letter={opts.characterIcon || characterIcons[opts.language] || 'ñ'} />,
|
|
230
|
+
onClick: (value, onChange, getFocusedValue) => {
|
|
231
|
+
const editorDOM = document.querySelector(`[data-key="${value.document.key}"]`);
|
|
232
|
+
let valueToUse = value;
|
|
233
|
+
const callback = (char, focus) => {
|
|
234
|
+
valueToUse = getFocusedValue();
|
|
235
|
+
|
|
236
|
+
if (char) {
|
|
237
|
+
const change = valueToUse
|
|
238
|
+
.change()
|
|
239
|
+
.insertTextByKey(valueToUse.anchorKey, valueToUse.anchorOffset, char);
|
|
240
|
+
|
|
241
|
+
valueToUse = change.value;
|
|
242
|
+
log('[characters:insert]: ', value);
|
|
243
|
+
onChange(change);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
log('[characters:click]');
|
|
247
|
+
|
|
248
|
+
if (focus) {
|
|
249
|
+
if (editorDOM) {
|
|
250
|
+
editorDOM.focus();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
insertDialog({ editorDOM, value: valueToUse, callback, opts });
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
pluginStyles: (node, parentNode, p) => {
|
|
260
|
+
if (p) {
|
|
261
|
+
return {
|
|
262
|
+
position: 'absolute',
|
|
263
|
+
top: 'initial'
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
}
|