@pie-lib/editable-html 10.0.0-beta.6 → 10.0.0

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 (118) hide show
  1. package/CHANGELOG.json +1 -1
  2. package/CHANGELOG.md +81 -0
  3. package/LICENSE.md +5 -0
  4. package/lib/editor.js +410 -543
  5. package/lib/editor.js.map +1 -1
  6. package/lib/index.js +200 -101
  7. package/lib/index.js.map +1 -1
  8. package/lib/parse-html.js +5 -6
  9. package/lib/parse-html.js.map +1 -1
  10. package/lib/plugins/characters/custom-popper.js +12 -2
  11. package/lib/plugins/characters/custom-popper.js.map +1 -1
  12. package/lib/plugins/characters/index.js +71 -19
  13. package/lib/plugins/characters/index.js.map +1 -1
  14. package/lib/plugins/characters/utils.js.map +1 -1
  15. package/lib/plugins/html/icons/index.js +38 -0
  16. package/lib/plugins/html/icons/index.js.map +1 -0
  17. package/lib/plugins/html/index.js +75 -0
  18. package/lib/plugins/html/index.js.map +1 -0
  19. package/lib/plugins/image/alt-dialog.js +26 -0
  20. package/lib/plugins/image/alt-dialog.js.map +1 -1
  21. package/lib/plugins/image/component.js +124 -90
  22. package/lib/plugins/image/component.js.map +1 -1
  23. package/lib/plugins/image/image-toolbar.js +45 -7
  24. package/lib/plugins/image/image-toolbar.js.map +1 -1
  25. package/lib/plugins/image/index.js +91 -113
  26. package/lib/plugins/image/index.js.map +1 -1
  27. package/lib/plugins/image/insert-image-handler.js +54 -72
  28. package/lib/plugins/image/insert-image-handler.js.map +1 -1
  29. package/lib/plugins/index.js +71 -31
  30. package/lib/plugins/index.js.map +1 -1
  31. package/lib/plugins/list/index.js +129 -58
  32. package/lib/plugins/list/index.js.map +1 -1
  33. package/lib/plugins/math/index.js +152 -118
  34. package/lib/plugins/math/index.js.map +1 -1
  35. package/lib/plugins/media/index.js +185 -168
  36. package/lib/plugins/media/index.js.map +1 -1
  37. package/lib/plugins/media/media-dialog.js +197 -110
  38. package/lib/plugins/media/media-dialog.js.map +1 -1
  39. package/lib/plugins/media/media-toolbar.js +24 -4
  40. package/lib/plugins/media/media-toolbar.js.map +1 -1
  41. package/lib/plugins/media/media-wrapper.js +65 -23
  42. package/lib/plugins/media/media-wrapper.js.map +1 -1
  43. package/lib/plugins/respArea/drag-in-the-blank/choice.js +50 -10
  44. package/lib/plugins/respArea/drag-in-the-blank/choice.js.map +1 -1
  45. package/lib/plugins/respArea/drag-in-the-blank/index.js +22 -9
  46. package/lib/plugins/respArea/drag-in-the-blank/index.js.map +1 -1
  47. package/lib/plugins/respArea/explicit-constructed-response/index.js +9 -4
  48. package/lib/plugins/respArea/explicit-constructed-response/index.js.map +1 -1
  49. package/lib/plugins/respArea/icons/index.js +18 -1
  50. package/lib/plugins/respArea/icons/index.js.map +1 -1
  51. package/lib/plugins/respArea/index.js +133 -122
  52. package/lib/plugins/respArea/index.js.map +1 -1
  53. package/lib/plugins/respArea/inline-dropdown/index.js +10 -4
  54. package/lib/plugins/respArea/inline-dropdown/index.js.map +1 -1
  55. package/lib/plugins/respArea/utils.js +33 -15
  56. package/lib/plugins/respArea/utils.js.map +1 -1
  57. package/lib/plugins/table/icons/index.js +7 -0
  58. package/lib/plugins/table/icons/index.js.map +1 -1
  59. package/lib/plugins/table/index.js +279 -390
  60. package/lib/plugins/table/index.js.map +1 -1
  61. package/lib/plugins/table/table-toolbar.js +47 -14
  62. package/lib/plugins/table/table-toolbar.js.map +1 -1
  63. package/lib/plugins/toolbar/default-toolbar.js +63 -51
  64. package/lib/plugins/toolbar/default-toolbar.js.map +1 -1
  65. package/lib/plugins/toolbar/done-button.js +9 -1
  66. package/lib/plugins/toolbar/done-button.js.map +1 -1
  67. package/lib/plugins/toolbar/editor-and-toolbar.js +140 -83
  68. package/lib/plugins/toolbar/editor-and-toolbar.js.map +1 -1
  69. package/lib/plugins/toolbar/index.js +5 -0
  70. package/lib/plugins/toolbar/index.js.map +1 -1
  71. package/lib/plugins/toolbar/toolbar-buttons.js +39 -8
  72. package/lib/plugins/toolbar/toolbar-buttons.js.map +1 -1
  73. package/lib/plugins/toolbar/toolbar.js +261 -225
  74. package/lib/plugins/toolbar/toolbar.js.map +1 -1
  75. package/lib/plugins/utils.js +16 -19
  76. package/lib/plugins/utils.js.map +1 -1
  77. package/lib/serialization.js +70 -11
  78. package/lib/serialization.js.map +1 -1
  79. package/lib/theme.js.map +1 -1
  80. package/package.json +18 -17
  81. package/src/editor.jsx +140 -450
  82. package/src/index.jsx +96 -62
  83. package/src/plugins/characters/index.jsx +18 -14
  84. package/src/plugins/html/icons/index.jsx +19 -0
  85. package/src/plugins/html/index.jsx +68 -0
  86. package/src/plugins/image/component.jsx +41 -67
  87. package/src/plugins/image/index.jsx +43 -108
  88. package/src/plugins/image/insert-image-handler.js +27 -62
  89. package/src/plugins/index.jsx +39 -21
  90. package/src/plugins/list/index.jsx +91 -66
  91. package/src/plugins/math/index.jsx +71 -84
  92. package/src/plugins/media/index.jsx +118 -147
  93. package/src/plugins/media/media-dialog.js +9 -10
  94. package/src/plugins/media/media-wrapper.jsx +27 -29
  95. package/src/plugins/respArea/drag-in-the-blank/index.jsx +7 -10
  96. package/src/plugins/respArea/explicit-constructed-response/index.jsx +2 -3
  97. package/src/plugins/respArea/index.jsx +90 -138
  98. package/src/plugins/respArea/inline-dropdown/index.jsx +2 -3
  99. package/src/plugins/respArea/utils.jsx +28 -23
  100. package/src/plugins/table/index.jsx +216 -340
  101. package/src/plugins/table/table-toolbar.jsx +5 -9
  102. package/src/plugins/toolbar/default-toolbar.jsx +31 -51
  103. package/src/plugins/toolbar/editor-and-toolbar.jsx +114 -121
  104. package/src/plugins/toolbar/toolbar.jsx +224 -258
  105. package/src/plugins/utils.js +2 -19
  106. package/src/serialization.jsx +1 -1
  107. package/lib/components.js +0 -92
  108. package/lib/components.js.map +0 -1
  109. package/lib/new-serialization.js +0 -280
  110. package/lib/new-serialization.js.map +0 -1
  111. package/lib/plugins/hotKeys/index.js +0 -60
  112. package/lib/plugins/hotKeys/index.js.map +0 -1
  113. package/lib/test-serializer.js +0 -138
  114. package/lib/test-serializer.js.map +0 -1
  115. package/src/components.js +0 -135
  116. package/src/new-serialization.jsx +0 -310
  117. package/src/plugins/hotKeys/index.js +0 -54
  118. package/src/test-serializer.js +0 -132
package/src/index.jsx CHANGED
@@ -1,8 +1,7 @@
1
- import React, { useState, useRef, useEffect } from 'react';
2
- import { useSlate } from 'slate-react';
1
+ import React from 'react';
3
2
  import PropTypes from 'prop-types';
4
3
  import Editor, { DEFAULT_PLUGINS, ALL_PLUGINS } from './editor';
5
- import { htmlToValue, valueToHtml } from './new-serialization';
4
+ import { htmlToValue, valueToHtml } from './serialization';
6
5
  import { parseDegrees } from './parse-html';
7
6
  import debug from 'debug';
8
7
  import { Range } from 'slate';
@@ -13,43 +12,87 @@ const log = debug('@pie-lib:editable-html');
13
12
  */
14
13
  export { htmlToValue, valueToHtml, Editor, DEFAULT_PLUGINS, ALL_PLUGINS };
15
14
 
16
- const useConstructor = (callback = () => {}) => {
17
- const [hasBeenCalled, setHasBeenCalled] = useState(false);
15
+ /**
16
+ * Wrapper around the editor that exposes a `markup` and `onChange(markup:string)` api.
17
+ * Because of the mismatch between the markup and the `Value` we need to convert the incoming markup to a value and
18
+ * compare it. TODO: This is an interim fix, we'll need to strip back `Editor` and look how best to maintain the
19
+ * `markup` api whilst avoiding the serialization mismatch. We should be making better use of schemas w/ normalize.
20
+ */
18
21
 
19
- if (hasBeenCalled) {
20
- return;
22
+ const reduceMultipleBrs = (markup) => {
23
+ try {
24
+ return markup.replace(/(<br\s*\/?>){3,}/gi, '<br>');
25
+ } catch (e) {
26
+ // eslint-disable-next-line no-console
27
+ console.log("Couldn't remove <br/> tags: ", e);
21
28
  }
22
29
 
23
- callback();
24
- setHasBeenCalled(true);
30
+ return markup;
25
31
  };
26
32
 
27
- const EditableHtml = React.forwardRef((props, forwardedRef) => {
28
- const editorRef = useRef(null);
29
- const rootRef = useRef(null);
30
- const [value, setValue] = useState();
33
+ export default class EditableHtml extends React.Component {
34
+ static propTypes = {
35
+ error: PropTypes.any,
36
+ onChange: PropTypes.func.isRequired,
37
+ onDone: PropTypes.func,
38
+ markup: PropTypes.string.isRequired,
39
+ allowValidation: PropTypes.bool,
40
+ toolbarOpts: PropTypes.object,
41
+ };
42
+
43
+ static defaultProps = {
44
+ onDone: () => {},
45
+ allowValidation: false,
46
+ };
31
47
 
32
- useConstructor(() => {
48
+ constructor(props) {
49
+ super(props);
33
50
  const v = htmlToValue(props.markup);
34
- setValue(v);
35
- });
51
+ this.state = {
52
+ value: v,
53
+ };
54
+ }
55
+
56
+ // eslint-disable-next-line react/no-deprecated
57
+ componentWillReceiveProps(props) {
58
+ if (!props.allowValidation && props.markup === this.props.markup) {
59
+ return;
60
+ }
61
+
62
+ const v = htmlToValue(reduceMultipleBrs(props.markup));
63
+ const current = htmlToValue(reduceMultipleBrs(this.props.markup));
64
+
65
+ if (v.equals && !v.equals(current)) {
66
+ this.setState({ value: v });
67
+ }
68
+ }
69
+
70
+ runSerializationOnMarkup = () => {
71
+ if (!this.props.markup) {
72
+ return;
73
+ }
74
+
75
+ const v = htmlToValue(reduceMultipleBrs(this.props.markup));
36
76
 
37
- const onChange = (value, done) => {
77
+ this.setState({ value: v });
78
+ };
79
+
80
+ onChange = (value, done) => {
38
81
  const html = valueToHtml(value);
39
82
  const htmlParsed = parseDegrees(html);
40
83
 
41
84
  log('value as html: ', html);
42
85
 
43
- if (html !== props.markup) {
44
- props.onChange(htmlParsed);
86
+ if (html !== this.props.markup) {
87
+ this.props.onChange(htmlParsed);
45
88
  }
46
89
 
47
90
  if (done) {
48
- props.onDone(htmlParsed);
91
+ this.props.onDone(htmlParsed);
49
92
  }
50
93
  };
51
94
 
52
- const focus = (position, node, select = false) => {
95
+ focus = (position, node, select = false) => {
53
96
  if (this.editorRef) {
54
97
  this.editorRef.change((c) => {
55
98
  const lastText = node ? c.value.document.getNextText(node.key) : c.value.document.getLastText();
@@ -84,48 +127,39 @@ const EditableHtml = React.forwardRef((props, forwardedRef) => {
84
127
  }
85
128
  };
86
129
 
87
- const { toolbarOpts, error } = props;
88
-
89
- if (toolbarOpts) {
90
- toolbarOpts.error = error;
91
- }
92
-
93
- const newProps = {
94
- ...props,
95
- markup: null,
96
- value,
97
- onChange,
98
- focus,
130
+ finishEditing = () => {
131
+ if (this.editorRef) {
132
+ this.editorRef.props.onEditingDone();
133
+ }
99
134
  };
100
135
 
101
- return (
102
- <Editor
103
- {...newProps}
104
- onRef={ref => {
105
- if (ref) {
106
- rootRef.current = ref;
136
+ render() {
137
+ const { value } = this.state;
138
+ const { toolbarOpts, error } = this.props;
107
139
 
108
- if (forwardedRef) {
109
- forwardedRef(ref);
110
- }
111
- }
112
- }}
113
- editorRef={ref => ref && (editorRef.current = ref)}
114
- />
115
- );
116
- });
117
-
118
- EditableHtml.propTypes = {
119
- onChange: PropTypes.func.isRequired,
120
- onDone: PropTypes.func,
121
- onEditor: PropTypes.func,
122
- markup: PropTypes.string.isRequired,
123
- allowValidation: PropTypes.bool
124
- };
125
-
126
- EditableHtml.defaultProps = {
127
- onDone: () => {},
128
- allowValidation: false
129
- };
140
+ if (toolbarOpts) {
141
+ toolbarOpts.error = error;
142
+ }
130
143
 
131
- export default EditableHtml;
144
+ const props = {
145
+ ...this.props,
146
+ markup: null,
147
+ value,
148
+ onChange: this.onChange,
149
+ focus: this.focus,
150
+ runSerializationOnMarkup: this.runSerializationOnMarkup,
151
+ };
152
+
153
+ return (
154
+ <Editor
155
+ onRef={(ref) => {
156
+ if (ref) {
157
+ this.rootRef = ref;
158
+ }
159
+ }}
160
+ editorRef={(ref) => ref && (this.editorRef = ref)}
161
+ {...props}
162
+ />
163
+ );
164
+ }
165
+ }
@@ -1,8 +1,5 @@
1
1
  import React from 'react';
2
- import { ReactEditor } from 'slate-react';
3
- import { Editor } from 'slate';
4
2
  import ReactDOM from 'react-dom';
5
- import PropTypes from 'prop-types';
6
3
  import debug from 'debug';
7
4
  import get from 'lodash/get';
8
5
 
@@ -11,6 +8,7 @@ import { PureToolbar } from '@pie-lib/math-toolbar';
11
8
  import CustomPopper from './custom-popper';
12
9
  import { insertSnackBar } from '../respArea/utils';
13
10
  import { characterIcons, spanishConfig, specialConfig } from './utils';
11
+ import PropTypes from 'prop-types';
14
12
  const log = debug('@pie-lib:editable-html:plugins:characters');
15
13
 
16
14
  const removePopOvers = () => {
@@ -28,7 +26,7 @@ export const removeDialogs = () => {
28
26
  removePopOvers();
29
27
  };
30
28
 
31
- const insertDialog = ({ editor, callback, opts }) => {
29
+ const insertDialog = ({ editorDOM, value, callback, opts }) => {
32
30
  const newEl = document.createElement('div');
33
31
 
34
32
  log('[characters:insertDialog]');
@@ -107,9 +105,7 @@ const insertDialog = ({ editor, callback, opts }) => {
107
105
  // this toolbar is added on the mousedown event
108
106
  // so right after mouseup, the click will be triggered
109
107
  if (firstCallMade) {
110
- const focusIsInModals =
111
- newEl.contains(e.target) || (popoverEl && popoverEl.contains(e.target));
112
- const editorDOM = ReactEditor.toDOMNode(editor, editor);
108
+ const focusIsInModals = newEl.contains(e.target) || (popoverEl && popoverEl.contains(e.target));
113
109
  const focusIsInEditor = editorDOM.contains(e.target);
114
110
 
115
111
  if (!(focusIsInModals || focusIsInEditor)) {
@@ -174,8 +170,7 @@ const insertDialog = ({ editor, callback, opts }) => {
174
170
  );
175
171
 
176
172
  ReactDOM.render(el, newEl, () => {
177
- const [nodeAtSelection] = Editor.node(editor, editor.selection);
178
- const cursorItem = ReactEditor.toDOMNode(editor, nodeAtSelection);
173
+ const cursorItem = document.querySelector(`[data-key="${value.anchorKey}"]`);
179
174
 
180
175
  if (cursorItem) {
181
176
  const bodyRect = document.body.getBoundingClientRect();
@@ -242,21 +237,30 @@ export default function CharactersPlugin(opts) {
242
237
  name: 'characters',
243
238
  toolbar: {
244
239
  icon: <CharacterIcon letter={opts.characterIcon || characterIcons[opts.language] || 'ñ'} />,
245
- onClick: editor => {
240
+ onClick: (value, onChange, getFocusedValue) => {
241
+ const editorDOM = document.querySelector(`[data-key="${value.document.key}"]`);
242
+ let valueToUse = value;
246
243
  const callback = (char, focus) => {
244
+ valueToUse = getFocusedValue();
245
+
247
246
  if (char) {
248
- log('[characters:insert]: ', char);
249
- editor.insertText(char);
247
+ const change = valueToUse.change().insertTextByKey(valueToUse.anchorKey, valueToUse.anchorOffset, char);
248
+
249
+ valueToUse = change.value;
250
+ log('[characters:insert]: ', value);
251
+ onChange(change);
250
252
  }
251
253
 
252
254
  log('[characters:click]');
253
255
 
254
256
  if (focus) {
255
- ReactEditor.focus(editor);
257
+ if (editorDOM) {
258
+ editorDOM.focus();
259
+ }
256
260
  }
257
261
  };
258
262
 
259
- insertDialog({ editor, callback, opts });
263
+ insertDialog({ editorDOM, value: valueToUse, callback, opts });
260
264
  },
261
265
  },
262
266
 
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import { withStyles } from '@material-ui/core/styles';
3
+
4
+ const styles = (theme) => ({
5
+ icon: {
6
+ fontFamily: 'Cerebri Sans, Arial, sans-serif',
7
+ fontSize: theme.typography.fontSize,
8
+ fontWeight: 'bold',
9
+ lineHeight: '14px',
10
+ position: 'relative',
11
+ whiteSpace: 'nowrap',
12
+ },
13
+ });
14
+
15
+ const HtmlModeIcon = ({ classes, isHtmlMode }) => (
16
+ <div className={classes.icon}>{isHtmlMode ? 'Exit <HTML> mode' : '<HTML>'}</div>
17
+ );
18
+
19
+ export default withStyles(styles)(HtmlModeIcon);
@@ -0,0 +1,68 @@
1
+ import React from 'react';
2
+ import HtmlModeIcon from './icons';
3
+ import { htmlToValue, valueToHtml } from './../../serialization';
4
+
5
+ const toggleToRichText = (value, onChange) => {
6
+ const plainText = value.document.text;
7
+ const slateValue = htmlToValue(plainText);
8
+
9
+ const change = value
10
+ .change()
11
+ .selectAll()
12
+ .delete()
13
+ .insertFragment(slateValue.document);
14
+ onChange(change);
15
+ };
16
+
17
+ export default function HtmlPlugin(opts) {
18
+ const { isHtmlMode, isEdited, toggleHtmlMode, handleAlertDialog } = opts;
19
+
20
+ const handleHtmlModeOn = (value, onChange) => {
21
+ const dialogProps = {
22
+ title: 'Warning',
23
+ text: 'Returning to rich text mode may cause edits to be lost.',
24
+ onConfirm: () => {
25
+ toggleToRichText(value, onChange);
26
+ handleAlertDialog(false);
27
+ },
28
+ onClose: () => {
29
+ handleAlertDialog(false);
30
+ },
31
+ };
32
+
33
+ handleAlertDialog(true, dialogProps);
34
+ };
35
+
36
+ const handleHtmlModeOff = (value, onChange) => {
37
+ const change = value
38
+ .change()
39
+ .selectAll()
40
+ .delete()
41
+ .insertText(valueToHtml(value));
42
+ onChange(change);
43
+ };
44
+
45
+ return {
46
+ name: 'html',
47
+ toolbar: {
48
+ icon: <HtmlModeIcon isHtmlMode={isHtmlMode} />,
49
+ buttonStyles: {
50
+ margin: '0 20px 0 auto',
51
+ },
52
+ type: 'html',
53
+ onClick: (value, onChange) => {
54
+ if (isHtmlMode) {
55
+ if (isEdited) {
56
+ handleHtmlModeOn(value, onChange);
57
+ } else {
58
+ toggleToRichText(value, onChange);
59
+ }
60
+ } else {
61
+ handleHtmlModeOff(value, onChange);
62
+ }
63
+
64
+ toggleHtmlMode();
65
+ },
66
+ },
67
+ };
68
+ }
@@ -1,12 +1,10 @@
1
- import React from 'react';
1
+ import LinearProgress from '@material-ui/core/LinearProgress';
2
2
  import PropTypes from 'prop-types';
3
+ import React from 'react';
3
4
  import classNames from 'classnames';
4
5
  import debug from 'debug';
5
- import isEqual from 'lodash/isEqual';
6
- import cloneDeep from 'lodash/cloneDeep';
7
- import LinearProgress from '@material-ui/core/LinearProgress';
8
6
  import { withStyles } from '@material-ui/core/styles';
9
- import { Editor } from 'slate';
7
+ import SlatePropTypes from 'slate-prop-types';
10
8
 
11
9
  const log = debug('@pie-lib:editable-html:plugins:image:component');
12
10
 
@@ -14,12 +12,7 @@ const size = (s) => (s ? `${s}px` : 'auto');
14
12
 
15
13
  export class Component extends React.Component {
16
14
  static propTypes = {
17
- node: PropTypes.shape({
18
- type: PropTypes.string,
19
- children: PropTypes.array,
20
- data: PropTypes.object
21
- }).isRequired,
22
- focused: PropTypes.bool,
15
+ node: SlatePropTypes.node.isRequired,
23
16
  editor: PropTypes.shape({
24
17
  change: PropTypes.func.isRequired,
25
18
  value: PropTypes.object,
@@ -50,27 +43,17 @@ export class Component extends React.Component {
50
43
  applySizeData = () => {
51
44
  const { node, editor } = this.props;
52
45
 
53
- let update = cloneDeep(node.data);
54
-
55
- const w = update.width;
46
+ let update = node.data;
56
47
 
48
+ const w = update.get('width');
57
49
  if (w) {
58
- update = update.resizePercent = this.getPercentFromWidth(w);
50
+ update = update.set('resizePercent', this.getPercentFromWidth(w));
59
51
  }
60
52
 
61
53
  log('[applySizeData] update: ', update);
62
54
 
63
- if (editor.selection && !isEqual(update, node.data)) {
64
- const nodePath = Editor.path(editor, editor.selection);
65
-
66
- editor.apply({
67
- type: 'set_node',
68
- path: nodePath,
69
- properties: {
70
- data: node.data
71
- },
72
- newProperties: { data: update }
73
- });
55
+ if (!update.equals(node.data)) {
56
+ editor.change((c) => c.setNodeByKey(node.key, { data: update }));
74
57
  }
75
58
  };
76
59
 
@@ -95,8 +78,8 @@ export class Component extends React.Component {
95
78
 
96
79
  getSize(data) {
97
80
  return {
98
- width: size(data.width),
99
- height: size(data.height),
81
+ width: size(data.get('width')),
82
+ height: size(data.get('height')),
100
83
  objectFit: 'contain',
101
84
  };
102
85
  }
@@ -133,22 +116,13 @@ export class Component extends React.Component {
133
116
 
134
117
  const { node, editor } = this.props;
135
118
 
136
- const update = cloneDeep(node.data);
137
-
138
- update.width = width;
139
- update.height = height;
119
+ let update = node.data;
140
120
 
141
- if (editor.selection && !isEqual(update, node.data)) {
142
- const nodePath = Editor.path(editor, editor.selection);
121
+ update = update.set('width', width);
122
+ update = update.set('height', height);
143
123
 
144
- editor.apply({
145
- type: 'set_node',
146
- path: nodePath,
147
- properties: {
148
- data: node.data,
149
- },
150
- newProperties: { data: update },
151
- });
124
+ if (!update.equals(node.data)) {
125
+ editor.change((c) => c.setNodeByKey(node.key, { data: update }));
152
126
  }
153
127
  }
154
128
  };
@@ -227,10 +201,14 @@ export class Component extends React.Component {
227
201
  };
228
202
 
229
203
  render() {
230
- const { node, focused, classes, attributes, children, onFocus } = this.props;
231
- const active = focused;
232
- const { alignment, alt, deleteStatus, loaded, percent, src } = node.data;
233
- const isLoaded = loaded !== false;
204
+ const { node, editor, classes, attributes, onFocus } = this.props;
205
+ const active = editor.value.isFocused && editor.value.selection.hasEdgeIn(node);
206
+ const src = node.data.get('src');
207
+ const loaded = node.data.get('loaded') !== false;
208
+ const deleteStatus = node.data.get('deleteStatus');
209
+ const alignment = node.data.get('alignment');
210
+ const percent = node.data.get('percent');
211
+ const alt = node.data.get('alt');
234
212
  let justifyContent;
235
213
 
236
214
  switch (alignment) {
@@ -256,26 +234,22 @@ export class Component extends React.Component {
256
234
 
257
235
  log('[render] style:', size);
258
236
 
259
- const className = classNames(classes.root, {
260
- [classes.loading]: !isLoaded,
261
- [classes.pendingDelete]: deleteStatus === 'pending',
262
- });
263
-
264
- const progressClasses = classNames(classes.progress, {
265
- [classes.hideProgress]: isLoaded
266
- });
267
-
268
- return (
269
- <div onFocus={onFocus} className={className} style={{ justifyContent }} {...attributes}>
270
- {children}
271
- <LinearProgress
272
- mode="determinate"
273
- value={percent > 0 ? percent : 0}
274
- className={progressClasses}
275
- />
237
+ const className = classNames(
238
+ classes.root,
239
+ !loaded && classes.loading,
240
+ deleteStatus === 'pending' && classes.pendingDelete,
241
+ );
242
+
243
+ const progressClasses = classNames(classes.progress, loaded && classes.hideProgress);
244
+
245
+ return [
246
+ <span key={'sp1'}>&nbsp;</span>,
247
+ <div key={'comp'} onFocus={onFocus} className={className} style={{ justifyContent }}>
248
+ <LinearProgress mode="determinate" value={percent > 0 ? percent : 0} className={progressClasses} />
276
249
  <div className={classes.imageContainer}>
277
250
  <img
278
- className={classNames(classes.image, { [classes.active]: active })}
251
+ {...attributes}
252
+ className={classNames(classes.image, active && classes.active)}
279
253
  ref={(ref) => {
280
254
  this.img = ref;
281
255
  }}
@@ -291,8 +265,9 @@ export class Component extends React.Component {
291
265
  className={classNames(classes.resize, 'resize')}
292
266
  />
293
267
  </div>
294
- </div>
295
- );
268
+ </div>,
269
+ <span key={'sp2'}>&nbsp;</span>,
270
+ ];
296
271
  }
297
272
  }
298
273
 
@@ -332,7 +307,6 @@ const styles = (theme) => ({
332
307
  border: `solid 1px ${theme.palette.common.white}`,
333
308
  display: 'flex',
334
309
  transition: 'opacity 200ms linear',
335
- width: '100%',
336
310
  },
337
311
  delete: {
338
312
  position: 'absolute',