@pie-lib/editable-html 10.0.0-beta.7 → 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 +139 -434
  82. package/src/index.jsx +96 -62
  83. package/src/plugins/characters/index.jsx +17 -12
  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 +38 -60
  87. package/src/plugins/image/index.jsx +42 -95
  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 +90 -62
  91. package/src/plugins/math/index.jsx +70 -93
  92. package/src/plugins/media/index.jsx +117 -146
  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 +4 -5
  96. package/src/plugins/respArea/explicit-constructed-response/index.jsx +1 -2
  97. package/src/plugins/respArea/index.jsx +84 -114
  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 +214 -334
  101. package/src/plugins/table/table-toolbar.jsx +4 -3
  102. package/src/plugins/toolbar/default-toolbar.jsx +30 -48
  103. package/src/plugins/toolbar/editor-and-toolbar.jsx +114 -114
  104. package/src/plugins/toolbar/toolbar.jsx +224 -254
  105. package/src/plugins/utils.js +0 -16
  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]');
@@ -108,7 +106,6 @@ const insertDialog = ({ editor, callback, opts }) => {
108
106
  // so right after mouseup, the click will be triggered
109
107
  if (firstCallMade) {
110
108
  const focusIsInModals = newEl.contains(e.target) || (popoverEl && popoverEl.contains(e.target));
111
- const editorDOM = ReactEditor.toDOMNode(editor, editor);
112
109
  const focusIsInEditor = editorDOM.contains(e.target);
113
110
 
114
111
  if (!(focusIsInModals || focusIsInEditor)) {
@@ -173,8 +170,7 @@ const insertDialog = ({ editor, callback, opts }) => {
173
170
  );
174
171
 
175
172
  ReactDOM.render(el, newEl, () => {
176
- const [nodeAtSelection] = Editor.node(editor, editor.selection);
177
- const cursorItem = ReactEditor.toDOMNode(editor, nodeAtSelection);
173
+ const cursorItem = document.querySelector(`[data-key="${value.anchorKey}"]`);
178
174
 
179
175
  if (cursorItem) {
180
176
  const bodyRect = document.body.getBoundingClientRect();
@@ -241,21 +237,30 @@ export default function CharactersPlugin(opts) {
241
237
  name: 'characters',
242
238
  toolbar: {
243
239
  icon: <CharacterIcon letter={opts.characterIcon || characterIcons[opts.language] || 'ñ'} />,
244
- onClick: (editor) => {
240
+ onClick: (value, onChange, getFocusedValue) => {
241
+ const editorDOM = document.querySelector(`[data-key="${value.document.key}"]`);
242
+ let valueToUse = value;
245
243
  const callback = (char, focus) => {
244
+ valueToUse = getFocusedValue();
245
+
246
246
  if (char) {
247
- log('[characters:insert]: ', char);
248
- 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);
249
252
  }
250
253
 
251
254
  log('[characters:click]');
252
255
 
253
256
  if (focus) {
254
- ReactEditor.focus(editor);
257
+ if (editorDOM) {
258
+ editorDOM.focus();
259
+ }
255
260
  }
256
261
  };
257
262
 
258
- insertDialog({ editor, callback, opts });
263
+ insertDialog({ editorDOM, value: valueToUse, callback, opts });
259
264
  },
260
265
  },
261
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,22 +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
- });
237
+ const className = classNames(
238
+ classes.root,
239
+ !loaded && classes.loading,
240
+ deleteStatus === 'pending' && classes.pendingDelete,
241
+ );
263
242
 
264
- const progressClasses = classNames(classes.progress, {
265
- [classes.hideProgress]: isLoaded,
266
- });
243
+ const progressClasses = classNames(classes.progress, loaded && classes.hideProgress);
267
244
 
268
- return (
269
- <div onFocus={onFocus} className={className} style={{ justifyContent }} {...attributes}>
270
- {children}
245
+ return [
246
+ <span key={'sp1'}>&nbsp;</span>,
247
+ <div key={'comp'} onFocus={onFocus} className={className} style={{ justifyContent }}>
271
248
  <LinearProgress mode="determinate" value={percent > 0 ? percent : 0} className={progressClasses} />
272
249
  <div className={classes.imageContainer}>
273
250
  <img
274
- className={classNames(classes.image, { [classes.active]: active })}
251
+ {...attributes}
252
+ className={classNames(classes.image, active && classes.active)}
275
253
  ref={(ref) => {
276
254
  this.img = ref;
277
255
  }}
@@ -287,8 +265,9 @@ export class Component extends React.Component {
287
265
  className={classNames(classes.resize, 'resize')}
288
266
  />
289
267
  </div>
290
- </div>
291
- );
268
+ </div>,
269
+ <span key={'sp2'}>&nbsp;</span>,
270
+ ];
292
271
  }
293
272
  }
294
273
 
@@ -328,7 +307,6 @@ const styles = (theme) => ({
328
307
  border: `solid 1px ${theme.palette.common.white}`,
329
308
  display: 'flex',
330
309
  transition: 'opacity 200ms linear',
331
- width: '100%',
332
310
  },
333
311
  delete: {
334
312
  position: 'absolute',