@pie-lib/editable-html-tip-tap 1.0.2 → 1.0.3

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 (129) hide show
  1. package/lib/components/CharacterPicker.js +221 -0
  2. package/lib/components/EditableHtml.js +323 -0
  3. package/lib/components/MenuBar.js +693 -0
  4. package/lib/components/TiptapContainer.js +90 -0
  5. package/lib/components/buttons/done-button.js +53 -0
  6. package/lib/components/characters/characterUtils.js +112 -0
  7. package/lib/components/characters/custom-popper.js +73 -0
  8. package/lib/components/common/done-button.js +53 -0
  9. package/lib/components/icons/CssIcon.js +37 -0
  10. package/lib/components/icons/RespArea.js +95 -0
  11. package/lib/components/icons/TableIcons.js +69 -0
  12. package/lib/components/icons/TextAlign.js +194 -0
  13. package/lib/components/icons/index.js +194 -0
  14. package/lib/components/image/ImageToolbar.js +16 -0
  15. package/lib/components/image/InsertImageHandler.js +16 -0
  16. package/lib/components/media/MediaDialog.js +16 -0
  17. package/lib/components/media/MediaToolbar.js +16 -0
  18. package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js +94 -0
  19. package/lib/components/respArea/DragInTheBlank/choice.js +289 -0
  20. package/lib/components/respArea/DragInTheBlank.js +94 -0
  21. package/lib/components/respArea/ExplicitConstructedResponse.js +120 -0
  22. package/lib/components/respArea/InlineDropdown.js +126 -0
  23. package/lib/components/respArea/ToolbarIcon.js +105 -0
  24. package/lib/components/respArea/choice.js +2 -0
  25. package/lib/extensions/component.js +5 -5
  26. package/lib/extensions/custom-toolbar-wrapper.js +2 -4
  27. package/lib/extensions/extended-table.js +30 -0
  28. package/lib/extensions/index.js +52 -0
  29. package/lib/extensions/media.js +5 -5
  30. package/lib/extensions/responseArea.js +7 -7
  31. package/lib/index.js +16 -1481
  32. package/lib/plugins/index.js +8 -80
  33. package/lib/styles/editorContainerStyles.js +200 -0
  34. package/lib/utils/size.js +34 -0
  35. package/package.json +1 -1
  36. package/src/components/CharacterPicker.jsx +185 -0
  37. package/src/components/EditableHtml.jsx +306 -0
  38. package/src/components/MenuBar.jsx +630 -0
  39. package/src/components/TiptapContainer.jsx +96 -0
  40. package/src/components/characters/characterUtils.js +127 -0
  41. package/src/components/image/ImageToolbar.jsx +1 -0
  42. package/src/components/image/InsertImageHandler.js +1 -0
  43. package/src/components/media/MediaDialog.js +1 -0
  44. package/src/components/media/MediaToolbar.jsx +1 -0
  45. package/src/{plugins/respArea/drag-in-the-blank → components/respArea/DragInTheBlank}/choice.jsx +1 -1
  46. package/src/{plugins/respArea/inline-dropdown/index.jsx → components/respArea/InlineDropdown.jsx} +1 -1
  47. package/src/components/respArea/ToolbarIcon.jsx +68 -0
  48. package/src/extensions/component.jsx +2 -2
  49. package/src/extensions/custom-toolbar-wrapper.jsx +6 -7
  50. package/src/extensions/extended-table.js +27 -0
  51. package/src/extensions/index.js +76 -0
  52. package/src/extensions/media.js +10 -4
  53. package/src/extensions/responseArea.js +7 -7
  54. package/src/index.jsx +3 -1440
  55. package/src/styles/editorContainerStyles.js +203 -0
  56. package/src/utils/size.js +32 -0
  57. package/src/__tests__/editor.test.jsx +0 -363
  58. package/src/__tests__/serialization.test.js +0 -291
  59. package/src/block-tags.js +0 -17
  60. package/src/editor.jsx +0 -1197
  61. package/src/extensions/characters.js +0 -46
  62. package/src/old-index.jsx +0 -162
  63. package/src/parse-html.js +0 -8
  64. package/src/plugins/README.md +0 -27
  65. package/src/plugins/characters/index.jsx +0 -284
  66. package/src/plugins/characters/utils.js +0 -447
  67. package/src/plugins/css/index.jsx +0 -340
  68. package/src/plugins/customPlugin/index.jsx +0 -85
  69. package/src/plugins/html/icons/index.jsx +0 -19
  70. package/src/plugins/html/index.jsx +0 -72
  71. package/src/plugins/image/__tests__/__snapshots__/component.test.jsx.snap +0 -51
  72. package/src/plugins/image/__tests__/__snapshots__/image-toolbar-logic.test.jsx.snap +0 -27
  73. package/src/plugins/image/__tests__/__snapshots__/image-toolbar.test.jsx.snap +0 -44
  74. package/src/plugins/image/__tests__/component.test.jsx +0 -41
  75. package/src/plugins/image/__tests__/image-toolbar-logic.test.jsx +0 -42
  76. package/src/plugins/image/__tests__/image-toolbar.test.jsx +0 -11
  77. package/src/plugins/image/__tests__/index.test.js +0 -95
  78. package/src/plugins/image/__tests__/insert-image-handler.test.js +0 -113
  79. package/src/plugins/image/__tests__/mock-change.js +0 -15
  80. package/src/plugins/image/alt-dialog.jsx +0 -82
  81. package/src/plugins/image/component.jsx +0 -343
  82. package/src/plugins/image/image-toolbar.jsx +0 -100
  83. package/src/plugins/image/index.jsx +0 -227
  84. package/src/plugins/image/insert-image-handler.js +0 -79
  85. package/src/plugins/index.jsx +0 -377
  86. package/src/plugins/list/__tests__/index.test.js +0 -54
  87. package/src/plugins/list/index.jsx +0 -305
  88. package/src/plugins/math/__tests__/__snapshots__/index.test.jsx.snap +0 -48
  89. package/src/plugins/math/__tests__/index.test.jsx +0 -245
  90. package/src/plugins/math/index.jsx +0 -379
  91. package/src/plugins/media/__tests__/index.test.js +0 -75
  92. package/src/plugins/media/index.jsx +0 -325
  93. package/src/plugins/media/media-dialog.js +0 -624
  94. package/src/plugins/media/media-toolbar.jsx +0 -56
  95. package/src/plugins/media/media-wrapper.jsx +0 -43
  96. package/src/plugins/rendering/index.js +0 -31
  97. package/src/plugins/respArea/index.jsx +0 -299
  98. package/src/plugins/respArea/math-templated/index.jsx +0 -104
  99. package/src/plugins/respArea/utils.jsx +0 -90
  100. package/src/plugins/table/CustomTablePlugin.js +0 -113
  101. package/src/plugins/table/__tests__/__snapshots__/table-toolbar.test.jsx.snap +0 -44
  102. package/src/plugins/table/__tests__/index.test.jsx +0 -401
  103. package/src/plugins/table/__tests__/table-toolbar.test.jsx +0 -42
  104. package/src/plugins/table/index.jsx +0 -427
  105. package/src/plugins/table/table-toolbar.jsx +0 -136
  106. package/src/plugins/textAlign/index.jsx +0 -23
  107. package/src/plugins/toolbar/__tests__/__snapshots__/default-toolbar.test.jsx.snap +0 -923
  108. package/src/plugins/toolbar/__tests__/__snapshots__/editor-and-toolbar.test.jsx.snap +0 -20
  109. package/src/plugins/toolbar/__tests__/__snapshots__/toolbar-buttons.test.jsx.snap +0 -36
  110. package/src/plugins/toolbar/__tests__/__snapshots__/toolbar.test.jsx.snap +0 -46
  111. package/src/plugins/toolbar/__tests__/default-toolbar.test.jsx +0 -94
  112. package/src/plugins/toolbar/__tests__/editor-and-toolbar.test.jsx +0 -37
  113. package/src/plugins/toolbar/__tests__/toolbar-buttons.test.jsx +0 -51
  114. package/src/plugins/toolbar/__tests__/toolbar.test.jsx +0 -106
  115. package/src/plugins/toolbar/default-toolbar.jsx +0 -206
  116. package/src/plugins/toolbar/editor-and-toolbar.jsx +0 -257
  117. package/src/plugins/toolbar/index.jsx +0 -23
  118. package/src/plugins/toolbar/toolbar-buttons.jsx +0 -138
  119. package/src/plugins/toolbar/toolbar.jsx +0 -338
  120. package/src/plugins/utils.js +0 -31
  121. package/src/serialization.jsx +0 -621
  122. /package/src/{plugins → components}/characters/custom-popper.js +0 -0
  123. /package/src/{plugins/toolbar → components/common}/done-button.jsx +0 -0
  124. /package/src/{plugins/css/icons/index.jsx → components/icons/CssIcon.jsx} +0 -0
  125. /package/src/{plugins/respArea/icons/index.jsx → components/icons/RespArea.jsx} +0 -0
  126. /package/src/{plugins/table/icons/index.jsx → components/icons/TableIcons.jsx} +0 -0
  127. /package/src/{plugins/textAlign/icons/index.jsx → components/icons/TextAlign.jsx} +0 -0
  128. /package/src/{plugins/respArea/drag-in-the-blank/index.jsx → components/respArea/DragInTheBlank/DragInTheBlank.jsx} +0 -0
  129. /package/src/{plugins/respArea/explicit-constructed-response/index.jsx → components/respArea/ExplicitConstructedResponse.jsx} +0 -0
@@ -1,46 +0,0 @@
1
- // InlineNodes.js
2
- import React from 'react';
3
- import { Node, ReactNodeViewRenderer } from '@tiptap/react';
4
- import ExplicitConstructedResponse from '../plugins/respArea/explicit-constructed-response';
5
- import DragInTheBlank from '../plugins/respArea/drag-in-the-blank';
6
- import InlineDropdown from '../plugins/respArea/inline-dropdown';
7
-
8
- /**
9
- * ExplicitConstructedResponse Node
10
- */
11
- export const ExplicitConstructedResponseNode = Node.create({
12
- name: 'explicit_constructed_response',
13
- group: 'inline',
14
- inline: true,
15
- atom: true,
16
- addAttributes() {
17
- return {
18
- index: { default: null },
19
- value: { default: '' },
20
- };
21
- },
22
- parseHTML() {
23
- return [
24
- {
25
- tag: 'span[data-type="explicit_constructed_response"]',
26
- getAttrs: (el) => ({
27
- index: el.dataset.index,
28
- value: el.dataset.value,
29
- }),
30
- },
31
- ];
32
- },
33
- renderHTML({ HTMLAttributes }) {
34
- return [
35
- 'span',
36
- {
37
- 'data-type': 'explicit_constructed_response',
38
- 'data-index': HTMLAttributes.index,
39
- 'data-value': HTMLAttributes.value,
40
- },
41
- ];
42
- },
43
- addNodeView() {
44
- return ReactNodeViewRenderer(ExplicitConstructedResponse);
45
- },
46
- });
package/src/old-index.jsx DELETED
@@ -1,162 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import Editor, { DEFAULT_PLUGINS, ALL_PLUGINS } from './editor';
4
- import { extraCSSRulesOpts, htmlToValue, valueToHtml, reduceMultipleBrs } from './serialization';
5
- import { parseDegrees } from './parse-html';
6
- import constants from './constants';
7
- import debug from 'debug';
8
- import { Range } from 'slate';
9
-
10
- const log = debug('@pie-lib:editable-html');
11
-
12
- /**
13
- * Wrapper around the editor that exposes a `markup` and `onChange(markup:string)` api.
14
- * Because of the mismatch between the markup and the `Value` we need to convert the incoming markup to a value and
15
- * compare it. TODO: This is an interim fix, we'll need to strip back `Editor` and look how best to maintain the
16
- * `markup` api whilst avoiding the serialization mismatch. We should be making better use of schemas w/ normalize.
17
- */
18
- export default class EditableHtml extends React.Component {
19
- static propTypes = {
20
- error: PropTypes.any,
21
- onChange: PropTypes.func.isRequired,
22
- onDone: PropTypes.func,
23
- markup: PropTypes.string.isRequired,
24
- allowValidation: PropTypes.bool,
25
- toolbarOpts: PropTypes.object,
26
- extraCSSRules: PropTypes.shape({
27
- names: PropTypes.arrayOf(PropTypes.string),
28
- rules: PropTypes.string,
29
- }),
30
- };
31
-
32
- static defaultProps = {
33
- onDone: () => {},
34
- allowValidation: false,
35
- };
36
-
37
- constructor(props) {
38
- super(props);
39
-
40
- if (props.extraCSSRules) {
41
- Object.assign(extraCSSRulesOpts, this.props.extraCSSRules);
42
- }
43
-
44
- const v = htmlToValue(props.markup);
45
- this.state = {
46
- value: v,
47
- };
48
- }
49
-
50
- // eslint-disable-next-line react/no-deprecated
51
- componentWillReceiveProps(props) {
52
- if (!props.allowValidation && props.markup === this.props.markup) {
53
- return;
54
- }
55
-
56
- const v = htmlToValue(props.markup);
57
- const current = htmlToValue(this.props.markup);
58
-
59
- if (v.equals && !v.equals(current)) {
60
- this.setState({ value: v });
61
- }
62
- }
63
-
64
- runSerializationOnMarkup = () => {
65
- if (!this.props.markup) {
66
- return;
67
- }
68
-
69
- const v = htmlToValue(reduceMultipleBrs(this.props.markup));
70
-
71
- this.setState({ value: v });
72
- };
73
-
74
- onChange = (value, done) => {
75
- const html = valueToHtml(value);
76
- const htmlParsed = parseDegrees(html);
77
-
78
- if (htmlParsed !== this.props.markup && this.props.onChange) {
79
- this.props.onChange(htmlParsed);
80
- }
81
-
82
- if (done && this.props.onDone) {
83
- this.props.onDone(htmlParsed);
84
- }
85
- };
86
-
87
- focus = (position, node, select = false) => {
88
- if (this.editorRef) {
89
- this.editorRef.change((c) => {
90
- const lastText = node ? c.value.document.getNextText(node.key) : c.value.document.getLastText();
91
- const editorDOM = document.querySelector(`[data-key="${this.editorRef.value.document.key}"]`);
92
-
93
- if (editorDOM !== document.activeElement) {
94
- document.activeElement.blur();
95
- }
96
-
97
- c.focus();
98
-
99
- if (position === 'end' && lastText) {
100
- c.moveFocusTo(lastText.key, lastText.text?.length).moveAnchorTo(lastText.key, lastText.text?.length);
101
- if (select) {
102
- const range = Range.fromJSON({
103
- anchorKey: lastText.key,
104
- anchorOffset: 0,
105
- focusKey: lastText.key,
106
- focusOffset: lastText.text?.length,
107
- isFocused: true,
108
- isBackward: false,
109
- });
110
- c.select(range);
111
- }
112
- }
113
-
114
- if (position === 'beginning' && lastText) {
115
- c.moveFocusTo(lastText.key, 0).moveAnchorTo(lastText.key, 0);
116
- }
117
- editorDOM.focus();
118
- });
119
- }
120
- };
121
-
122
- finishEditing = () => {
123
- if (this.editorRef) {
124
- this.editorRef.props.onEditingDone();
125
- }
126
- };
127
-
128
- render() {
129
- const { value } = this.state;
130
- const { toolbarOpts, error } = this.props;
131
-
132
- if (toolbarOpts) {
133
- toolbarOpts.error = error;
134
- }
135
-
136
- const props = {
137
- ...this.props,
138
- markup: null,
139
- value,
140
- onChange: this.onChange,
141
- focus: this.focus,
142
- runSerializationOnMarkup: this.runSerializationOnMarkup,
143
- };
144
-
145
- return (
146
- <Editor
147
- onRef={(ref) => {
148
- if (ref) {
149
- this.rootRef = ref;
150
- }
151
- }}
152
- editorRef={(ref) => ref && (this.editorRef = ref)}
153
- {...props}
154
- />
155
- );
156
- }
157
- }
158
-
159
- /**
160
- * Export lower level Editor and serialization functions.
161
- */
162
- export { htmlToValue, valueToHtml, Editor, DEFAULT_PLUGINS, ALL_PLUGINS, constants };
package/src/parse-html.js DELETED
@@ -1,8 +0,0 @@
1
- export const parseDegrees = (html) =>
2
- html
3
- // removes \( use case: 50°
4
- .replace(/\\[(]/g, '')
5
- // removes \) use case: 50°+m<1
6
- .replace(/\\[)]/g, '')
7
- // removes \degree use case: 50°
8
- .replace(/\\degree/g, '&deg;');
@@ -1,27 +0,0 @@
1
- # plugins
2
-
3
- ## Custom toolbar
4
-
5
- To create a custom toolbar you need to add the following methods to the toolbar object:
6
-
7
- ```typescript
8
- type ChangeFn = (key:string, update : object) : void;
9
-
10
- type Toolbar = {
11
- /**
12
- * return true if this plugin supports this node type
13
- */
14
- supports : (node: Slate.Node) : Boolean;
15
- /**
16
- * return a React component to edit the data within the node,
17
- * call toolbarDone to finish editing, call toolbarChange to update
18
- * the main editor without closing editing.
19
- */
20
- customToolbar: (node: Slate.Node, toolbarDone : ChangeFn, toolbarChange: ChangeFn),
21
- /**
22
- * Takes the output of the `customToolbar#toolbarDone` call
23
- * and passes in a value so that you can create a Slate.Change.
24
- */
25
- applyChange: (key: string, data: object, value: Slate.Value) : Slate.Change | undefined
26
- }
27
- ```
@@ -1,284 +0,0 @@
1
- import React from 'react';
2
- import ReactDOM from 'react-dom';
3
- import debug from 'debug';
4
- import get from 'lodash/get';
5
-
6
- import { PureToolbar } from '@pie-lib/math-toolbar';
7
-
8
- import CustomPopper from './custom-popper';
9
- import { insertSnackBar } from '../respArea/utils';
10
- import { characterIcons, spanishConfig, specialConfig } from './utils';
11
- import PropTypes from 'prop-types';
12
-
13
- const log = debug('@pie-lib:editable-html:plugins:characters');
14
-
15
- const removePopOvers = () => {
16
- const prevPopOvers = document.querySelectorAll('#mouse-over-popover');
17
-
18
- log('[characters:removePopOvers]');
19
- prevPopOvers.forEach((s) => s.remove());
20
- };
21
-
22
- export const removeDialogs = () => {
23
- const prevDialogs = document.querySelectorAll('.insert-character-dialog');
24
-
25
- log('[characters:removeDialogs]');
26
- prevDialogs.forEach((s) => s.remove());
27
- removePopOvers();
28
- };
29
-
30
- const insertDialog = ({ editorDOM, value, callback, opts }) => {
31
- const newEl = document.createElement('div');
32
-
33
- log('[characters:insertDialog]');
34
-
35
- removeDialogs();
36
-
37
- newEl.className = 'insert-character-dialog';
38
-
39
- let configToUse;
40
-
41
- switch (true) {
42
- case opts.language === 'spanish':
43
- configToUse = spanishConfig;
44
- break;
45
- case opts.language === 'special':
46
- configToUse = specialConfig;
47
- break;
48
- default:
49
- configToUse = opts;
50
- }
51
-
52
- if (!configToUse.characters) {
53
- insertSnackBar('No characters provided or language not recognized');
54
- return;
55
- }
56
-
57
- const layoutForCharacters = configToUse.characters.reduce(
58
- (obj, arr) => {
59
- if (arr.length >= obj.columns) {
60
- obj.columns = arr.length;
61
- }
62
-
63
- return obj;
64
- },
65
- { rows: configToUse.characters.length, columns: 0 },
66
- );
67
-
68
- let popoverEl;
69
-
70
- const closePopOver = () => {
71
- if (popoverEl) {
72
- popoverEl.remove();
73
- }
74
-
75
- removePopOvers();
76
- };
77
-
78
- const renderPopOver = (event, el) => {
79
- if (!event) {
80
- return;
81
- }
82
-
83
- const infoStyle = { fontSize: '20px', lineHeight: '20px' };
84
-
85
- closePopOver();
86
-
87
- popoverEl = document.createElement('div');
88
- ReactDOM.render(
89
- <CustomPopper onClose={closePopOver} anchorEl={event.currentTarget}>
90
- <div>{el.label}</div>
91
-
92
- <div style={infoStyle}>{el.description}</div>
93
-
94
- <div style={infoStyle}>{el.unicode}</div>
95
- </CustomPopper>,
96
- popoverEl,
97
- );
98
-
99
- document.body.appendChild(newEl);
100
- };
101
-
102
- let firstCallMade = false;
103
-
104
- const listener = (e) => {
105
- // this will be triggered right after setting it because
106
- // this toolbar is added on the mousedown event
107
- // so right after mouseup, the click will be triggered
108
- if (firstCallMade) {
109
- const focusIsInModals = newEl.contains(e.target) || (popoverEl && popoverEl.contains(e.target));
110
- const focusIsInEditor = editorDOM.contains(e.target);
111
-
112
- if (!(focusIsInModals || focusIsInEditor)) {
113
- handleClose();
114
- }
115
- } else {
116
- firstCallMade = true;
117
- }
118
- };
119
-
120
- const handleClose = () => {
121
- callback(undefined, true);
122
- newEl.remove();
123
- closePopOver();
124
- document.body.removeEventListener('click', listener);
125
- };
126
-
127
- const handleChange = (val) => {
128
- if (typeof val === 'string') {
129
- callback(val, true);
130
- }
131
- };
132
-
133
- const el = (
134
- <PureToolbar
135
- keyPadCharacterRef={opts.keyPadCharacterRef}
136
- setKeypadInteraction={opts.setKeypadInteraction}
137
- autoFocus
138
- noDecimal
139
- hideInput
140
- noLatexHandling
141
- hideDoneButtonBackground
142
- layoutForKeyPad={layoutForCharacters}
143
- additionalKeys={configToUse.characters.reduce((arr, n) => {
144
- arr = [
145
- ...arr,
146
- ...n.map((k) => ({
147
- name: get(k, 'name') || k,
148
- write: get(k, 'write') || k,
149
- label: get(k, 'label') || k,
150
- category: 'character',
151
- extraClass: 'character',
152
- extraProps: {
153
- ...(k.extraProps || {}),
154
- style: {
155
- ...(k.extraProps || {}).style,
156
- border: '1px solid #000',
157
- },
158
- },
159
- ...(configToUse.hasPreview
160
- ? {
161
- actions: { onMouseEnter: (ev) => renderPopOver(ev, k), onMouseLeave: closePopOver },
162
- }
163
- : {}),
164
- })),
165
- ];
166
-
167
- return arr;
168
- }, [])}
169
- keypadMode="language"
170
- onChange={handleChange}
171
- onDone={handleClose}
172
- />
173
- );
174
-
175
- ReactDOM.render(el, newEl, () => {
176
- const cursorItem = document.querySelector(`[data-key="${value.anchorKey}"]`);
177
-
178
- if (cursorItem) {
179
- const bodyRect = document.body.getBoundingClientRect();
180
- const boundRect = cursorItem.getBoundingClientRect();
181
-
182
- document.body.appendChild(newEl);
183
-
184
- // when height of toolbar exceeds screen - can happen in scrollable contexts
185
- let additionalTopOffset = 0;
186
- if (boundRect.y < newEl.offsetHeight) {
187
- additionalTopOffset = newEl.offsetHeight - boundRect.y + 10;
188
- }
189
-
190
- newEl.style.maxWidth = '500px';
191
- newEl.style.position = 'absolute';
192
- newEl.style.top = `${boundRect.top + Math.abs(bodyRect.top) - newEl.offsetHeight - 10 + additionalTopOffset}px`;
193
- newEl.style.zIndex = 99999;
194
-
195
- const leftValue = `${boundRect.left + Math.abs(bodyRect.left) + cursorItem.offsetWidth + 10}px`;
196
-
197
- const rightValue = `${boundRect.x}px`;
198
-
199
- newEl.style.left = leftValue;
200
-
201
- const leftAlignedWidth = newEl.offsetWidth;
202
-
203
- newEl.style.left = 'unset';
204
- newEl.style.right = rightValue;
205
-
206
- const rightAlignedWidth = newEl.offsetWidth;
207
-
208
- newEl.style.left = 'unset';
209
- newEl.style.right = 'unset';
210
-
211
- if (leftAlignedWidth >= rightAlignedWidth) {
212
- newEl.style.left = leftValue;
213
- } else {
214
- newEl.style.right = rightValue;
215
- }
216
-
217
- document.body.addEventListener('click', listener);
218
- }
219
- });
220
- };
221
-
222
- const CharacterIcon = ({ letter }) => (
223
- <div
224
- style={{
225
- fontSize: '24px',
226
- lineHeight: '24px',
227
- }}
228
- >
229
- {letter}
230
- </div>
231
- );
232
-
233
- CharacterIcon.propTypes = {
234
- letter: PropTypes.string,
235
- };
236
-
237
- export default function CharactersPlugin(opts) {
238
- removeDialogs();
239
-
240
- return {
241
- name: 'characters',
242
- toolbar: {
243
- icon: <CharacterIcon letter={opts.characterIcon || characterIcons[opts.language] || 'ñ'} />,
244
- ariaLabel: `${opts.language} characters Toolbar`,
245
- onClick: (value, onChange, getFocusedValue) => {
246
- const editorDOM = document.querySelector(`[data-key="${value.document.key}"]`);
247
- let valueToUse = value;
248
-
249
- const callback = (char, focus) => {
250
- if (getFocusedValue) {
251
- valueToUse = getFocusedValue() || valueToUse;
252
- }
253
-
254
- if (char) {
255
- const change = valueToUse.change().insertTextByKey(valueToUse.anchorKey, valueToUse.anchorOffset, char);
256
-
257
- valueToUse = change.value;
258
- log('[characters:insert]: ', value);
259
- onChange(change);
260
- }
261
-
262
- log('[characters:click]');
263
-
264
- if (focus) {
265
- if (editorDOM) {
266
- editorDOM.focus();
267
- }
268
- }
269
- };
270
-
271
- insertDialog({ editorDOM, value: valueToUse, callback, opts });
272
- },
273
- },
274
-
275
- pluginStyles: (node, parentNode, p) => {
276
- if (p) {
277
- return {
278
- position: 'absolute',
279
- top: 'initial',
280
- };
281
- }
282
- },
283
- };
284
- }