@pie-lib/editable-html 9.5.13 → 10.0.0-beta.1

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 (144) hide show
  1. package/CHANGELOG.md +0 -302
  2. package/lib/components.js +116 -0
  3. package/lib/components.js.map +1 -0
  4. package/lib/editor.js +418 -103
  5. package/lib/editor.js.map +1 -1
  6. package/lib/index.js +101 -155
  7. package/lib/index.js.map +1 -1
  8. package/lib/new-serialization.js +320 -0
  9. package/lib/new-serialization.js.map +1 -0
  10. package/lib/old-serialization.js +330 -0
  11. package/lib/parse-html.js +1 -1
  12. package/lib/parse-html.js.map +1 -1
  13. package/lib/plugins/characters/custom-popper.js +1 -1
  14. package/lib/plugins/characters/custom-popper.js.map +1 -1
  15. package/lib/plugins/characters/index.js +21 -19
  16. package/lib/plugins/characters/index.js.map +1 -1
  17. package/lib/plugins/characters/utils.js +1 -1
  18. package/lib/plugins/characters/utils.js.map +1 -1
  19. package/lib/plugins/hotKeys/index.js +67 -0
  20. package/lib/plugins/hotKeys/index.js.map +1 -0
  21. package/lib/plugins/image/alt-dialog.js +1 -6
  22. package/lib/plugins/image/alt-dialog.js.map +1 -1
  23. package/lib/plugins/image/component.js +70 -53
  24. package/lib/plugins/image/component.js.map +1 -1
  25. package/lib/plugins/image/image-toolbar.js +7 -9
  26. package/lib/plugins/image/image-toolbar.js.map +1 -1
  27. package/lib/plugins/image/index.js +83 -27
  28. package/lib/plugins/image/index.js.map +1 -1
  29. package/lib/plugins/image/insert-image-handler.js +72 -33
  30. package/lib/plugins/image/insert-image-handler.js.map +1 -1
  31. package/lib/plugins/index.js +23 -41
  32. package/lib/plugins/index.js.map +1 -1
  33. package/lib/plugins/list/index.js +64 -100
  34. package/lib/plugins/list/index.js.map +1 -1
  35. package/lib/plugins/math/index.js +86 -60
  36. package/lib/plugins/math/index.js.map +1 -1
  37. package/lib/plugins/media/index.js +202 -132
  38. package/lib/plugins/media/index.js.map +1 -1
  39. package/lib/plugins/media/media-dialog.js +17 -16
  40. package/lib/plugins/media/media-dialog.js.map +1 -1
  41. package/lib/plugins/media/media-toolbar.js +3 -3
  42. package/lib/plugins/media/media-toolbar.js.map +1 -1
  43. package/lib/plugins/media/media-wrapper.js +21 -58
  44. package/lib/plugins/media/media-wrapper.js.map +1 -1
  45. package/lib/plugins/respArea/drag-in-the-blank/choice.js +3 -3
  46. package/lib/plugins/respArea/drag-in-the-blank/choice.js.map +1 -1
  47. package/lib/plugins/respArea/drag-in-the-blank/index.js +3 -2
  48. package/lib/plugins/respArea/drag-in-the-blank/index.js.map +1 -1
  49. package/lib/plugins/respArea/explicit-constructed-response/index.js +3 -2
  50. package/lib/plugins/respArea/explicit-constructed-response/index.js.map +1 -1
  51. package/lib/plugins/respArea/icons/index.js +13 -15
  52. package/lib/plugins/respArea/icons/index.js.map +1 -1
  53. package/lib/plugins/respArea/index.js +87 -53
  54. package/lib/plugins/respArea/index.js.map +1 -1
  55. package/lib/plugins/respArea/inline-dropdown/index.js +4 -3
  56. package/lib/plugins/respArea/inline-dropdown/index.js.map +1 -1
  57. package/lib/plugins/respArea/utils.js +17 -20
  58. package/lib/plugins/respArea/utils.js.map +1 -1
  59. package/lib/plugins/table/icons/index.js +1 -1
  60. package/lib/plugins/table/icons/index.js.map +1 -1
  61. package/lib/plugins/table/index.js +381 -212
  62. package/lib/plugins/table/index.js.map +1 -1
  63. package/lib/plugins/table/table-toolbar.js +5 -6
  64. package/lib/plugins/table/table-toolbar.js.map +1 -1
  65. package/lib/plugins/toolbar/default-toolbar.js +55 -11
  66. package/lib/plugins/toolbar/default-toolbar.js.map +1 -1
  67. package/lib/plugins/toolbar/done-button.js +1 -1
  68. package/lib/plugins/toolbar/done-button.js.map +1 -1
  69. package/lib/plugins/toolbar/editor-and-toolbar.js +186 -232
  70. package/lib/plugins/toolbar/editor-and-toolbar.js.map +1 -1
  71. package/lib/plugins/toolbar/index.js +1 -2
  72. package/lib/plugins/toolbar/index.js.map +1 -1
  73. package/lib/plugins/toolbar/toolbar-buttons.js +1 -1
  74. package/lib/plugins/toolbar/toolbar-buttons.js.map +1 -1
  75. package/lib/plugins/toolbar/toolbar.js +253 -239
  76. package/lib/plugins/toolbar/toolbar.js.map +1 -1
  77. package/lib/plugins/utils.js +27 -2
  78. package/lib/plugins/utils.js.map +1 -1
  79. package/lib/serialization.js +1 -1
  80. package/lib/serialization.js.map +1 -1
  81. package/lib/slate-editor.js +302 -0
  82. package/lib/test-serializer.js +189 -0
  83. package/lib/test-serializer.js.map +1 -0
  84. package/lib/theme.js +1 -1
  85. package/lib/theme.js.map +1 -1
  86. package/package.json +18 -14
  87. package/playground/image/data.js +20 -20
  88. package/playground/image/index.html +22 -20
  89. package/playground/image/index.jsx +12 -10
  90. package/playground/index.html +25 -23
  91. package/playground/mathquill/index.html +23 -20
  92. package/playground/mathquill/index.jsx +18 -22
  93. package/playground/prod-test/index.html +24 -20
  94. package/playground/prod-test/index.jsx +5 -3
  95. package/playground/schema-override/data.js +10 -10
  96. package/playground/schema-override/image-plugin.jsx +3 -4
  97. package/playground/schema-override/index.html +21 -19
  98. package/playground/schema-override/index.jsx +13 -14
  99. package/playground/serialization/data.js +10 -10
  100. package/playground/serialization/image-plugin.jsx +3 -4
  101. package/playground/serialization/index.html +22 -20
  102. package/playground/table-examples.html +5 -8
  103. package/playground/webpack.config.js +10 -10
  104. package/src/components.js +135 -0
  105. package/src/editor.jsx +478 -141
  106. package/src/index.jsx +71 -95
  107. package/src/new-serialization.jsx +291 -0
  108. package/src/parse-html.js +1 -1
  109. package/src/plugins/characters/custom-popper.js +7 -7
  110. package/src/plugins/characters/index.jsx +33 -34
  111. package/src/plugins/characters/utils.js +81 -81
  112. package/src/plugins/hotKeys/index.js +54 -0
  113. package/src/plugins/image/alt-dialog.jsx +4 -5
  114. package/src/plugins/image/component.jsx +106 -89
  115. package/src/plugins/image/image-toolbar.jsx +27 -19
  116. package/src/plugins/image/index.jsx +75 -43
  117. package/src/plugins/image/insert-image-handler.js +62 -27
  118. package/src/plugins/index.jsx +23 -41
  119. package/src/plugins/list/index.jsx +70 -95
  120. package/src/plugins/math/index.jsx +102 -82
  121. package/src/plugins/media/index.jsx +159 -124
  122. package/src/plugins/media/media-dialog.js +98 -71
  123. package/src/plugins/media/media-toolbar.jsx +8 -8
  124. package/src/plugins/media/media-wrapper.jsx +29 -30
  125. package/src/plugins/respArea/drag-in-the-blank/choice.jsx +21 -19
  126. package/src/plugins/respArea/drag-in-the-blank/index.jsx +14 -11
  127. package/src/plugins/respArea/explicit-constructed-response/index.jsx +7 -6
  128. package/src/plugins/respArea/icons/index.jsx +11 -14
  129. package/src/plugins/respArea/index.jsx +92 -52
  130. package/src/plugins/respArea/inline-dropdown/index.jsx +9 -8
  131. package/src/plugins/respArea/utils.jsx +26 -35
  132. package/src/plugins/table/icons/index.jsx +17 -11
  133. package/src/plugins/table/index.jsx +288 -231
  134. package/src/plugins/table/table-toolbar.jsx +15 -11
  135. package/src/plugins/toolbar/default-toolbar.jsx +65 -19
  136. package/src/plugins/toolbar/done-button.jsx +4 -4
  137. package/src/plugins/toolbar/editor-and-toolbar.jsx +150 -145
  138. package/src/plugins/toolbar/index.jsx +2 -3
  139. package/src/plugins/toolbar/toolbar-buttons.jsx +11 -11
  140. package/src/plugins/toolbar/toolbar.jsx +244 -221
  141. package/src/plugins/utils.js +21 -4
  142. package/src/serialization.jsx +32 -32
  143. package/src/test-serializer.js +139 -0
  144. package/src/test-serializer.js.rej +20 -0
@@ -10,19 +10,19 @@ const styles = () => ({
10
10
  display: 'inline-flex',
11
11
  padding: '2px',
12
12
  '& :hover': {
13
- color: 'black',
14
- },
13
+ color: 'black'
14
+ }
15
15
  },
16
16
  active: {
17
- color: 'black',
17
+ color: 'black'
18
18
  },
19
19
  disabled: {
20
20
  opacity: 0.7,
21
21
  cursor: 'not-allowed',
22
22
  '& :hover': {
23
- color: 'grey',
24
- },
25
- },
23
+ color: 'grey'
24
+ }
25
+ }
26
26
  });
27
27
 
28
28
  const log = debug('pie-elements:editable-html:raw-button');
@@ -34,14 +34,14 @@ export class RawButton extends React.Component {
34
34
  children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
35
35
  active: PropTypes.bool,
36
36
  disabled: PropTypes.bool,
37
- extraStyles: PropTypes.object,
37
+ extraStyles: PropTypes.object
38
38
  };
39
39
 
40
40
  constructor(props) {
41
41
  super(props);
42
42
  }
43
43
 
44
- onClick = (e) => {
44
+ onClick = e => {
45
45
  log('[onClick]');
46
46
  e.preventDefault();
47
47
  const { onClick } = this.props;
@@ -52,7 +52,7 @@ export class RawButton extends React.Component {
52
52
  const { active, classes, children, disabled, extraStyles } = this.props;
53
53
  const names = classNames(classes.button, {
54
54
  [classes.active]: active,
55
- [classes.disabled]: disabled,
55
+ [classes.disabled]: disabled
56
56
  });
57
57
 
58
58
  return (
@@ -71,14 +71,14 @@ export class RawMarkButton extends React.Component {
71
71
  mark: PropTypes.string,
72
72
  children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
73
73
  classes: PropTypes.object.isRequired,
74
- active: PropTypes.bool,
74
+ active: PropTypes.bool
75
75
  };
76
76
 
77
77
  constructor(props) {
78
78
  super(props);
79
79
  }
80
80
 
81
- onToggle = (e) => {
81
+ onToggle = e => {
82
82
  e.preventDefault();
83
83
  this.props.onToggle(this.props.mark);
84
84
  };
@@ -1,23 +1,22 @@
1
- import React from 'react';
2
- import { Change } from 'slate';
1
+ import React, { useEffect } from 'react';
2
+ import { Change, Editor, Element as SlateElement, Text, Node } from 'slate';
3
+ import { useSlate, useSlateSelection } from "slate-react";
3
4
  import Delete from '@material-ui/icons/Delete';
4
5
  import IconButton from '@material-ui/core/IconButton';
5
6
  import PropTypes from 'prop-types';
6
7
  import classNames from 'classnames';
7
8
  import debug from 'debug';
8
- import SlatePropTypes from 'slate-prop-types';
9
9
  import debounce from 'lodash/debounce';
10
10
 
11
11
  import { DoneButton } from './done-button';
12
12
 
13
- import { findSingleNode, findParentNode } from '../utils';
14
13
  import { withStyles } from '@material-ui/core/styles';
15
14
  import DefaultToolbar from './default-toolbar';
16
15
  import { removeDialogs as removeCharacterDialogs } from '../characters';
17
16
 
18
17
  const log = debug('@pie-lib:editable-html:plugins:toolbar');
19
18
 
20
- const getCustomToolbar = (plugin, node, value, handleDone, onDataChange) => {
19
+ const getCustomToolbar = (plugin, node, value, editor, handleDone) => {
21
20
  if (!plugin) {
22
21
  return;
23
22
  }
@@ -34,58 +33,74 @@ const getCustomToolbar = (plugin, node, value, handleDone, onDataChange) => {
34
33
  return plugin.toolbar.CustomToolbarComp;
35
34
  } else if (typeof plugin.toolbar.customToolbar === 'function') {
36
35
  log('deprecated - use CustomToolbarComp');
37
- return plugin.toolbar.customToolbar(node, value, handleDone, onDataChange);
36
+ return plugin.toolbar.customToolbar(node, value, editor, handleDone);
38
37
  }
39
38
  };
40
39
 
41
- export class Toolbar extends React.Component {
42
- static propTypes = {
43
- zIndex: PropTypes.number,
44
- value: SlatePropTypes.value.isRequired,
45
- plugins: PropTypes.array,
46
- plugin: PropTypes.object,
47
- onImageClick: PropTypes.func,
48
- onDone: PropTypes.func.isRequired,
49
- toolbarRef: PropTypes.func.isRequired,
50
- classes: PropTypes.object.isRequired,
51
- isFocused: PropTypes.bool,
52
- autoWidth: PropTypes.bool,
53
- onChange: PropTypes.func.isRequired,
54
- getFocusedValue: PropTypes.func.isRequired,
55
- pluginProps: PropTypes.object,
56
- toolbarOpts: PropTypes.shape({
57
- position: PropTypes.oneOf(['bottom', 'top']),
58
- alignment: PropTypes.oneOf(['left', 'right']),
59
- alwaysVisible: PropTypes.bool,
60
- ref: PropTypes.func,
61
- showDone: PropTypes.bool,
62
- }),
63
- onDataChange: PropTypes.func,
64
- };
65
-
66
- constructor(props) {
67
- super(props);
68
- this.state = {
69
- change: null,
70
- };
40
+ const style = {
41
+ toolbar: {
42
+ position: 'absolute',
43
+ zIndex: 10,
44
+ cursor: 'pointer',
45
+ justifyContent: 'space-between',
46
+ background: 'var(--editable-html-toolbar-bg, #efefef)',
47
+ minWidth: '280px',
48
+ margin: '5px 0 0 0',
49
+ padding: '2px',
50
+ boxShadow:
51
+ '0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12)',
52
+ boxSizing: 'border-box',
53
+ display: 'none'
54
+ },
55
+ toolbarWithNoDone: {
56
+ minWidth: '265px'
57
+ },
58
+ toolbarTop: {
59
+ top: '-45px'
60
+ },
61
+ toolbarRight: {
62
+ right: 0
63
+ },
64
+ fullWidth: {
65
+ width: '100%'
66
+ },
67
+ autoWidth: {
68
+ width: 'auto'
69
+ },
70
+ focused: {
71
+ display: 'flex'
72
+ },
73
+ iconRoot: {
74
+ width: '28px',
75
+ height: '28px',
76
+ padding: '4px',
77
+ verticalAlign: 'top'
78
+ },
79
+ label: {
80
+ color: 'var(--editable-html-toolbar-check, #00bb00)'
81
+ },
82
+ shared: {
83
+ display: 'flex'
71
84
  }
85
+ };
72
86
 
73
- componentWillUnmount() {
74
- removeCharacterDialogs();
75
- }
87
+ export const Toolbar = props => {
88
+ useEffect(() => {
89
+ return () => removeCharacterDialogs();
90
+ }, []);
76
91
 
77
- hasMark = (type) => {
78
- const { value } = this.props;
79
- return value.marks.some((mark) => mark.type == type);
92
+ const hasMark = type => {
93
+ const { value } = props;
94
+ return value.marks.some(mark => mark.type === type);
80
95
  };
81
96
 
82
- hasBlock = (type) => {
83
- const { value } = this.props;
84
- return value.blocks.some((node) => node.type == type);
97
+ const hasBlock = type => {
98
+ const { value } = props;
99
+ return value.blocks.some(node => node.type === type);
85
100
  };
86
101
 
87
- onToggle = (plugin) => {
88
- const { value, onChange } = this.props;
102
+ const onToggle = plugin => {
103
+ const { value, onChange } = props;
89
104
 
90
105
  if (!plugin.onToggle) return;
91
106
 
@@ -93,24 +108,23 @@ export class Toolbar extends React.Component {
93
108
  onChange(change);
94
109
  };
95
110
 
96
- onClick = (e) => {
111
+ const onClick = e => {
97
112
  log('[onClick]');
98
113
  e.preventDefault();
99
114
  };
100
115
 
101
- onButtonClick = (fn) => {
102
- return (e) => {
116
+ const onButtonClick = fn => {
117
+ return e => {
103
118
  e.preventDefault();
104
119
  fn();
105
120
  };
106
121
  };
107
122
 
108
- onToolbarDone = (change, finishEditing) => {
123
+ const onToolbarDone = (change, finishEditing) => {
109
124
  log('[onToolbarDone] change: ', change, 'finishEditing: ', finishEditing);
110
- const { onChange, onDone } = this.props;
125
+ const { onChange, onDone } = props;
111
126
 
112
- // use handler only if this is an actual Slate Change
113
- if (change instanceof Change) {
127
+ if (change) {
114
128
  onChange(change, () => {
115
129
  if (finishEditing) {
116
130
  onDone();
@@ -124,191 +138,200 @@ export class Toolbar extends React.Component {
124
138
  }
125
139
  };
126
140
 
127
- onDeleteClick = debounce((e, plugin, node, value, onChange) => plugin.deleteNode(e, node, value, onChange), 500);
141
+ const onDeleteClick = debounce(
142
+ (e, plugin, node, value, onChange) => plugin.deleteNode(e, node, value, onChange),
143
+ 500
144
+ );
128
145
 
129
- onDeleteMouseDown = (e, plugin, node, value, onChange) => {
146
+ const onDeleteMouseDown = (e, plugin, node, value, onChange) => {
130
147
  e.persist();
131
- this.onDeleteClick(e, plugin, node, value, onChange);
148
+ onDeleteClick(e, plugin, node, value, onChange);
132
149
  };
133
150
 
134
- render() {
135
- const {
136
- classes,
137
- plugins,
138
- pluginProps,
139
- toolbarOpts,
140
- value,
141
- autoWidth,
142
- onChange,
143
- getFocusedValue,
144
- isFocused,
145
- onDone,
146
- toolbarRef,
147
- } = this.props;
148
-
149
- const node = findSingleNode(value);
150
- const parentNode = findParentNode(value, node);
151
-
152
- log(' --------------> [render] node: ', node);
153
- log('[render] node: ', node);
154
-
155
- const plugin = plugins.find((p) => {
156
- if (!node) {
157
- return;
158
- }
151
+ const {
152
+ classes,
153
+ plugins,
154
+ pluginProps,
155
+ toolbarOpts,
156
+ value,
157
+ autoWidth,
158
+ onChange,
159
+ getFocusedValue,
160
+ isFocused,
161
+ onDone,
162
+ toolbarRef
163
+ } = props;
164
+
165
+ const editor = useSlate();
166
+ const selection = useSlateSelection();
167
+ const getNode = (editor, selection, depth) => {
168
+ if (!selection) {
169
+ return null;
170
+ }
159
171
 
160
- if (p.toolbar) {
161
- return p.toolbar.supports && p.toolbar.supports(node, value);
162
- }
163
- });
164
- const parentPlugin = plugins.find((p) => {
165
- if (!parentNode) {
166
- return;
167
- }
172
+ const [node, path] = Editor.node(editor, selection, depth ? { depth } : undefined);
168
173
 
169
- if (p.toolbar) {
170
- return p.toolbar.supports && p.toolbar.supports(parentNode, value);
171
- }
172
- });
174
+ if (!node) {
175
+ return null;
176
+ }
173
177
 
174
- log('[render] plugin: ', plugin);
178
+ if (!Text.isText(node)) {
179
+ return [node, path];
180
+ }
175
181
 
176
- const handleDone = (change, done) => {
177
- let handler = onDone;
182
+ return getNode(editor, selection, path.length - 1);
183
+ };
184
+ const getParentNode = (editor, path) => Array.isArray(path) && path.length > 0 && Editor.parent(editor, path);
178
185
 
179
- if (plugin && plugin.toolbar && plugin.toolbar.customToolbar) {
180
- handler = this.onToolbarDone;
181
- }
186
+ const [node, nodePath] = getNode(editor, selection) || [];
187
+ const [parentNode] = getParentNode(editor, nodePath) || [];
182
188
 
183
- handler(change, done);
189
+ log(' --------------> [render] node: ', node);
190
+ log('[render] node: ', node);
184
191
 
185
- if (parentPlugin && parentPlugin.handleDone) {
186
- parentPlugin.handleDone(value, node, plugin, onChange);
187
- }
188
- };
192
+ const plugin = plugins.find(p => {
193
+ if (!node) {
194
+ return;
195
+ }
189
196
 
190
- const handleDataChange = (key, data) => {
191
- this.props.onDataChange(key, data);
192
- };
197
+ if (p.toolbar) {
198
+ return p.supports && p.supports(node, editor, value);
199
+ }
200
+ });
201
+ const parentPlugin = plugins.find(p => {
202
+ if (!parentNode) {
203
+ return;
204
+ }
193
205
 
194
- const CustomToolbar = getCustomToolbar(plugin, node, value, handleDone, this.props.onDataChange);
206
+ if (p.toolbar) {
207
+ return p.supports && p.supports(parentNode, editor, value);
208
+ }
209
+ });
195
210
 
196
- const filteredPlugins = plugin && plugin.filterPlugins ? plugin.filterPlugins(node, plugins) : plugins;
211
+ log('[render] plugin: ', plugin);
197
212
 
198
- log('[render] CustomToolbar: ', CustomToolbar);
199
- const parentExtraStyles =
200
- parentPlugin && parentPlugin.pluginStyles ? parentPlugin.pluginStyles(node, parentNode, plugin) : {};
201
- const pluginExtraStyles = plugin && plugin.pluginStyles ? plugin.pluginStyles(node, parentNode, plugin) : {};
202
- const extraStyles = {
203
- ...pluginExtraStyles,
204
- ...parentExtraStyles,
205
- };
213
+ const handleDone = editor => {
214
+ let handler = onDone;
206
215
 
207
- const deletable = node && plugin && plugin.deleteNode;
208
- const customToolbarShowDone =
209
- node && plugin && plugin.toolbar && plugin.toolbar.showDone && !toolbarOpts.alwaysVisible;
210
-
211
- // If there is a toolbarOpts we check if the showDone is not equal to false
212
- const defaultToolbarShowDone = !toolbarOpts || toolbarOpts.showDone !== false;
213
-
214
- const hasDoneButton = defaultToolbarShowDone || customToolbarShowDone;
215
-
216
- const names = classNames(classes.toolbar, {
217
- [classes.toolbarWithNoDone]: !hasDoneButton,
218
- [classes.toolbarTop]: toolbarOpts.position === 'top',
219
- [classes.toolbarRight]: toolbarOpts.alignment === 'right',
220
- [classes.focused]: toolbarOpts.alwaysVisible || isFocused,
221
- [classes.autoWidth]: autoWidth,
222
- [classes.fullWidth]: !autoWidth,
223
- });
224
-
225
- return (
226
- <div className={names} style={extraStyles} onClick={this.onClick} ref={toolbarRef}>
227
- {CustomToolbar ? (
228
- <CustomToolbar
229
- node={node}
230
- value={value}
231
- onToolbarDone={this.onToolbarDone}
232
- onDataChange={handleDataChange}
233
- pluginProps={pluginProps}
234
- />
235
- ) : (
236
- <DefaultToolbar
237
- plugins={filteredPlugins}
238
- pluginProps={pluginProps}
239
- value={value}
240
- onChange={onChange}
241
- getFocusedValue={getFocusedValue}
242
- showDone={defaultToolbarShowDone}
243
- onDone={handleDone}
244
- deletable={deletable}
245
- />
246
- )}
216
+ if (plugin && plugin.toolbar && plugin.toolbar.customToolbar) {
217
+ handler = onToolbarDone;
218
+ }
219
+
220
+ handler(editor);
247
221
 
248
- <div className={classes.shared}>
249
- {deletable && (
250
- <IconButton
251
- aria-label="Delete"
252
- className={classes.iconRoot}
253
- onMouseDown={(e) => this.onDeleteMouseDown(e, plugin, node, value, onChange)}
254
- classes={{
255
- root: classes.iconRoot,
256
- }}
257
- >
258
- <Delete />
259
- </IconButton>
260
- )}
261
- {customToolbarShowDone && <DoneButton onClick={handleDone} />}
262
- </div>
222
+ if (parentPlugin && parentPlugin.handleDone) {
223
+ parentPlugin.handleDone(editor, node, plugin);
224
+ }
225
+ };
226
+
227
+ const CustomToolbar = getCustomToolbar(plugin, node, value, editor, handleDone);
228
+
229
+ const filteredPlugins =
230
+ plugin && plugin.filterPlugins ? plugin.filterPlugins(node, plugins) : plugins;
231
+
232
+ log('[render] CustomToolbar: ', CustomToolbar);
233
+ const parentExtraStyles =
234
+ parentPlugin && parentPlugin.pluginStyles
235
+ ? parentPlugin.pluginStyles(node, parentNode, plugin)
236
+ : {};
237
+ const pluginExtraStyles =
238
+ plugin && plugin.pluginStyles ? plugin.pluginStyles(node, parentNode, plugin) : {};
239
+ const extraStyles = {
240
+ ...pluginExtraStyles,
241
+ ...parentExtraStyles
242
+ };
243
+
244
+ const deletable = node && plugin && typeof plugin.deleteNode === 'function';
245
+ const customToolbarShowDone =
246
+ node && plugin && plugin.toolbar && plugin.toolbar.showDone && !toolbarOpts.alwaysVisible;
247
+
248
+ // If there is a toolbarOpts we check if the showDone is not equal to false
249
+ const defaultToolbarShowDone = !toolbarOpts || toolbarOpts.showDone !== false;
250
+
251
+ const hasDoneButton = defaultToolbarShowDone || customToolbarShowDone;
252
+
253
+ const names = classNames(classes.toolbar, {
254
+ [classes.toolbarWithNoDone]: !hasDoneButton,
255
+ [classes.toolbarTop]: toolbarOpts.position === 'top',
256
+ [classes.toolbarRight]: toolbarOpts.alignment === 'right',
257
+ [classes.focused]: toolbarOpts.alwaysVisible || isFocused,
258
+ [classes.autoWidth]: autoWidth,
259
+ [classes.fullWidth]: !autoWidth
260
+ });
261
+
262
+ return (
263
+ <div className={names} style={extraStyles} onClick={onClick} ref={toolbarRef}>
264
+ {CustomToolbar ? (
265
+ <CustomToolbar
266
+ editor={editor}
267
+ node={node}
268
+ nodePath={nodePath}
269
+ value={value}
270
+ onToolbarDone={onToolbarDone}
271
+ pluginProps={pluginProps}
272
+ />
273
+ ) : (
274
+ <DefaultToolbar
275
+ editor={editor}
276
+ nodePath={nodePath}
277
+ plugins={filteredPlugins}
278
+ pluginProps={pluginProps}
279
+ value={value}
280
+ onChange={onChange}
281
+ getFocusedValue={getFocusedValue}
282
+ showDone={defaultToolbarShowDone}
283
+ onDone={handleDone}
284
+ deletable={deletable}
285
+ />
286
+ )}
287
+
288
+ <div className={classes.shared}>
289
+ {deletable && (
290
+ <IconButton
291
+ aria-label="Delete"
292
+ className={classes.iconRoot}
293
+ onMouseDown={e => onDeleteMouseDown(e, plugin, node, value, onChange)}
294
+ classes={{
295
+ root: classes.iconRoot
296
+ }}
297
+ >
298
+ <Delete />
299
+ </IconButton>
300
+ )}
301
+ {customToolbarShowDone && <DoneButton onClick={handleDone} />}
263
302
  </div>
264
- );
265
- }
266
- }
303
+ </div>
304
+ );
305
+ };
267
306
 
268
- const style = {
269
- toolbar: {
270
- position: 'absolute',
271
- zIndex: 10,
272
- cursor: 'pointer',
273
- justifyContent: 'space-between',
274
- background: 'var(--editable-html-toolbar-bg, #efefef)',
275
- minWidth: '280px',
276
- margin: '5px 0 0 0',
277
- padding: '2px',
278
- boxShadow:
279
- '0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12)',
280
- boxSizing: 'border-box',
281
- display: 'none',
282
- },
283
- toolbarWithNoDone: {
284
- minWidth: '265px',
285
- },
286
- toolbarTop: {
287
- top: '-45px',
288
- },
289
- toolbarRight: {
290
- right: 0,
291
- },
292
- fullWidth: {
293
- width: '100%',
294
- },
295
- autoWidth: {
296
- width: 'auto',
297
- },
298
- focused: {
299
- display: 'flex',
300
- },
301
- iconRoot: {
302
- width: '28px',
303
- height: '28px',
304
- padding: '4px',
305
- verticalAlign: 'top',
306
- },
307
- label: {
308
- color: 'var(--editable-html-toolbar-check, #00bb00)',
309
- },
310
- shared: {
311
- display: 'flex',
312
- },
307
+ Toolbar.propTypes = {
308
+ editor: PropTypes.object.isRequired,
309
+ zIndex: PropTypes.number,
310
+ value: PropTypes.arrayOf(
311
+ PropTypes.shape({
312
+ type: PropTypes.string,
313
+ children: PropTypes.array,
314
+ data: PropTypes.object
315
+ })
316
+ ),
317
+ plugins: PropTypes.array,
318
+ plugin: PropTypes.object,
319
+ onImageClick: PropTypes.func,
320
+ onDone: PropTypes.func.isRequired,
321
+ toolbarRef: PropTypes.func.isRequired,
322
+ classes: PropTypes.object.isRequired,
323
+ isFocused: PropTypes.bool,
324
+ autoWidth: PropTypes.bool,
325
+ onChange: PropTypes.func.isRequired,
326
+ getFocusedValue: PropTypes.func.isRequired,
327
+ pluginProps: PropTypes.object,
328
+ toolbarOpts: PropTypes.shape({
329
+ position: PropTypes.oneOf(['bottom', 'top']),
330
+ alignment: PropTypes.oneOf(['left', 'right']),
331
+ alwaysVisible: PropTypes.bool,
332
+ ref: PropTypes.func,
333
+ showDone: PropTypes.bool
334
+ })
313
335
  };
336
+
314
337
  export default withStyles(style, { index: 1000 })(Toolbar);
@@ -1,4 +1,6 @@
1
- export const findSingleNode = (value) => {
1
+ import { Editor, Element as SlateElement } from "slate";
2
+
3
+ export const findSingleNode = value => {
2
4
  if (!value || !value.isCollapsed || !value.startKey) {
3
5
  return;
4
6
  }
@@ -24,8 +26,23 @@ export const findParentNode = (value, node) => {
24
26
  return value.document.getParent(node.key);
25
27
  };
26
28
 
27
- export const hasMark = (value, type) => value && value.marks.some((mark) => mark.type == type);
29
+ export const hasMark = (value, type) => value && value.marks.some(mark => mark.type == type);
30
+
31
+ export const hasBlock = (value, type) => value && value.blocks.some(node => node.type == type);
32
+
33
+ export const isBlockActive = (editor, format, blockType = 'type') => {
34
+ const { selection } = editor;
35
+ if (!selection) return false;
28
36
 
29
- export const hasBlock = (value, type) => value && value.blocks.some((node) => node.type == type);
37
+ const [match] = Array.from(
38
+ Editor.nodes(editor, {
39
+ at: Editor.unhangRange(editor, selection),
40
+ match: n => !Editor.isEditor(n) && SlateElement.isElement(n) && n[blockType] === format
41
+ })
42
+ );
43
+
44
+ return !!match;
45
+ };
30
46
 
31
- export const hasNode = ({ document }, type) => document && document.nodes.some((node) => node.type == type);
47
+ export const hasNode = ({ document }, type) =>
48
+ document && document.nodes.some(node => node.type == type);