@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.
Files changed (98) hide show
  1. package/CHANGELOG.json +150 -0
  2. package/CHANGELOG.md +421 -0
  3. package/lib/editor.js +390 -172
  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 +71 -27
  31. package/lib/plugins/media/index.js.map +1 -1
  32. package/lib/plugins/media/media-dialog.js +248 -72
  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 +51 -44
  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 +64 -62
  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 +224 -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 +49 -6
  89. package/src/plugins/media/media-dialog.js +261 -89
  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 +35 -4
  96. package/src/plugins/toolbar/toolbar-buttons.jsx +13 -2
  97. package/src/plugins/toolbar/toolbar.jsx +18 -3
  98. package/src/serialization.jsx +19 -3
package/lib/theme.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/theme.js"],"names":["primary"],"mappings":";;;;;;AAAO,IAAMA,OAAO,GAAG,SAAhB","sourcesContent":["export const primary = '#304ffe';\n"],"file":"theme.js"}
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.53+3034d18f",
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.3.9",
15
- "@pie-lib/math-toolbar": "^1.8.0",
16
- "@pie-lib/render-ui": "^4.12.0",
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.2",
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": "3034d18f8b084e72b2791c5afe5bc0eb179a48e6",
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
- onDelete: PropTypes.func
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
- onKeyDown: () => {}
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
- this.props.imageSupport &&
114
- this.props.imageSupport.delete &&
150
+ props.imageSupport &&
151
+ props.imageSupport.delete &&
115
152
  ((src, done) => {
116
- this.props.imageSupport.delete(src, e => {
153
+ props.imageSupport.delete(src, e => {
117
154
  done(e, this.state.value);
118
155
  });
119
156
  }),
120
157
  insertImageRequested:
121
- this.props.imageSupport &&
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
- this.props.imageSupport.add(handler);
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 } = this.props;
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.text.length === 0) {
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: props.responseAreaProps && props.responseAreaProps.type,
168
- options: props.responseAreaProps && props.responseAreaProps.options,
169
- respAreaToolbar: props.responseAreaProps && props.responseAreaProps.respAreaToolbar,
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.text.length === 0) {
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({ focusedNode: node }, this.handleBlur.bind(this, resolve));
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 || document.activeElement.closest(`[class*="${this.toolbarRef.className}"]`);
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
- this.onBlur(e);
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
- this.setState({ value: change.value }, () => {
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
- className={classes.slateEditor}
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.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;