@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.
Files changed (98) hide show
  1. package/CHANGELOG.json +150 -0
  2. package/CHANGELOG.md +421 -0
  3. package/lib/editor.js +385 -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 +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 +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 +218 -25
  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 +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.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.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": "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
- onDelete: PropTypes.func
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
- onKeyDown: () => {}
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
- this.props.imageSupport &&
114
- this.props.imageSupport.delete &&
146
+ props.imageSupport &&
147
+ props.imageSupport.delete &&
115
148
  ((src, done) => {
116
- this.props.imageSupport.delete(src, e => {
149
+ props.imageSupport.delete(src, e => {
117
150
  done(e, this.state.value);
118
151
  });
119
152
  }),
120
153
  insertImageRequested:
121
- this.props.imageSupport &&
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
- this.props.imageSupport.add(handler);
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 } = this.props;
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.text.length === 0) {
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: props.responseAreaProps && props.responseAreaProps.type,
168
- options: props.responseAreaProps && props.responseAreaProps.options,
169
- respAreaToolbar: props.responseAreaProps && props.responseAreaProps.respAreaToolbar,
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.text.length === 0) {
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({ focusedNode: node }, this.handleBlur.bind(this, resolve));
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 || document.activeElement.closest(`[class*="${this.toolbarRef.className}"]`);
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
- this.onBlur(e);
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
- this.setState({ value: change.value }, () => {
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
- className={classes.slateEditor}
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.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;