@pie-lib/editable-html 10.0.0-beta.5 → 10.0.0-beta.7

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 +255 -152
  2. package/lib/components.js +15 -39
  3. package/lib/components.js.map +1 -1
  4. package/lib/editor.js +200 -356
  5. package/lib/editor.js.map +1 -1
  6. package/lib/index.js +25 -49
  7. package/lib/index.js.map +1 -1
  8. package/lib/new-serialization.js +6 -67
  9. package/lib/new-serialization.js.map +1 -1
  10. package/lib/parse-html.js +7 -6
  11. package/lib/parse-html.js.map +1 -1
  12. package/lib/plugins/characters/custom-popper.js +3 -13
  13. package/lib/plugins/characters/custom-popper.js.map +1 -1
  14. package/lib/plugins/characters/index.js +20 -59
  15. package/lib/plugins/characters/index.js.map +1 -1
  16. package/lib/plugins/characters/utils.js +1 -1
  17. package/lib/plugins/characters/utils.js.map +1 -1
  18. package/lib/plugins/hotKeys/index.js +9 -16
  19. package/lib/plugins/hotKeys/index.js.map +1 -1
  20. package/lib/plugins/image/alt-dialog.js +6 -27
  21. package/lib/plugins/image/alt-dialog.js.map +1 -1
  22. package/lib/plugins/image/component.js +42 -99
  23. package/lib/plugins/image/component.js.map +1 -1
  24. package/lib/plugins/image/image-toolbar.js +14 -50
  25. package/lib/plugins/image/image-toolbar.js.map +1 -1
  26. package/lib/plugins/image/index.js +16 -59
  27. package/lib/plugins/image/index.js.map +1 -1
  28. package/lib/plugins/image/insert-image-handler.js +13 -25
  29. package/lib/plugins/image/insert-image-handler.js.map +1 -1
  30. package/lib/plugins/index.js +6 -36
  31. package/lib/plugins/index.js.map +1 -1
  32. package/lib/plugins/list/index.js +11 -46
  33. package/lib/plugins/list/index.js.map +1 -1
  34. package/lib/plugins/math/index.js +89 -93
  35. package/lib/plugins/math/index.js.map +1 -1
  36. package/lib/plugins/media/index.js +32 -109
  37. package/lib/plugins/media/index.js.map +1 -1
  38. package/lib/plugins/media/media-dialog.js +107 -195
  39. package/lib/plugins/media/media-dialog.js.map +1 -1
  40. package/lib/plugins/media/media-toolbar.js +7 -27
  41. package/lib/plugins/media/media-toolbar.js.map +1 -1
  42. package/lib/plugins/media/media-wrapper.js +9 -14
  43. package/lib/plugins/media/media-wrapper.js.map +1 -1
  44. package/lib/plugins/respArea/drag-in-the-blank/choice.js +13 -53
  45. package/lib/plugins/respArea/drag-in-the-blank/choice.js.map +1 -1
  46. package/lib/plugins/respArea/drag-in-the-blank/index.js +6 -20
  47. package/lib/plugins/respArea/drag-in-the-blank/index.js.map +1 -1
  48. package/lib/plugins/respArea/explicit-constructed-response/index.js +5 -10
  49. package/lib/plugins/respArea/explicit-constructed-response/index.js.map +1 -1
  50. package/lib/plugins/respArea/icons/index.js +16 -31
  51. package/lib/plugins/respArea/icons/index.js.map +1 -1
  52. package/lib/plugins/respArea/index.js +7 -54
  53. package/lib/plugins/respArea/index.js.map +1 -1
  54. package/lib/plugins/respArea/inline-dropdown/index.js +3 -10
  55. package/lib/plugins/respArea/inline-dropdown/index.js.map +1 -1
  56. package/lib/plugins/respArea/utils.js +6 -21
  57. package/lib/plugins/respArea/utils.js.map +1 -1
  58. package/lib/plugins/table/icons/index.js +1 -8
  59. package/lib/plugins/table/icons/index.js.map +1 -1
  60. package/lib/plugins/table/index.js +54 -187
  61. package/lib/plugins/table/index.js.map +1 -1
  62. package/lib/plugins/table/table-toolbar.js +12 -44
  63. package/lib/plugins/table/table-toolbar.js.map +1 -1
  64. package/lib/plugins/toolbar/default-toolbar.js +17 -46
  65. package/lib/plugins/toolbar/default-toolbar.js.map +1 -1
  66. package/lib/plugins/toolbar/done-button.js +2 -10
  67. package/lib/plugins/toolbar/done-button.js.map +1 -1
  68. package/lib/plugins/toolbar/editor-and-toolbar.js +134 -144
  69. package/lib/plugins/toolbar/editor-and-toolbar.js.map +1 -1
  70. package/lib/plugins/toolbar/index.js +2 -6
  71. package/lib/plugins/toolbar/index.js.map +1 -1
  72. package/lib/plugins/toolbar/toolbar-buttons.js +9 -40
  73. package/lib/plugins/toolbar/toolbar-buttons.js.map +1 -1
  74. package/lib/plugins/toolbar/toolbar.js +29 -83
  75. package/lib/plugins/toolbar/toolbar.js.map +1 -1
  76. package/lib/plugins/utils.js +8 -30
  77. package/lib/plugins/utils.js.map +1 -1
  78. package/lib/serialization.js +11 -69
  79. package/lib/serialization.js.map +1 -1
  80. package/lib/test-serializer.js +3 -46
  81. package/lib/test-serializer.js.map +1 -1
  82. package/lib/theme.js +1 -1
  83. package/lib/theme.js.map +1 -1
  84. package/package.json +7 -7
  85. package/playground/image/data.js +20 -20
  86. package/playground/image/index.html +20 -22
  87. package/playground/image/index.jsx +10 -12
  88. package/playground/index.html +23 -25
  89. package/playground/mathquill/index.html +20 -23
  90. package/playground/mathquill/index.jsx +22 -18
  91. package/playground/prod-test/index.html +20 -24
  92. package/playground/prod-test/index.jsx +3 -5
  93. package/playground/schema-override/data.js +10 -10
  94. package/playground/schema-override/image-plugin.jsx +4 -3
  95. package/playground/schema-override/index.html +19 -21
  96. package/playground/schema-override/index.jsx +14 -13
  97. package/playground/serialization/data.js +10 -10
  98. package/playground/serialization/image-plugin.jsx +4 -3
  99. package/playground/serialization/index.html +20 -22
  100. package/playground/table-examples.html +8 -5
  101. package/playground/webpack.config.js +10 -10
  102. package/src/components.js +7 -7
  103. package/src/editor.jsx +144 -155
  104. package/src/index.jsx +24 -17
  105. package/src/new-serialization.jsx +22 -22
  106. package/src/parse-html.js +1 -1
  107. package/src/plugins/characters/custom-popper.js +7 -7
  108. package/src/plugins/characters/index.jsx +36 -26
  109. package/src/plugins/characters/utils.js +81 -81
  110. package/src/plugins/hotKeys/index.js +3 -3
  111. package/src/plugins/image/alt-dialog.jsx +5 -4
  112. package/src/plugins/image/component.jsx +52 -53
  113. package/src/plugins/image/image-toolbar.jsx +19 -27
  114. package/src/plugins/image/index.jsx +41 -47
  115. package/src/plugins/image/insert-image-handler.js +23 -14
  116. package/src/plugins/index.jsx +8 -10
  117. package/src/plugins/list/index.jsx +21 -24
  118. package/src/plugins/math/index.jsx +93 -40
  119. package/src/plugins/media/index.jsx +42 -42
  120. package/src/plugins/media/media-dialog.js +63 -89
  121. package/src/plugins/media/media-toolbar.jsx +8 -8
  122. package/src/plugins/media/media-wrapper.jsx +10 -7
  123. package/src/plugins/respArea/drag-in-the-blank/choice.jsx +19 -21
  124. package/src/plugins/respArea/drag-in-the-blank/index.jsx +10 -12
  125. package/src/plugins/respArea/explicit-constructed-response/index.jsx +6 -5
  126. package/src/plugins/respArea/icons/index.jsx +14 -11
  127. package/src/plugins/respArea/index.jsx +32 -56
  128. package/src/plugins/respArea/inline-dropdown/index.jsx +6 -6
  129. package/src/plugins/respArea/utils.jsx +15 -11
  130. package/src/plugins/table/icons/index.jsx +11 -17
  131. package/src/plugins/table/index.jsx +69 -69
  132. package/src/plugins/table/table-toolbar.jsx +8 -13
  133. package/src/plugins/toolbar/default-toolbar.jsx +15 -17
  134. package/src/plugins/toolbar/done-button.jsx +4 -4
  135. package/src/plugins/toolbar/editor-and-toolbar.jsx +50 -54
  136. package/src/plugins/toolbar/index.jsx +3 -2
  137. package/src/plugins/toolbar/toolbar-buttons.jsx +11 -11
  138. package/src/plugins/toolbar/toolbar.jsx +43 -42
  139. package/src/plugins/utils.js +7 -8
  140. package/src/serialization.jsx +34 -32
  141. package/src/test-serializer.js +13 -13
  142. package/lib/old-serialization.js +0 -330
  143. package/lib/slate-editor.js +0 -302
  144. package/package-lock.json +0 -3762
package/src/index.jsx CHANGED
@@ -5,6 +5,7 @@ import Editor, { DEFAULT_PLUGINS, ALL_PLUGINS } from './editor';
5
5
  import { htmlToValue, valueToHtml } from './new-serialization';
6
6
  import { parseDegrees } from './parse-html';
7
7
  import debug from 'debug';
8
+ import { Range } from 'slate';
8
9
 
9
10
  const log = debug('@pie-lib:editable-html');
10
11
  /**
@@ -48,15 +49,11 @@ const EditableHtml = React.forwardRef((props, forwardedRef) => {
48
49
  }
49
50
  };
50
51
 
51
- const focus = (position, node) => {
52
+ const focus = (position, node, select = false) => {
52
53
  if (this.editorRef) {
53
- this.editorRef.change(c => {
54
- const lastText = node
55
- ? c.value.document.getNextText(node.key)
56
- : c.value.document.getLastText();
57
- const editorDOM = document.querySelector(
58
- `[data-key="${this.editorRef.value.document.key}"]`
59
- );
54
+ this.editorRef.change((c) => {
55
+ const lastText = node ? c.value.document.getNextText(node.key) : c.value.document.getLastText();
56
+ const editorDOM = document.querySelector(`[data-key="${this.editorRef.value.document.key}"]`);
60
57
 
61
58
  if (editorDOM !== document.activeElement) {
62
59
  document.activeElement.blur();
@@ -65,15 +62,24 @@ const EditableHtml = React.forwardRef((props, forwardedRef) => {
65
62
  c.focus();
66
63
 
67
64
  if (position === 'end' && lastText) {
68
- c.moveFocusTo(lastText.key, lastText.text?.length).moveAnchorTo(
69
- lastText.key,
70
- lastText.text?.length
71
- );
65
+ c.moveFocusTo(lastText.key, lastText.text?.length).moveAnchorTo(lastText.key, lastText.text?.length);
66
+ if (select) {
67
+ const range = Range.fromJSON({
68
+ anchorKey: lastText.key,
69
+ anchorOffset: 0,
70
+ focusKey: lastText.key,
71
+ focusOffset: lastText.text?.length,
72
+ isFocused: true,
73
+ isBackward: false,
74
+ });
75
+ c.select(range);
76
+ }
72
77
  }
73
78
 
74
79
  if (position === 'beginning' && lastText) {
75
80
  c.moveFocusTo(lastText.key, 0).moveAnchorTo(lastText.key, 0);
76
81
  }
82
+ editorDOM.focus();
77
83
  });
78
84
  }
79
85
  };
@@ -88,13 +94,14 @@ const EditableHtml = React.forwardRef((props, forwardedRef) => {
88
94
  ...props,
89
95
  markup: null,
90
96
  value,
91
- onChange
97
+ onChange,
98
+ focus,
92
99
  };
93
100
 
94
101
  return (
95
102
  <Editor
96
103
  {...newProps}
97
- onRef={ref => {
104
+ onRef={(ref) => {
98
105
  if (ref) {
99
106
  rootRef.current = ref;
100
107
 
@@ -103,7 +110,7 @@ const EditableHtml = React.forwardRef((props, forwardedRef) => {
103
110
  }
104
111
  }
105
112
  }}
106
- editorRef={ref => ref && (editorRef.current = ref)}
113
+ editorRef={(ref) => ref && (editorRef.current = ref)}
107
114
  />
108
115
  );
109
116
  });
@@ -113,12 +120,12 @@ EditableHtml.propTypes = {
113
120
  onDone: PropTypes.func,
114
121
  onEditor: PropTypes.func,
115
122
  markup: PropTypes.string.isRequired,
116
- allowValidation: PropTypes.bool
123
+ allowValidation: PropTypes.bool,
117
124
  };
118
125
 
119
126
  EditableHtml.defaultProps = {
120
127
  onDone: () => {},
121
- allowValidation: false
128
+ allowValidation: false,
122
129
  };
123
130
 
124
131
  export default EditableHtml;
@@ -9,9 +9,9 @@ import { serialization as mediaSerialization } from './plugins/media';
9
9
  import { serialization as listSerialization } from './plugins/list';
10
10
  import { serialization as tableSerialization } from './plugins/table';
11
11
  import { serialization as responseAreaSerialization } from './plugins/respArea';
12
- import { Mark, Text, Value } from "slate";
13
- import { jsx } from "slate-hyperscript";
14
- import escapeHtml from "escape-html";
12
+ import { Mark, Text, Value } from 'slate';
13
+ import { jsx } from 'slate-hyperscript';
14
+ import escapeHtml from 'escape-html';
15
15
 
16
16
  const log = debug('@pie-lib:editable-html:serialization');
17
17
 
@@ -32,7 +32,7 @@ export const BLOCK_TAGS = {
32
32
  h3: 'heading-three',
33
33
  h4: 'heading-four',
34
34
  h5: 'heading-five',
35
- h6: 'heading-six'
35
+ h6: 'heading-six',
36
36
  };
37
37
 
38
38
  /**
@@ -48,10 +48,10 @@ export const MARK_TAGS = {
48
48
  s: 'strikethrough',
49
49
  del: 'strikethrough',
50
50
  code: 'code',
51
- strong: 'bold'
51
+ strong: 'bold',
52
52
  };
53
53
 
54
- export const parseStyleString = s => {
54
+ export const parseStyleString = (s) => {
55
55
  const regex = /([\w-]*)\s*:\s*([^;]*)/g;
56
56
  let match;
57
57
  const result = {};
@@ -61,18 +61,18 @@ export const parseStyleString = s => {
61
61
  return result;
62
62
  };
63
63
 
64
- export const getBase64 = file => {
64
+ export const getBase64 = (file) => {
65
65
  return new Promise((resolve, reject) => {
66
66
  const reader = new FileReader();
67
67
  reader.readAsDataURL(file);
68
68
  reader.onload = () => resolve(reader.result);
69
- reader.onerror = error => reject(error);
69
+ reader.onerror = (error) => reject(error);
70
70
  });
71
71
  };
72
72
 
73
- export const reactAttributes = o => toStyleObject(o, { camelize: true, addUnits: false });
73
+ export const reactAttributes = (o) => toStyleObject(o, { camelize: true, addUnits: false });
74
74
 
75
- const attributesToMap = el => (acc, attribute) => {
75
+ const attributesToMap = (el) => (acc, attribute) => {
76
76
  const value = el.getAttribute(attribute);
77
77
  if (value) {
78
78
  if (attribute === 'style') {
@@ -116,9 +116,9 @@ const blocks = {
116
116
  /**
117
117
  * Here for rendering styles for all block elements
118
118
  */
119
- data: { attributes: attributes.reduce(attributesToMap(el), {}) }
119
+ data: { attributes: attributes.reduce(attributesToMap(el), {}) },
120
120
  },
121
- next(el.childNodes)
121
+ next(el.childNodes),
122
122
  );
123
123
  },
124
124
  serialize: (object, children) => {
@@ -136,7 +136,7 @@ const blocks = {
136
136
  return <Tag {...jsonData.attributes}>{children}</Tag>;
137
137
  }
138
138
  }
139
- }
139
+ },
140
140
  };
141
141
 
142
142
  const marks = {
@@ -175,10 +175,10 @@ const marks = {
175
175
 
176
176
  return string;
177
177
  }
178
- }
178
+ },
179
179
  };
180
180
 
181
- const findPreviousText = el => {
181
+ const findPreviousText = (el) => {
182
182
  if (el.nodeName === '#text') {
183
183
  return el;
184
184
  }
@@ -217,7 +217,7 @@ export const TEXT_RULE = {
217
217
  return array;
218
218
  }, []);
219
219
  }
220
- }
220
+ },
221
221
  };
222
222
 
223
223
  const RULES = [
@@ -229,7 +229,7 @@ const RULES = [
229
229
  responseAreaSerialization,
230
230
  TEXT_RULE,
231
231
  blocks,
232
- marks
232
+ marks,
233
233
  ];
234
234
 
235
235
  function allWhitespace(node) {
@@ -240,7 +240,7 @@ function allWhitespace(node) {
240
240
  function defaultParseHtml(html) {
241
241
  if (typeof DOMParser === 'undefined') {
242
242
  throw new Error(
243
- 'The native `DOMParser` global which the `Html` serializer uses by default is not present in this environment. You must supply the `options.parseHtml` function instead.'
243
+ 'The native `DOMParser` global which the `Html` serializer uses by default is not present in this environment. You must supply the `options.parseHtml` function instead.',
244
244
  );
245
245
  }
246
246
 
@@ -264,14 +264,14 @@ function defaultParseHtml(html) {
264
264
  const parseHtml =
265
265
  typeof window === 'undefined'
266
266
  ? () => ({
267
- childNodes: []
267
+ childNodes: [],
268
268
  })
269
269
  : defaultParseHtml;
270
270
 
271
271
  const serializer = new TestSerializer({
272
272
  defaultBlock: 'div',
273
273
  rules: RULES,
274
- parseHtml
274
+ parseHtml,
275
275
  });
276
276
 
277
277
  const _extends =
@@ -290,7 +290,7 @@ const _extends =
290
290
  return target;
291
291
  };
292
292
 
293
- export const htmlToValue = html => {
293
+ export const htmlToValue = (html) => {
294
294
  try {
295
295
  return serializer.deserialize(html);
296
296
  } catch (e) {
@@ -299,7 +299,7 @@ export const htmlToValue = html => {
299
299
  }
300
300
  };
301
301
 
302
- export const valueToHtml = value => serializer.serialize(value);
302
+ export const valueToHtml = (value) => serializer.serialize(value);
303
303
 
304
304
  /**
305
305
  *
package/src/parse-html.js CHANGED
@@ -1,4 +1,4 @@
1
- export const parseDegrees = html =>
1
+ export const parseDegrees = (html) =>
2
2
  html
3
3
  // removes \( use case: 50°
4
4
  .replace(/\\[(]/g, '')
@@ -8,17 +8,17 @@ const styles = () => ({
8
8
  background: '#fff',
9
9
  padding: '10px',
10
10
  pointerEvents: 'none',
11
- zIndex: 99999
11
+ zIndex: 99999,
12
12
  },
13
13
  paper: {
14
14
  padding: 20,
15
15
  height: 'auto',
16
- width: 'auto'
16
+ width: 'auto',
17
17
  },
18
18
  typography: {
19
19
  fontSize: 50,
20
- textAlign: 'center'
21
- }
20
+ textAlign: 'center',
21
+ },
22
22
  });
23
23
 
24
24
  const CustomPopper = withStyles(styles)(({ classes, children, ...props }) => (
@@ -27,15 +27,15 @@ const CustomPopper = withStyles(styles)(({ classes, children, ...props }) => (
27
27
  open
28
28
  className={classes.popover}
29
29
  classes={{
30
- paper: classes.paper
30
+ paper: classes.paper,
31
31
  }}
32
32
  anchorOrigin={{
33
33
  vertical: 'bottom',
34
- horizontal: 'left'
34
+ horizontal: 'left',
35
35
  }}
36
36
  transformOrigin={{
37
37
  vertical: 'top',
38
- horizontal: 'left'
38
+ horizontal: 'left',
39
39
  }}
40
40
  disableRestoreFocus
41
41
  disableAutoFocus
@@ -1,5 +1,8 @@
1
1
  import React from 'react';
2
+ import { ReactEditor } from 'slate-react';
3
+ import { Editor } from 'slate';
2
4
  import ReactDOM from 'react-dom';
5
+ import PropTypes from 'prop-types';
3
6
  import debug from 'debug';
4
7
  import get from 'lodash/get';
5
8
 
@@ -8,22 +11,20 @@ import { PureToolbar } from '@pie-lib/math-toolbar';
8
11
  import CustomPopper from './custom-popper';
9
12
  import { insertSnackBar } from '../respArea/utils';
10
13
  import { characterIcons, spanishConfig, specialConfig } from './utils';
11
- import { ReactEditor } from 'slate-react';
12
- import { Editor } from 'slate';
13
14
  const log = debug('@pie-lib:editable-html:plugins:characters');
14
15
 
15
16
  const removePopOvers = () => {
16
17
  const prevPopOvers = document.querySelectorAll('#mouse-over-popover');
17
18
 
18
19
  log('[characters:removePopOvers]');
19
- prevPopOvers.forEach(s => s.remove());
20
+ prevPopOvers.forEach((s) => s.remove());
20
21
  };
21
22
 
22
23
  export const removeDialogs = () => {
23
24
  const prevDialogs = document.querySelectorAll('.insert-character-dialog');
24
25
 
25
26
  log('[characters:removeDialogs]');
26
- prevDialogs.forEach(s => s.remove());
27
+ prevDialogs.forEach((s) => s.remove());
27
28
  removePopOvers();
28
29
  };
29
30
 
@@ -62,7 +63,7 @@ const insertDialog = ({ editor, callback, opts }) => {
62
63
 
63
64
  return obj;
64
65
  },
65
- { rows: configToUse.characters.length, columns: 0 }
66
+ { rows: configToUse.characters.length, columns: 0 },
66
67
  );
67
68
 
68
69
  let popoverEl;
@@ -93,7 +94,7 @@ const insertDialog = ({ editor, callback, opts }) => {
93
94
 
94
95
  <div style={infoStyle}>{el.unicode}</div>
95
96
  </CustomPopper>,
96
- popoverEl
97
+ popoverEl,
97
98
  );
98
99
 
99
100
  document.body.appendChild(newEl);
@@ -101,13 +102,12 @@ const insertDialog = ({ editor, callback, opts }) => {
101
102
 
102
103
  let firstCallMade = false;
103
104
 
104
- const listener = e => {
105
+ const listener = (e) => {
105
106
  // this will be triggered right after setting it because
106
107
  // this toolbar is added on the mousedown event
107
108
  // so right after mouseup, the click will be triggered
108
109
  if (firstCallMade) {
109
- const focusIsInModals =
110
- newEl.contains(e.target) || (popoverEl && popoverEl.contains(e.target));
110
+ const focusIsInModals = newEl.contains(e.target) || (popoverEl && popoverEl.contains(e.target));
111
111
  const editorDOM = ReactEditor.toDOMNode(editor, editor);
112
112
  const focusIsInEditor = editorDOM.contains(e.target);
113
113
 
@@ -126,7 +126,7 @@ const insertDialog = ({ editor, callback, opts }) => {
126
126
  document.body.removeEventListener('click', listener);
127
127
  };
128
128
 
129
- const handleChange = val => {
129
+ const handleChange = (val) => {
130
130
  if (typeof val === 'string') {
131
131
  callback(val, true);
132
132
  }
@@ -138,11 +138,12 @@ const insertDialog = ({ editor, callback, opts }) => {
138
138
  noDecimal
139
139
  hideInput
140
140
  noLatexHandling
141
+ hideDoneButtonBackground
141
142
  layoutForKeyPad={layoutForCharacters}
142
143
  additionalKeys={configToUse.characters.reduce((arr, n) => {
143
144
  arr = [
144
145
  ...arr,
145
- ...n.map(k => ({
146
+ ...n.map((k) => ({
146
147
  name: get(k, 'name') || k,
147
148
  write: get(k, 'write') || k,
148
149
  label: get(k, 'label') || k,
@@ -152,15 +153,15 @@ const insertDialog = ({ editor, callback, opts }) => {
152
153
  ...(k.extraProps || {}),
153
154
  style: {
154
155
  ...(k.extraProps || {}).style,
155
- border: '1px solid #000'
156
- }
156
+ border: '1px solid #000',
157
+ },
157
158
  },
158
159
  ...(configToUse.hasPreview
159
160
  ? {
160
- actions: { onMouseEnter: ev => renderPopOver(ev, k), onMouseLeave: closePopOver }
161
+ actions: { onMouseEnter: (ev) => renderPopOver(ev, k), onMouseLeave: closePopOver },
161
162
  }
162
- : {})
163
- }))
163
+ : {}),
164
+ })),
164
165
  ];
165
166
 
166
167
  return arr;
@@ -180,14 +181,19 @@ const insertDialog = ({ editor, callback, opts }) => {
180
181
  const boundRect = cursorItem.getBoundingClientRect();
181
182
 
182
183
  document.body.appendChild(newEl);
184
+
185
+ // when height of toolbar exceeds screen - can happen in scrollable contexts
186
+ let additionalTopOffset = 0;
187
+ if (boundRect.y < newEl.offsetHeight) {
188
+ additionalTopOffset = newEl.offsetHeight - boundRect.y + 10;
189
+ }
190
+
191
+ newEl.style.maxWidth = '500px';
183
192
  newEl.style.position = 'absolute';
184
- newEl.style.top = `${boundRect.top + Math.abs(bodyRect.top) - newEl.offsetHeight - 10}px`;
193
+ newEl.style.top = `${boundRect.top + Math.abs(bodyRect.top) - newEl.offsetHeight - 10 + additionalTopOffset}px`;
185
194
  newEl.style.zIndex = 99999;
186
195
 
187
- const leftValue = `${boundRect.left +
188
- Math.abs(bodyRect.left) +
189
- cursorItem.offsetWidth +
190
- 10}px`;
196
+ const leftValue = `${boundRect.left + Math.abs(bodyRect.left) + cursorItem.offsetWidth + 10}px`;
191
197
 
192
198
  const rightValue = `${boundRect.x}px`;
193
199
 
@@ -218,20 +224,24 @@ const CharacterIcon = ({ letter }) => (
218
224
  <div
219
225
  style={{
220
226
  fontSize: '25px',
221
- lineHeight: '15px'
227
+ lineHeight: '15px',
222
228
  }}
223
229
  >
224
230
  {letter}
225
231
  </div>
226
232
  );
227
233
 
234
+ CharacterIcon.propTypes = {
235
+ letter: PropTypes.string,
236
+ };
237
+
228
238
  export default function CharactersPlugin(opts) {
229
239
  removeDialogs();
230
240
  return {
231
241
  name: 'characters',
232
242
  toolbar: {
233
243
  icon: <CharacterIcon letter={opts.characterIcon || characterIcons[opts.language] || 'ñ'} />,
234
- onClick: editor => {
244
+ onClick: (editor) => {
235
245
  const callback = (char, focus) => {
236
246
  if (char) {
237
247
  log('[characters:insert]: ', char);
@@ -246,16 +256,16 @@ export default function CharactersPlugin(opts) {
246
256
  };
247
257
 
248
258
  insertDialog({ editor, callback, opts });
249
- }
259
+ },
250
260
  },
251
261
 
252
262
  pluginStyles: (node, parentNode, p) => {
253
263
  if (p) {
254
264
  return {
255
265
  position: 'absolute',
256
- top: 'initial'
266
+ top: 'initial',
257
267
  };
258
268
  }
259
- }
269
+ },
260
270
  };
261
271
  }