@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.
Files changed (98) hide show
  1. package/CHANGELOG.json +165 -0
  2. package/CHANGELOG.md +421 -0
  3. package/lib/editor.js +395 -174
  4. package/lib/editor.js.map +1 -1
  5. package/lib/index.js +66 -53
  6. package/lib/index.js.map +1 -1
  7. package/lib/parse-html.js.map +1 -1
  8. package/lib/plugins/characters/custom-popper.js +73 -0
  9. package/lib/plugins/characters/custom-popper.js.map +1 -0
  10. package/lib/plugins/characters/index.js +285 -0
  11. package/lib/plugins/characters/index.js.map +1 -0
  12. package/lib/plugins/characters/utils.js +381 -0
  13. package/lib/plugins/characters/utils.js.map +1 -0
  14. package/lib/plugins/image/alt-dialog.js +119 -0
  15. package/lib/plugins/image/alt-dialog.js.map +1 -0
  16. package/lib/plugins/image/component.js +253 -77
  17. package/lib/plugins/image/component.js.map +1 -1
  18. package/lib/plugins/image/image-toolbar.js +95 -61
  19. package/lib/plugins/image/image-toolbar.js.map +1 -1
  20. package/lib/plugins/image/index.js +62 -20
  21. package/lib/plugins/image/index.js.map +1 -1
  22. package/lib/plugins/image/insert-image-handler.js +9 -15
  23. package/lib/plugins/image/insert-image-handler.js.map +1 -1
  24. package/lib/plugins/index.js +20 -12
  25. package/lib/plugins/index.js.map +1 -1
  26. package/lib/plugins/list/index.js +82 -14
  27. package/lib/plugins/list/index.js.map +1 -1
  28. package/lib/plugins/math/index.js +50 -55
  29. package/lib/plugins/math/index.js.map +1 -1
  30. package/lib/plugins/media/index.js +26 -25
  31. package/lib/plugins/media/index.js.map +1 -1
  32. package/lib/plugins/media/media-dialog.js +45 -56
  33. package/lib/plugins/media/media-dialog.js.map +1 -1
  34. package/lib/plugins/media/media-toolbar.js +24 -30
  35. package/lib/plugins/media/media-toolbar.js.map +1 -1
  36. package/lib/plugins/media/media-wrapper.js +28 -35
  37. package/lib/plugins/media/media-wrapper.js.map +1 -1
  38. package/lib/plugins/respArea/drag-in-the-blank/choice.js +68 -46
  39. package/lib/plugins/respArea/drag-in-the-blank/choice.js.map +1 -1
  40. package/lib/plugins/respArea/drag-in-the-blank/index.js +12 -12
  41. package/lib/plugins/respArea/drag-in-the-blank/index.js.map +1 -1
  42. package/lib/plugins/respArea/explicit-constructed-response/index.js +10 -9
  43. package/lib/plugins/respArea/explicit-constructed-response/index.js.map +1 -1
  44. package/lib/plugins/respArea/icons/index.js +11 -11
  45. package/lib/plugins/respArea/icons/index.js.map +1 -1
  46. package/lib/plugins/respArea/index.js +58 -42
  47. package/lib/plugins/respArea/index.js.map +1 -1
  48. package/lib/plugins/respArea/inline-dropdown/index.js +8 -8
  49. package/lib/plugins/respArea/inline-dropdown/index.js.map +1 -1
  50. package/lib/plugins/respArea/utils.js +5 -5
  51. package/lib/plugins/respArea/utils.js.map +1 -1
  52. package/lib/plugins/table/icons/index.js +12 -12
  53. package/lib/plugins/table/icons/index.js.map +1 -1
  54. package/lib/plugins/table/index.js +83 -27
  55. package/lib/plugins/table/index.js.map +1 -1
  56. package/lib/plugins/table/table-toolbar.js +41 -50
  57. package/lib/plugins/table/table-toolbar.js.map +1 -1
  58. package/lib/plugins/toolbar/default-toolbar.js +19 -13
  59. package/lib/plugins/toolbar/default-toolbar.js.map +1 -1
  60. package/lib/plugins/toolbar/done-button.js +5 -5
  61. package/lib/plugins/toolbar/done-button.js.map +1 -1
  62. package/lib/plugins/toolbar/editor-and-toolbar.js +55 -45
  63. package/lib/plugins/toolbar/editor-and-toolbar.js.map +1 -1
  64. package/lib/plugins/toolbar/index.js +5 -5
  65. package/lib/plugins/toolbar/index.js.map +1 -1
  66. package/lib/plugins/toolbar/toolbar-buttons.js +49 -52
  67. package/lib/plugins/toolbar/toolbar-buttons.js.map +1 -1
  68. package/lib/plugins/toolbar/toolbar.js +69 -63
  69. package/lib/plugins/toolbar/toolbar.js.map +1 -1
  70. package/lib/plugins/utils.js +1 -1
  71. package/lib/plugins/utils.js.map +1 -1
  72. package/lib/serialization.js +32 -9
  73. package/lib/serialization.js.map +1 -1
  74. package/lib/theme.js.map +1 -1
  75. package/package.json +7 -6
  76. package/src/editor.jsx +225 -26
  77. package/src/index.jsx +22 -5
  78. package/src/plugins/characters/custom-popper.js +48 -0
  79. package/src/plugins/characters/index.jsx +268 -0
  80. package/src/plugins/characters/utils.js +447 -0
  81. package/src/plugins/image/alt-dialog.jsx +69 -0
  82. package/src/plugins/image/component.jsx +204 -21
  83. package/src/plugins/image/image-toolbar.jsx +68 -22
  84. package/src/plugins/image/index.jsx +47 -9
  85. package/src/plugins/index.jsx +4 -1
  86. package/src/plugins/list/index.jsx +67 -5
  87. package/src/plugins/math/index.jsx +31 -37
  88. package/src/plugins/media/index.jsx +3 -0
  89. package/src/plugins/media/media-dialog.js +1 -1
  90. package/src/plugins/respArea/drag-in-the-blank/choice.jsx +28 -1
  91. package/src/plugins/respArea/explicit-constructed-response/index.jsx +3 -3
  92. package/src/plugins/respArea/index.jsx +50 -31
  93. package/src/plugins/table/index.jsx +63 -14
  94. package/src/plugins/toolbar/default-toolbar.jsx +20 -2
  95. package/src/plugins/toolbar/editor-and-toolbar.jsx +39 -5
  96. package/src/plugins/toolbar/toolbar-buttons.jsx +13 -2
  97. package/src/plugins/toolbar/toolbar.jsx +22 -4
  98. 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.length).moveAnchorTo(
98
+ c.moveFocusTo(lastText.key, lastText.text?.length).moveAnchorTo(
88
99
  lastText.key,
89
- lastText.text.length
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
+ }