@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
package/src/index.jsx CHANGED
@@ -1,1442 +1,5 @@
1
- import React, { useEffect, useMemo, useRef, useState } from 'react';
2
- import ReactDOM from 'react-dom';
3
- import { createRoot } from 'react-dom/client';
4
- import { NodeSelection } from 'prosemirror-state';
5
- import { TextStyleKit } from '@tiptap/extension-text-style';
6
- import { EditorContent, useEditor, useEditorState } from '@tiptap/react';
7
- import StarterKit from '@tiptap/starter-kit';
8
- import { Table } from '@tiptap/extension-table';
9
- import { TableRow } from '@tiptap/extension-table-row';
10
- import { TableCell } from '@tiptap/extension-table-cell';
11
- import { TableHeader } from '@tiptap/extension-table-header';
12
- import SuperScript from '@tiptap/extension-superscript';
13
- import SubScript from '@tiptap/extension-subscript';
14
- import TextAlign from '@tiptap/extension-text-align';
15
- import Image from '@tiptap/extension-image';
16
-
17
- import { withStyles } from '@material-ui/core/styles';
18
- import {
19
- ExplicitConstructedResponseNode,
20
- DragInTheBlankNode,
21
- InlineDropdownNode,
22
- ResponseAreaExtension,
23
- } from './extensions/responseArea';
24
- import { MathNode } from './extensions/math';
25
- import classNames from 'classnames';
26
- import { color } from '@pie-lib/render-ui';
27
- import { primary } from './theme';
28
- import { PIE_TOOLBAR__CLASS } from './constants';
29
- import { DoneButton } from './plugins/toolbar/done-button';
30
- import { buildPlugins, DEFAULT_PLUGINS } from "./plugins/index";
31
- import Bold from '@material-ui/icons/FormatBold';
32
- import Italic from '@material-ui/icons/FormatItalic';
33
- import Strikethrough from '@material-ui/icons/FormatStrikethrough';
34
- import Code from '@material-ui/icons/Code';
35
- import GridOn from '@material-ui/icons/GridOn';
36
- import BulletedListIcon from '@material-ui/icons/FormatListBulleted';
37
- import NumberedListIcon from '@material-ui/icons/FormatListNumbered';
38
- import Underline from '@material-ui/icons/FormatUnderlined';
39
- import Functions from '@material-ui/icons/Functions';
40
- import ImageIcon from '@material-ui/icons/Image';
41
- import { ToolbarIcon } from './plugins/respArea/icons';
42
- import Redo from '@material-ui/icons/Redo';
43
- import Undo from '@material-ui/icons/Undo';
44
- import TheatersIcon from '@material-ui/icons/Theaters';
45
- import VolumeUpIcon from '@material-ui/icons/VolumeUp';
46
- import { characterIcons, spanishConfig, specialConfig } from './plugins/characters/utils';
47
- import PropTypes from 'prop-types';
48
- import { MathToolbar, PureToolbar } from '@pie-lib/math-toolbar';
49
- import get from 'lodash/get';
50
- import CustomPopper from './plugins/characters/custom-popper';
51
- import TextAlignIcon from './plugins/textAlign/icons';
52
- import CSSIcon from './plugins/css/icons';
53
-
54
- import { ImageUploadNode } from './extensions/image';
55
- import { Media } from './extensions/media';
56
- import { CSSMark } from './extensions/css';
57
- import { AddColumn, AddRow, RemoveColumn, RemoveRow, RemoveTable } from './plugins/table/icons';
58
- import BorderAll from '@material-ui/icons/BorderAll';
59
-
60
- const CharacterIcon = ({ letter }) => (
61
- <div
62
- style={{
63
- fontSize: '24px',
64
- lineHeight: '24px',
65
- }}
66
- >
67
- {letter}
68
- </div>
69
- );
70
-
71
- CharacterIcon.propTypes = {
72
- letter: PropTypes.string,
73
- };
74
-
75
- export function CharacterPicker({ editor, opts, onClose }) {
76
- if (!opts?.characters?.length) {
77
- return null;
78
- }
79
-
80
- const containerRef = useRef(null);
81
- const [position, setPosition] = useState({ top: 0, left: 0 });
82
- const [popover, setPopover] = useState(null);
83
-
84
- let configToUse;
85
-
86
- switch (true) {
87
- case opts.language === 'spanish':
88
- configToUse = spanishConfig;
89
- break;
90
- case opts.language === 'special':
91
- configToUse = specialConfig;
92
- break;
93
- default:
94
- configToUse = opts;
95
- }
96
-
97
- const layoutForCharacters = configToUse.characters.reduce(
98
- (obj, arr) => {
99
- if (arr.length >= obj.columns) {
100
- obj.columns = arr.length;
101
- }
102
-
103
- return obj;
104
- },
105
- { rows: configToUse.characters.length, columns: 0 },
106
- );
107
-
108
- useEffect(() => {
109
- return () => {
110
- closePopOver();
111
- };
112
- }, []);
113
-
114
- useEffect(() => {
115
- if (!editor) return;
116
-
117
- // Calculate position relative to selection
118
- const bodyRect = document.body.getBoundingClientRect();
119
- const { from } = editor.state.selection;
120
- const start = editor.view.coordsAtPos(from);
121
- setPosition({
122
- top: start.top + Math.abs(bodyRect.top) + 40, // shift above
123
- left: start.left,
124
- });
125
-
126
- const handleClickOutside = (e) => {
127
- if (containerRef.current && !containerRef.current.contains(e.target) && !editor.view.dom.contains(e.target)) {
128
- onClose();
129
- }
130
- };
131
-
132
- document.addEventListener('mousedown', handleClickOutside);
133
- return () => document.removeEventListener('mousedown', handleClickOutside);
134
- }, [editor]);
135
-
136
- const renderPopOver = (event, el) => setPopover({ anchorEl: event.currentTarget, el });
137
- const closePopOver = () => setPopover(null);
138
-
139
- const handleChange = (val) => {
140
- if (typeof val === 'string') {
141
- editor
142
- .chain()
143
- .focus()
144
- .insertContent(val)
145
- .run();
146
- }
147
- };
148
-
149
- return (
150
- <React.Fragment>
151
- {ReactDOM.createPortal(
152
- <div
153
- ref={containerRef}
154
- className="insert-character-dialog"
155
- style={{
156
- position: 'absolute',
157
- top: `${position.top}px`,
158
- left: `${position.left}px`,
159
- maxWidth: '500px',
160
- }}
161
- >
162
- <div>
163
- <PureToolbar
164
- keyPadCharacterRef={opts.keyPadCharacterRef}
165
- setKeypadInteraction={opts.setKeypadInteraction}
166
- autoFocus
167
- noDecimal
168
- hideInput
169
- noLatexHandling
170
- hideDoneButtonBackground
171
- layoutForKeyPad={layoutForCharacters}
172
- additionalKeys={configToUse.characters.reduce((arr, n) => {
173
- arr = [
174
- ...arr,
175
- ...n.map((k) => ({
176
- name: get(k, 'name') || k,
177
- write: get(k, 'write') || k,
178
- label: get(k, 'label') || k,
179
- category: 'character',
180
- extraClass: 'character',
181
- extraProps: {
182
- ...(k.extraProps || {}),
183
- style: {
184
- ...(k.extraProps || {}).style,
185
- border: '1px solid #000',
186
- },
187
- },
188
- ...(configToUse.hasPreview
189
- ? {
190
- actions: { onMouseEnter: (ev) => renderPopOver(ev, k), onMouseLeave: closePopOver },
191
- }
192
- : {}),
193
- })),
194
- ];
195
-
196
- return arr;
197
- }, [])}
198
- keypadMode="language"
199
- onChange={handleChange}
200
- onDone={onClose}
201
- />
202
- </div>
203
- </div>,
204
- document.body,
205
- )}
206
- {popover &&
207
- ReactDOM.createPortal(
208
- <CustomPopper onClose={closePopOver} anchorEl={popover.anchorEl}>
209
- <div>{popover.el.label}</div>
210
- <div style={{ fontSize: 20, lineHeight: '20px' }}>{popover.el.description}</div>
211
- <div style={{ fontSize: 20, lineHeight: '20px' }}>{popover.el.unicode}</div>
212
- </CustomPopper>,
213
- document.body,
214
- )}
215
- </React.Fragment>
216
- );
217
- }
218
-
219
- const SuperscriptIcon = () => (
220
- <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="none">
221
- <path
222
- d="M22,7h-2v1h3v1h-4V7c0-0.55,0.45-1,1-1h2V5h-3V4h3c0.55,0,1,0.45,1,1v1C23,6.55,22.55,7,22,7z M5.88,20h2.66l3.4-5.42h0.12 l3.4,5.42h2.66l-4.65-7.27L17.81,6h-2.68l-3.07,4.99h-0.12L8.85,6H6.19l4.32,6.73L5.88,20z"
223
- fill="currentColor"
224
- />
225
- </svg>
226
- );
227
-
228
- const SubscriptIcon = () => (
229
- <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="none">
230
- <path
231
- d="M22,18h-2v1h3v1h-4v-2c0-0.55,0.45-1,1-1h2v-1h-3v-1h3c0.55,0,1,0.45,1,1v1C23,17.55,22.55,18,22,18z M5.88,18h2.66 l3.4-5.42h0.12l3.4,5.42h2.66l-4.65-7.27L17.81,4h-2.68l-3.07,4.99h-0.12L8.85,4H6.19l4.32,6.73L5.88,18z"
232
- fill="currentColor"
233
- />
234
- </svg>
235
- );
236
-
237
- const HeadingIcon = () => (
238
- <svg
239
- width="30"
240
- height="28"
241
- viewBox="0 0 30 28"
242
- fill="none"
243
- xmlns="http://www.w3.org/2000/svg"
244
- style={{ width: '20px', height: '18px' }}
245
- >
246
- <path
247
- d="M27 4V24H29C29.5 24 30 24.5 30 25V27C30 27.5625 29.5 28 29 28H19C18.4375 28 18 27.5625 18 27V25C18 24.5 18.4375 24 19 24H21V16H9V24H11C11.5 24 12 24.5 12 25V27C12 27.5625 11.5 28 11 28H1C0.4375 28 0 27.5625 0 27V25C0 24.5 0.4375 24 1 24H3V4H1C0.4375 4 0 3.5625 0 3V1C0 0.5 0.4375 0 1 0H11C11.5 0 12 0.5 12 1V3C12 3.5625 11.5 4 11 4H9V12H21V4H19C18.4375 4 18 3.5625 18 3V1C18 0.5 18.4375 0 19 0H29C29.5 0 30 0.5 30 1V3C30 3.5625 29.5 4 29 4H27Z"
248
- fill="currentColor"
249
- />
250
- </svg>
251
- );
252
-
253
- const ExtendedTable = Table.extend({
254
- addAttributes() {
255
- return {
256
- border: { default: '1' },
257
- };
258
- },
259
- renderHTML(props) {
260
- const originalTable = this.parent(props);
261
- const { border } = props.HTMLAttributes;
262
-
263
- const previousStyle = `${originalTable[1].style}${originalTable[1].style.match(/.*; */) ? '' : ';'}`;
264
-
265
- originalTable[1].style = `${previousStyle}
266
- width: 100%;
267
- color: var(--pie-text, black);
268
- table-layout: fixed;
269
- border-collapse: collapse;
270
- background-color: var(--pie-background, rgba(255, 255, 255))`;
271
- originalTable[1].border = border ? border : '1';
272
-
273
- return originalTable;
274
- },
275
- });
276
-
277
- const styles = (theme) => ({
278
- root: {
279
- position: 'relative',
280
- padding: '0px',
281
- border: '1px solid #ccc',
282
- borderRadius: '4px',
283
- cursor: 'text',
284
- '& [data-slate-editor="true"]': {
285
- wordBreak: 'break-word',
286
- overflow: 'visible',
287
- maxHeight: '500px',
288
- // needed in order to be able to put the focus before a void element when it is the first one in the editor
289
- padding: '5px',
290
- },
291
-
292
- '&:first-child': {
293
- marginTop: 0,
294
- },
295
-
296
- '& ul, & ol': {
297
- padding: '0 1rem',
298
- margin: '1.25rem 1rem 1.25rem 0.4rem',
299
- },
300
-
301
- '& ul li p, & ol li p': {
302
- marginTop: '0.25em',
303
- marginBottom: '0.25em',
304
- },
305
-
306
- '& h1, & h2, & h3, & h4, & h5, & h6': {
307
- lineHeight: 1.1,
308
- marginTop: '2.5rem',
309
- textWrap: 'pretty',
310
- },
311
-
312
- '& h1, & h2': {
313
- marginTop: '3.5rem',
314
- marginBottom: '1.5rem',
315
- },
316
-
317
- '& h1': {
318
- fontSize: '1.4rem',
319
- },
320
-
321
- '& h2': {
322
- fontSize: '1.2rem',
323
- },
324
-
325
- '& h3': {
326
- fontSize: '1.1rem',
327
- },
328
-
329
- '& h4, & h5, & h6': {
330
- fontSize: '1rem',
331
- },
332
-
333
- '& code': {
334
- backgroundColor: 'var(--purple-light)',
335
- borderRadius: '0.4rem',
336
- color: 'var(--black)',
337
- fontSize: '0.85rem',
338
- padding: '0.25em 0.3em',
339
- },
340
-
341
- '& pre': {
342
- background: 'var(--black)',
343
- borderRadius: '0.5rem',
344
- color: 'var(--white)',
345
- fontFamily: "'JetBrainsMono', monospace",
346
- margin: '1.5rem 0',
347
- padding: '0.75rem 1rem',
348
-
349
- '& code': {
350
- background: 'none',
351
- color: 'inherit',
352
- fontSize: '0.8rem',
353
- padding: 0,
354
- },
355
- },
356
-
357
- '& blockquote': {
358
- borderLeft: '3px solid var(--gray-3)',
359
- margin: '1.5rem 0',
360
- paddingLeft: '1rem',
361
- },
362
-
363
- '& hr': {
364
- border: 'none',
365
- borderTop: '1px solid var(--gray-2)',
366
- margin: '2rem 0',
367
- },
368
-
369
- '& table': {
370
- tableLayout: 'fixed',
371
- width: '100%',
372
- borderCollapse: 'collapse',
373
- color: color.text(),
374
- backgroundColor: color.background(),
375
- },
376
- '& table:not([border="1"]) tr': {
377
- borderTop: '1px solid #dfe2e5',
378
- },
379
- '& td, th': {
380
- padding: '.6em 1em',
381
- textAlign: 'center',
382
- },
383
- '& table:not([border="1"]) td, th': {
384
- border: '1px solid #dfe2e5',
385
- },
386
- },
387
- children: {
388
- padding: '10px 16px',
389
- },
390
- editorHolder: {
391
- position: 'relative',
392
- padding: '0px',
393
- // overflowY: 'auto',
394
- overflow: 'visible',
395
- color: color.text(),
396
- backgroundColor: color.background(),
397
- '&::before': {
398
- left: '0',
399
- right: '0',
400
- bottom: '0',
401
- height: '1px',
402
- content: '""',
403
- position: 'absolute',
404
- transition: 'background-color 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
405
- pointerEvents: 'none',
406
- backgroundColor: 'rgba(0, 0, 0, 0.42)',
407
- },
408
- '&::after': {
409
- left: '0',
410
- right: '0',
411
- bottom: '0',
412
- height: '1px',
413
- content: '""',
414
- position: 'absolute',
415
- transform: 'scaleX(0)',
416
- transition: 'transform 200ms cubic-bezier(0.0, 0.0, 0.2, 1) 0ms, background-color 200ms linear',
417
- backgroundColor: 'rgba(0, 0, 0, 0.42)',
418
- },
419
- '&:focus': {
420
- '&::after': {
421
- transform: 'scaleX(1)',
422
- backgroundColor: primary,
423
- height: '2px',
424
- },
425
- },
426
- '&:hover': {
427
- '&::after': {
428
- transform: 'scaleX(1)',
429
- backgroundColor: theme.palette.common.black,
430
- height: '2px',
431
- },
432
- },
433
- },
434
- disabledUnderline: {
435
- '&::before': {
436
- display: 'none',
437
- },
438
- '&::after': {
439
- display: 'none',
440
- },
441
- },
442
- disabledScrollbar: {
443
- '&::-webkit-scrollbar': {
444
- display: 'none',
445
- },
446
- scrollbarWidth: 'none',
447
- '-ms-overflow-style': 'none',
448
- },
449
- readOnly: {
450
- '&::before': {
451
- background: 'transparent',
452
- backgroundSize: '5px 1px',
453
- backgroundImage: 'linear-gradient(to right, rgba(0, 0, 0, 0.42) 33%, transparent 0%)',
454
- backgroundRepeat: 'repeat-x',
455
- backgroundPosition: 'left top',
456
- },
457
- '&::after': {
458
- left: '0',
459
- right: '0',
460
- bottom: '0',
461
- height: '1px',
462
- content: '""',
463
- position: 'absolute',
464
- transform: 'scaleX(0)',
465
- transition: 'transform 200ms cubic-bezier(0.0, 0.0, 0.2, 1) 0ms, background-color 0ms linear',
466
- backgroundColor: 'rgba(0, 0, 0, 0)',
467
- },
468
- '&:hover': {
469
- '&::after': {
470
- transform: 'scaleX(0)',
471
- backgroundColor: theme.palette.common.black,
472
- height: '2px',
473
- },
474
- },
475
- },
476
- editorInFocus: {
477
- '&::after': {
478
- transform: 'scaleX(1)',
479
- backgroundColor: primary,
480
- height: '2px',
481
- },
482
- '&:hover': {
483
- '&::after': {
484
- backgroundColor: primary,
485
- },
486
- },
487
- },
488
- error: {
489
- border: `2px solid ${theme.palette.error.main} !important`,
490
- },
491
- noBorder: {
492
- border: 'none',
493
- },
494
- noPadding: {
495
- padding: 0,
496
- },
497
- toolbarOnTop: {
498
- marginTop: '45px',
499
- },
500
- });
501
-
502
- const defaultResponseAreaProps = {
503
- options: {},
504
- respAreaToolbar: () => {},
505
- onHandleAreaChange: () => {},
506
- };
507
-
508
- const valueToSize = (v) => {
509
- if (!v) {
510
- return;
511
- }
512
- const calcRegex = /^calc\((.*)\)$/;
513
-
514
- if (typeof v === 'string') {
515
- if (v.endsWith('%')) {
516
- return undefined;
517
- } else if (
518
- v.endsWith('px') ||
519
- v.endsWith('vh') ||
520
- v.endsWith('vw') ||
521
- v.endsWith('ch') ||
522
- v.endsWith('em') ||
523
- v.match(calcRegex)
524
- ) {
525
- return v;
526
- } else {
527
- const value = parseInt(v, 10);
528
- return isNaN(value) ? value : `${value}px`;
529
- }
530
- }
531
- if (typeof v === 'number') {
532
- return `${v}px`;
533
- }
534
- };
535
-
536
- function TiptapContainer(props) {
537
- const {
538
- editor,
539
- editorState,
540
- classes,
541
- children,
542
- disableUnderline,
543
- disableScrollbar,
544
- activePlugins,
545
- toolbarOpts,
546
- responseAreaProps,
547
- autoFocus,
548
- minWidth,
549
- width,
550
- maxWidth,
551
- minHeight,
552
- height,
553
- maxHeight,
554
- } = props;
555
- const holderNames = classNames(classes.editorHolder, {
556
- [classes.editorInFocus]: editorState.isFocused,
557
- [classes.readOnly]: editorState.readOnly,
558
- [classes.disabledUnderline]: disableUnderline,
559
- [classes.disabledScrollbar]: disableScrollbar,
560
- });
561
-
562
- useEffect(() => {
563
- if (editor && autoFocus) {
564
- Promise.resolve().then(() => {
565
- editor.commands.focus('end');
566
- });
567
- }
568
- }, [editor, autoFocus]);
569
-
570
- const sizeStyle = useMemo(() => {
571
- return {
572
- width: valueToSize(width),
573
- minWidth: valueToSize(minWidth),
574
- maxWidth: valueToSize(maxWidth),
575
- height: valueToSize(height),
576
- minHeight: valueToSize(minHeight),
577
- maxHeight: valueToSize(maxHeight),
578
- };
579
- }, [minWidth, width, maxWidth, minHeight, height, maxHeight]);
580
-
581
- return (
582
- <div
583
- className={classNames(
584
- {
585
- [classes.noBorder]: toolbarOpts && toolbarOpts.noBorder,
586
- [classes.error]: toolbarOpts && toolbarOpts.error,
587
- },
588
- classes.root,
589
- props.className,
590
- )}
591
- style={{ width: sizeStyle.width, minWidth: sizeStyle.minWidth, maxWidth: sizeStyle.maxWidth }}
592
- >
593
- <div className={holderNames}>
594
- <div
595
- className={classNames(
596
- {
597
- [classes.noPadding]: toolbarOpts && toolbarOpts.noPadding,
598
- },
599
- classes.children,
600
- )}
601
- >
602
- {children}
603
- </div>
604
- </div>
605
-
606
- {/*{editorState.isFocused && <MenuBar editor={editor} />}*/}
607
- {/*<Toolbar editor={editor} editorState={editorState} plugins={plugins} toolbarOpts={toolbarOpts} isFocused={editorState.isFocused} />*/}
608
- {editor && (
609
- <StyledMenuBar
610
- editor={editor}
611
- responseAreaProps={responseAreaProps}
612
- toolbarOpts={toolbarOpts}
613
- activePlugins={activePlugins}
614
- onChange={props.onChange}
615
- />
616
- )}
617
- </div>
618
- );
619
- }
620
-
621
- const EditorContainer = withStyles(styles)(TiptapContainer);
622
-
623
- function MenuBar({ editor, classes, activePlugins, toolbarOpts: toolOpts, responseAreaProps, onChange }) {
624
- const [showPicker, setShowPicker] = useState(false);
625
- const toolbarOpts = toolOpts ?? {};
626
- // Read the current editor's state, and re-render the component when it changes
627
- const editorState = useEditorState({
628
- editor,
629
- selector: (ctx) => {
630
- const { selection } = ctx.editor?.state || {};
631
-
632
- let currentNode;
633
-
634
- if (selection instanceof NodeSelection) {
635
- currentNode = selection.node; // the selected node
636
- }
637
-
638
- const hideDefaultToolbar =
639
- ctx.editor?.isActive('math') ||
640
- ctx.editor?.isActive('explicit_constructed_response') ||
641
- ctx.editor?.isActive('imageUploadNode');
642
-
643
- return {
644
- currentNode,
645
- hideDefaultToolbar,
646
- isFocused: ctx.editor?.isFocused,
647
- isBold: ctx.editor.isActive('bold') ?? false,
648
- canBold:
649
- ctx.editor
650
- .can()
651
- .chain()
652
- .toggleBold()
653
- .run() ?? false,
654
- isTable: ctx.editor.isActive('table') ?? false,
655
- tableHasBorder: ctx.editor.getAttributes('table')?.border === '1' ?? false,
656
- canTable:
657
- ctx.editor
658
- .can()
659
- .chain()
660
- .insertTable()
661
- .run() ?? false,
662
- isItalic: ctx.editor.isActive('italic') ?? false,
663
- canItalic:
664
- ctx.editor
665
- .can()
666
- .chain()
667
- .toggleItalic()
668
- .run() ?? false,
669
- isStrike: ctx.editor.isActive('strike') ?? false,
670
- canStrike:
671
- ctx.editor
672
- .can()
673
- .chain()
674
- .toggleStrike()
675
- .run() ?? false,
676
- isCode: ctx.editor.isActive('code') ?? false,
677
- canCode:
678
- ctx.editor
679
- .can()
680
- .chain()
681
- .toggleCode()
682
- .run() ?? false,
683
- canClearMarks:
684
- ctx.editor
685
- .can()
686
- .chain()
687
- .unsetAllMarks()
688
- .run() ?? false,
689
- isUnderline: ctx.editor.isActive('underline') ?? false,
690
- isSubScript: ctx.editor.isActive('subscript') ?? false,
691
- isSuperScript: ctx.editor.isActive('superscript') ?? false,
692
- isParagraph: ctx.editor.isActive('paragraph') ?? false,
693
- isHeading1: ctx.editor.isActive('heading', { level: 1 }) ?? false,
694
- isHeading2: ctx.editor.isActive('heading', { level: 2 }) ?? false,
695
- isHeading3: ctx.editor.isActive('heading', { level: 3 }) ?? false,
696
- isHeading4: ctx.editor.isActive('heading', { level: 4 }) ?? false,
697
- isHeading5: ctx.editor.isActive('heading', { level: 5 }) ?? false,
698
- isHeading6: ctx.editor.isActive('heading', { level: 6 }) ?? false,
699
- isBulletList: ctx.editor.isActive('bulletList') ?? false,
700
- isOrderedList: ctx.editor.isActive('orderedList') ?? false,
701
- isCodeBlock: ctx.editor.isActive('codeBlock') ?? false,
702
- isBlockquote: ctx.editor.isActive('blockquote') ?? false,
703
- canUndo:
704
- ctx.editor
705
- .can()
706
- .chain()
707
- .undo()
708
- .run() ?? false,
709
- canRedo:
710
- ctx.editor
711
- .can()
712
- .chain()
713
- .redo()
714
- .run() ?? false,
715
- };
716
- },
717
- });
718
- const hasDoneButton = false;
719
- const autoWidth = false;
720
-
721
- const names = classNames(classes.toolbar, PIE_TOOLBAR__CLASS, {
722
- [classes.toolbarWithNoDone]: !hasDoneButton,
723
- [classes.toolbarTop]: toolbarOpts.position === 'top',
724
- [classes.toolbarRight]: toolbarOpts.alignment === 'right',
725
- [classes.focused]: toolbarOpts.alwaysVisible || (editorState.isFocused && !editor._toolbarOpened),
726
- [classes.autoWidth]: autoWidth,
727
- [classes.fullWidth]: !autoWidth,
728
- [classes.hidden]: toolbarOpts.isHidden === true,
729
- });
730
- const customStyles = toolbarOpts.minWidth !== undefined ? { minWidth: toolbarOpts.minWidth } : {};
731
- const handleMouseDown = (e) => {
732
- e.preventDefault();
733
- };
734
- const toolbarButtons = useMemo(
735
- () => [
736
- {
737
- icon: <GridOn />,
738
- onClick: (editor) =>
739
- editor
740
- .chain()
741
- .focus()
742
- .insertTable({ rows: 2, cols: 2, withHeaderRow: false })
743
- .run(),
744
- hidden: (state) => !activePlugins?.includes('table') || state.isTable,
745
- isActive: (state) => state.isTable,
746
- isDisabled: (state) => !state.canTable,
747
- },
748
- {
749
- icon: <AddRow />,
750
- onClick: (editor) =>
751
- editor
752
- .chain()
753
- .focus()
754
- .addRowAfter()
755
- .run(),
756
- hidden: (state) => !state.isTable,
757
- isActive: (state) => state.isTable,
758
- isDisabled: (state) => !state.canTable,
759
- },
760
- {
761
- icon: <RemoveRow />,
762
- onClick: (editor) =>
763
- editor
764
- .chain()
765
- .focus()
766
- .deleteRow()
767
- .run(),
768
- hidden: (state) => !state.isTable,
769
- isActive: (state) => state.isTable,
770
- isDisabled: (state) => !state.canTable,
771
- },
772
- {
773
- icon: <AddColumn />,
774
- onClick: (editor) =>
775
- editor
776
- .chain()
777
- .focus()
778
- .addColumnAfter()
779
- .run(),
780
- hidden: (state) => !state.isTable,
781
- isActive: (state) => state.isTable,
782
- isDisabled: (state) => !state.canTable,
783
- },
784
- {
785
- icon: <RemoveColumn />,
786
- onClick: (editor) =>
787
- editor
788
- .chain()
789
- .focus()
790
- .deleteColumn()
791
- .run(),
792
- hidden: (state) => !state.isTable,
793
- isActive: (state) => state.isTable,
794
- isDisabled: (state) => !state.canTable,
795
- },
796
- {
797
- icon: <RemoveTable />,
798
- onClick: (editor) =>
799
- editor
800
- .chain()
801
- .focus()
802
- .deleteTable()
803
- .run(),
804
- hidden: (state) => !state.isTable,
805
- isActive: (state) => state.isTable,
806
- isDisabled: (state) => !state.canTable,
807
- },
808
- {
809
- icon: <BorderAll />,
810
- onClick: (editor) => {
811
- const tableAttrs = editor.getAttributes('table');
812
-
813
- const update = {
814
- ...tableAttrs,
815
- border: tableAttrs.border !== '0' ? '0' : '1',
816
- };
817
-
818
- editor.commands.updateAttributes('table', update);
819
- },
820
- hidden: (state) => !state.isTable,
821
- isActive: (state) => state.tableHasBorder,
822
- isDisabled: (state) => !state.canTable,
823
- },
824
- {
825
- icon: <Bold />,
826
- onClick: (editor) =>
827
- editor
828
- .chain()
829
- .focus()
830
- .toggleBold()
831
- .run(),
832
- hidden: (state) => !activePlugins?.includes('bold') || state.isTable,
833
- isActive: (state) => state.isBold,
834
- isDisabled: (state) => !state.canBold,
835
- },
836
- {
837
- icon: <Italic />,
838
- onClick: (editor) =>
839
- editor
840
- .chain()
841
- .focus()
842
- .toggleItalic()
843
- .run(),
844
- hidden: (state) => !activePlugins?.includes('italic') || state.isTable,
845
- isActive: (state) => state.isItalic,
846
- isDisabled: (state) => !state.canItalic,
847
- },
848
- {
849
- icon: <Strikethrough />,
850
- onClick: (editor) =>
851
- editor
852
- .chain()
853
- .focus()
854
- .toggleStrike()
855
- .run(),
856
- hidden: (state) => !activePlugins?.includes('strikethrough') || state.isTable,
857
- isActive: (state) => state.isStrike,
858
- isDisabled: (state) => !state.canStrike,
859
- },
860
- {
861
- icon: <Code />,
862
- onClick: (editor) =>
863
- editor
864
- .chain()
865
- .focus()
866
- .toggleCode()
867
- .run(),
868
- hidden: (state) => !activePlugins?.includes('code') || state.isTable,
869
- isActive: (state) => state.isCode,
870
- isDisabled: (state) => !state.canCode,
871
- },
872
- {
873
- icon: <Underline />,
874
- onClick: (editor) =>
875
- editor
876
- .chain()
877
- .focus()
878
- .toggleUnderline()
879
- .run(),
880
- hidden: (state) => !activePlugins?.includes('underline') || state.isTable,
881
- isActive: (state) => state.isUnderline,
882
- },
883
- {
884
- icon: <SubscriptIcon />,
885
- onClick: (editor) =>
886
- editor
887
- .chain()
888
- .focus()
889
- .toggleSubscript()
890
- .run(),
891
- hidden: (state) => !activePlugins?.includes('subscript') || state.isTable,
892
- isActive: (state) => state.isSubScript,
893
- },
894
- {
895
- icon: <SuperscriptIcon />,
896
- onClick: (editor) =>
897
- editor
898
- .chain()
899
- .focus()
900
- .toggleSuperscript()
901
- .run(),
902
- hidden: (state) => !activePlugins?.includes('superscript') || state.isTable,
903
- isActive: (state) => state.isSuperScript,
904
- },
905
- {
906
- icon: <ImageIcon />,
907
- hidden: (state) => !activePlugins?.includes('image') || state.isTable,
908
- onClick: (editor) =>
909
- editor
910
- .chain()
911
- .focus()
912
- .setImageUploadNode()
913
- .run(),
914
- },
915
- {
916
- icon: <TheatersIcon />,
917
- hidden: (state) => !activePlugins?.includes('video') || state.isTable,
918
- onClick: (editor) =>
919
- editor
920
- .chain()
921
- .focus()
922
- .insertMedia({ tag: 'video' })
923
- .run(),
924
- },
925
- {
926
- icon: <VolumeUpIcon />,
927
- hidden: (state) => !activePlugins?.includes('audio') || state.isTable,
928
- onClick: (editor) =>
929
- editor
930
- .chain()
931
- .focus()
932
- .insertMedia({ tag: 'audio' })
933
- .run(),
934
- },
935
- {
936
- icon: <CSSIcon />,
937
- hidden: (state) => !activePlugins?.includes('css') || state.isTable,
938
- onClick: (editor) => editor.commands.openCSSClassDialog(),
939
- },
940
- {
941
- icon: <HeadingIcon />,
942
- hidden: (state) => !activePlugins?.includes('h3') || state.isTable,
943
- onClick: (editor) =>
944
- editor
945
- .chain()
946
- .focus()
947
- .toggleHeading({ level: 3 })
948
- .run(),
949
- isActive: (state) => state.isHeading3,
950
- },
951
- {
952
- icon: <Functions />,
953
- hidden: () => !activePlugins?.includes('math'),
954
- onClick: (editor) =>
955
- editor
956
- .chain()
957
- .focus()
958
- .insertMath('')
959
- .run(),
960
- },
961
- {
962
- icon: <CharacterIcon letter="ñ" />,
963
- hidden: (state) => !activePlugins?.includes('languageCharacters') || state.isTable,
964
- onClick: () => setShowPicker(spanishConfig),
965
- },
966
- {
967
- icon: <CharacterIcon letter="€" />,
968
- hidden: (state) => activePlugins?.filter(p => p === 'languageCharacters').length !== 2 || state.isTable,
969
- onClick: () => setShowPicker(specialConfig),
970
- },
971
- {
972
- icon: <TextAlignIcon editor={editor} />,
973
- hidden: (state) => !activePlugins?.includes('text-align') || state.isTable,
974
- onClick: () => {},
975
- },
976
- {
977
- icon: <BulletedListIcon />,
978
- hidden: (state) => !activePlugins?.includes('bulleted-list') || state.isTable,
979
- onClick: (editor) =>
980
- editor
981
- .chain()
982
- .focus()
983
- .toggleBulletList()
984
- .run(),
985
- isActive: (state) => state.isBulletList,
986
- },
987
- {
988
- icon: <NumberedListIcon />,
989
- hidden: (state) => !activePlugins?.includes('numbered-list') || state.isTable,
990
- onClick: (editor) =>
991
- editor
992
- .chain()
993
- .focus()
994
- .toggleOrderedList()
995
- .run(),
996
- isActive: (state) => state.isOrderedList,
997
- },
998
- {
999
- icon: <Undo />,
1000
- hidden: (state) => !activePlugins?.includes('undo') || state.isTable,
1001
- onClick: (editor) =>
1002
- editor
1003
- .chain()
1004
- .focus()
1005
- .undo()
1006
- .run(),
1007
- isDisabled: (state) => !state.canUndo,
1008
- },
1009
- {
1010
- icon: <Redo />,
1011
- hidden: (state) => !activePlugins?.includes('redo') || state.isTable,
1012
- onClick: (editor) =>
1013
- editor
1014
- .chain()
1015
- .focus()
1016
- .redo()
1017
- .run(),
1018
- isDisabled: (state) => !state.canRedo,
1019
- },
1020
- ],
1021
- [activePlugins, editor, spanishConfig, specialConfig, setShowPicker],
1022
- );
1023
-
1024
- return (
1025
- <div className={names} style={{ ...customStyles }} onMouseDown={handleMouseDown}>
1026
- {!editorState.hideDefaultToolbar && (
1027
- <div className={classes.defaultToolbar} tabIndex="1">
1028
- <div className={classes.buttonsContainer}>
1029
- {toolbarButtons
1030
- .filter((btn) => !btn.hidden?.(editorState))
1031
- .map((btn, index) => {
1032
- const disabled = btn.isDisabled?.(editorState);
1033
- const active = btn.isActive?.(editorState);
1034
-
1035
- return (
1036
- <button
1037
- key={index}
1038
- disabled={disabled}
1039
- onClick={(e) => {
1040
- e.preventDefault();
1041
- btn.onClick(editor);
1042
- }}
1043
- className={classNames(classes.button, { [classes.active]: active })}
1044
- >
1045
- {btn.icon}
1046
- </button>
1047
- );
1048
- })}
1049
- </div>
1050
- {activePlugins?.includes('responseArea') && (
1051
- <button
1052
- onClick={() => {
1053
- editor
1054
- .chain()
1055
- .focus()
1056
- .insertResponseArea(responseAreaProps.type)
1057
- .run();
1058
- }}
1059
- className={classes.button}
1060
- >
1061
- <ToolbarIcon />
1062
- </button>
1063
- )}
1064
-
1065
- <DoneButton
1066
- onClick={() => {
1067
- onChange?.(editor.getHTML());
1068
- editor.commands.blur();
1069
- }}
1070
- />
1071
- </div>
1072
- )}
1073
- {showPicker && (
1074
- <CharacterPicker
1075
- editor={editor}
1076
- opts={{
1077
- ...showPicker,
1078
- renderPopOver: (ev, ch) => console.log('Show popover', ch),
1079
- closePopOver: () => console.log('Close popover'),
1080
- }}
1081
- onClose={() => setShowPicker(false)}
1082
- />
1083
- )}
1084
- </div>
1085
- );
1086
- }
1087
-
1088
- const style = (theme) => ({
1089
- defaultToolbar: {
1090
- display: 'flex',
1091
- width: '100%',
1092
- justifyContent: 'space-between',
1093
- },
1094
- buttonsContainer: {
1095
- alignItems: 'center',
1096
- display: 'flex',
1097
- width: '100%',
1098
- },
1099
- button: {
1100
- color: 'grey',
1101
- display: 'inline-flex',
1102
- padding: '2px',
1103
- background: 'none',
1104
- border: 'none',
1105
- cursor: 'pointer',
1106
- '&:hover': {
1107
- color: 'black',
1108
- },
1109
- '&:focus': {
1110
- outline: `2px solid ${theme.palette.grey[700]}`,
1111
- },
1112
- },
1113
- active: {
1114
- color: 'black',
1115
- },
1116
- disabled: {
1117
- opacity: 0.7,
1118
- cursor: 'not-allowed',
1119
- '& :hover': {
1120
- color: 'grey',
1121
- },
1122
- },
1123
- isActive: {
1124
- background: 'var(--purple)',
1125
- color: 'var(--white)',
1126
- },
1127
- toolbar: {
1128
- position: 'absolute',
1129
- zIndex: 20,
1130
- cursor: 'pointer',
1131
- justifyContent: 'space-between',
1132
- background: 'var(--editable-html-toolbar-bg, #efefef)',
1133
- minWidth: '280px',
1134
- margin: '5px 0 0 0',
1135
- padding: '2px',
1136
- boxShadow:
1137
- '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)',
1138
- boxSizing: 'border-box',
1139
- display: 'flex',
1140
- opacity: 0,
1141
- pointerEvents: 'none',
1142
- },
1143
- toolbarWithNoDone: {
1144
- minWidth: '265px',
1145
- },
1146
- toolbarTop: {
1147
- top: '-45px',
1148
- },
1149
- toolbarRight: {
1150
- right: 0,
1151
- },
1152
- fullWidth: {
1153
- width: '100%',
1154
- },
1155
- hidden: {
1156
- visibility: 'hidden',
1157
- },
1158
- autoWidth: {
1159
- width: 'auto',
1160
- },
1161
- focused: {
1162
- opacity: 1,
1163
- pointerEvents: 'auto',
1164
- },
1165
- iconRoot: {
1166
- width: '28px',
1167
- height: '28px',
1168
- padding: '4px',
1169
- verticalAlign: 'top',
1170
- },
1171
- label: {
1172
- color: 'var(--editable-html-toolbar-check, #00bb00)',
1173
- },
1174
- shared: {
1175
- display: 'flex',
1176
- },
1177
- });
1178
- const StyledMenuBar = withStyles(style, { index: 1000 })(MenuBar);
1179
-
1180
- const defaultToolbarOpts = {
1181
- position: 'bottom',
1182
- alignment: 'left',
1183
- alwaysVisible: false,
1184
- showDone: true,
1185
- doneOn: 'blur',
1186
- };
1187
-
1188
- export const EditableHtml = (props) => {
1189
- const [pendingImages, setPendingImages] = useState([]);
1190
- const [scheduled, setScheduled] = useState(false);
1191
- const { classes, toolbarOpts } = props;
1192
- const toolbarOptsToUse = {
1193
- ...defaultToolbarOpts,
1194
- ...toolbarOpts,
1195
- };
1196
- const activePluginsToUse = useMemo(() => {
1197
- let { customPlugins } = props.pluginProps || {};
1198
-
1199
- customPlugins = customPlugins || [];
1200
-
1201
- return buildPlugins(props.activePlugins, customPlugins, {
1202
- math: {},
1203
- textAlign: {},
1204
- html: {},
1205
- extraCSSRules: props.extraCSSRules || {},
1206
- image: {},
1207
- toolbar: {},
1208
- table: {},
1209
- responseArea: {},
1210
- languageCharacters: props.languageCharactersProps,
1211
- keyPadCharacterRef: {},
1212
- setKeypadInteraction: {},
1213
- media: {},
1214
- });
1215
- }, [props]);
1216
- const extensions = [
1217
- TextStyleKit,
1218
- StarterKit,
1219
- ExtendedTable,
1220
- TableRow,
1221
- TableHeader,
1222
- TableCell,
1223
- ResponseAreaExtension,
1224
- ExplicitConstructedResponseNode.configure(props.responseAreaProps),
1225
- DragInTheBlankNode.configure(props.responseAreaProps),
1226
- InlineDropdownNode.configure(props.responseAreaProps),
1227
- MathNode.configure({
1228
- toolbarOpts: toolbarOptsToUse,
1229
- }),
1230
- SubScript,
1231
- SuperScript,
1232
- TextAlign.configure({
1233
- types: ['heading', 'paragraph'],
1234
- alignments: ['left', 'right', 'center'],
1235
- }),
1236
- Image,
1237
- ImageUploadNode.configure({
1238
- toolbarOpts: toolbarOptsToUse,
1239
- imageHandling: {
1240
- disableImageAlignmentButtons: props.disableImageAlignmentButtons,
1241
- onDone: () => props.onDone?.(editor.getHTML()),
1242
- onDelete:
1243
- props.imageSupport &&
1244
- props.imageSupport.delete &&
1245
- ((node, done) => {
1246
- const { src } = node.attrs;
1247
-
1248
- props.imageSupport.delete(src, (e) => {
1249
- const newPendingImages = pendingImages.filter((img) => img.key !== node.key);
1250
- const newState = {
1251
- pendingImages: newPendingImages,
1252
- scheduled: scheduled && newPendingImages.length === 0 ? false : scheduled,
1253
- };
1254
-
1255
- setPendingImages(newState.pendingImages);
1256
- setScheduled(newState.scheduled);
1257
- done();
1258
- });
1259
- }),
1260
- insertImageRequested:
1261
- props.imageSupport &&
1262
- ((addedImage, getHandler) => {
1263
- const onFinish = (result) => {
1264
- let cb;
1265
-
1266
- if (scheduled && result) {
1267
- // finish editing only on success
1268
- cb = props.onChange;
1269
- }
1270
-
1271
- const newPendingImages = pendingImages.filter((img) => img.key !== addedImage.key);
1272
- const newState = {
1273
- pendingImages: newPendingImages,
1274
- };
1275
-
1276
- if (newPendingImages.length === 0) {
1277
- newState.scheduled = false;
1278
- }
1279
-
1280
- setPendingImages(newState.pendingImages);
1281
- setScheduled(newState.scheduled);
1282
- cb?.(editor.getHTML());
1283
- };
1284
- const callback = () => {
1285
- /**
1286
- * The handler is the object through which the outer context
1287
- * communicates file upload events like: fileChosen, cancel, progress
1288
- */
1289
- const handler = getHandler(onFinish);
1290
- props.imageSupport.add(handler);
1291
- };
1292
-
1293
- setPendingImages([...pendingImages, addedImage]);
1294
- callback();
1295
- }),
1296
- maxImageWidth: props.maxImageWidth,
1297
- maxImageHeight: props.maxImageHeight,
1298
- },
1299
- limit: 3,
1300
- }),
1301
- Media.configure({
1302
- uploadSoundSupport: props.uploadSoundSupport,
1303
- }),
1304
- CSSMark.configure({
1305
- extraCSSRules: props.extraCSSRules,
1306
- }),
1307
- ];
1308
- const editor = useEditor({
1309
- extensions,
1310
- immediatelyRender: false,
1311
- content: props.markup,
1312
- onUpdate: ({ editor, transaction }) => transaction.isDone && props.onChange?.(editor.getHTML()),
1313
- onBlur: ({ editor }) => {
1314
- if (toolbarOptsToUse.doneOn === 'blur') {
1315
- props.onChange?.(editor.getHTML());
1316
- } else {
1317
- props.onDone?.(editor.getHTML());
1318
- }
1319
- },
1320
- });
1321
-
1322
- useEffect(() => {
1323
- if (!editor) {
1324
- return;
1325
- }
1326
-
1327
- if (props.markup !== editor.getHTML()) {
1328
- editor.commands.setContent(props.markup, false); // false = don’t emit update
1329
- }
1330
- }, [props.markup, editor]);
1331
-
1332
- useEffect(() => {
1333
- // Define your variables in a JS object
1334
- const cssVariables = {
1335
- '--white': '#fff',
1336
- '--black': '#2e2b29',
1337
- '--black-contrast': '#110f0e',
1338
- '--gray-1': 'rgba(61, 37, 20, .05)',
1339
- '--gray-2': 'rgba(61, 37, 20, .08)',
1340
- '--gray-3': 'rgba(61, 37, 20, .12)',
1341
- '--gray-4': 'rgba(53, 38, 28, .3)',
1342
- '--gray-5': 'rgba(28, 25, 23, .6)',
1343
- '--green': '#22c55e',
1344
- '--purple': '#6a00f5',
1345
- '--purple-contrast': '#5800cc',
1346
- '--purple-light': 'rgba(88, 5, 255, .05)',
1347
- '--yellow-contrast': '#facc15',
1348
- '--yellow': 'rgba(250, 204, 21, .4)',
1349
- '--yellow-light': '#fffae5',
1350
- '--red': '#ff5c33',
1351
- '--red-light': '#ffebe5',
1352
- '--shadow': `0px 12px 33px 0px rgba(0, 0, 0, .06),
1353
- 0px 3.618px 9.949px 0px rgba(0, 0, 0, .04)`,
1354
- };
1355
-
1356
- Object.entries(cssVariables).forEach(([key, value]) => {
1357
- document.documentElement.style.setProperty(key, value);
1358
- });
1359
- }, []);
1360
-
1361
- const editorState = useEditorState({
1362
- editor,
1363
- selector: (ctx) => {
1364
- return {
1365
- isFocused: ctx.editor?.isFocused,
1366
- };
1367
- },
1368
- });
1369
-
1370
- const valueToSize = (v) => {
1371
- if (!v) {
1372
- return;
1373
- }
1374
- const calcRegex = /^calc\((.*)\)$/;
1375
-
1376
- if (typeof v === 'string') {
1377
- if (v.endsWith('%')) {
1378
- return undefined;
1379
- } else if (
1380
- v.endsWith('px') ||
1381
- v.endsWith('vh') ||
1382
- v.endsWith('vw') ||
1383
- v.endsWith('ch') ||
1384
- v.endsWith('em') ||
1385
- v.match(calcRegex)
1386
- ) {
1387
- return v;
1388
- } else {
1389
- const value = parseInt(v, 10);
1390
- return isNaN(value) ? value : `${value}px`;
1391
- }
1392
- }
1393
- if (typeof v === 'number') {
1394
- return `${v}px`;
1395
- }
1396
- };
1397
-
1398
- const sizeStyle = useMemo(() => {
1399
- const { minWidth, width, maxWidth, minHeight, height, maxHeight } = props;
1400
-
1401
- return {
1402
- width: valueToSize(width),
1403
- minWidth: valueToSize(minWidth),
1404
- maxWidth: valueToSize(maxWidth),
1405
- height: valueToSize(height),
1406
- minHeight: valueToSize(minHeight),
1407
- maxHeight: valueToSize(maxHeight),
1408
- };
1409
- }, [props]);
1410
-
1411
- return (
1412
- <EditorContainer
1413
- {...{ ...props, activePlugins: activePluginsToUse, toolbarOpts: toolbarOptsToUse }}
1414
- editorState={editorState}
1415
- editor={editor}
1416
- >
1417
- {editor && (
1418
- <EditorContent
1419
- style={{
1420
- minHeight: sizeStyle.minHeight,
1421
- height: sizeStyle.height,
1422
- maxHeight: sizeStyle.maxHeight,
1423
- }}
1424
- className={classes.root}
1425
- editor={editor}
1426
- />
1427
- )}
1428
- </EditorContainer>
1429
- );
1430
- };
1431
-
1432
- const StyledEditor = withStyles({
1433
- root: {
1434
- outline: 'none !important',
1435
- '& .ProseMirror': {
1436
- outline: 'none !important',
1437
- position: 'initial',
1438
- },
1439
- },
1440
- })(EditableHtml);
1
+ import StyledEditor, { EditableHtml } from './components/EditableHtml';
2
+ import { ALL_PLUGINS } from './extensions';
1441
3
 
4
+ export { EditableHtml, ALL_PLUGINS };
1442
5
  export default StyledEditor;