@pie-lib/editable-html-tip-tap 1.0.1 → 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 -1454
  32. package/lib/plugins/index.js +10 -82
  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 -1409
  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,1411 +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 { 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.chain().focus().insertTable({ rows: 2, cols: 2, withHeaderRow: false }).run(),
740
- hidden: (state) => !activePlugins?.includes('table') || state.isTable,
741
- isActive: (state) => state.isTable,
742
- isDisabled: (state) => !state.canTable,
743
- },
744
- {
745
- icon: <AddRow />,
746
- onClick: (editor) =>
747
- editor
748
- .chain()
749
- .focus()
750
- .addRowAfter()
751
- .run(),
752
- hidden: (state) => !state.isTable,
753
- isActive: (state) => state.isTable,
754
- isDisabled: (state) => !state.canTable,
755
- },
756
- {
757
- icon: <RemoveRow />,
758
- onClick: (editor) =>
759
- editor
760
- .chain()
761
- .focus()
762
- .deleteRow()
763
- .run(),
764
- hidden: (state) => !state.isTable,
765
- isActive: (state) => state.isTable,
766
- isDisabled: (state) => !state.canTable,
767
- },
768
- {
769
- icon: <AddColumn />,
770
- onClick: (editor) =>
771
- editor
772
- .chain()
773
- .focus()
774
- .addColumnAfter()
775
- .run(),
776
- hidden: (state) => !state.isTable,
777
- isActive: (state) => state.isTable,
778
- isDisabled: (state) => !state.canTable,
779
- },
780
- {
781
- icon: <RemoveColumn />,
782
- onClick: (editor) =>
783
- editor
784
- .chain()
785
- .focus()
786
- .deleteColumn()
787
- .run(),
788
- hidden: (state) => !state.isTable,
789
- isActive: (state) => state.isTable,
790
- isDisabled: (state) => !state.canTable,
791
- },
792
- {
793
- icon: <RemoveTable />,
794
- onClick: (editor) =>
795
- editor
796
- .chain()
797
- .focus()
798
- .deleteTable()
799
- .run(),
800
- hidden: (state) => !state.isTable,
801
- isActive: (state) => state.isTable,
802
- isDisabled: (state) => !state.canTable,
803
- },
804
- {
805
- icon: <BorderAll />,
806
- onClick: (editor) => {
807
- const tableAttrs = editor.getAttributes('table');
808
-
809
- const update = {
810
- ...tableAttrs,
811
- border: tableAttrs.border !== '0' ? '0' : '1',
812
- };
813
-
814
- editor.commands.updateAttributes('table', update);
815
- },
816
- hidden: (state) => !state.isTable,
817
- isActive: (state) => state.tableHasBorder,
818
- isDisabled: (state) => !state.canTable,
819
- },
820
- {
821
- icon: <Bold />,
822
- onClick: (editor) =>
823
- editor
824
- .chain()
825
- .focus()
826
- .toggleBold()
827
- .run(),
828
- hidden: (state) => state.isTable,
829
- isActive: (state) => state.isBold,
830
- isDisabled: (state) => !state.canBold,
831
- },
832
- {
833
- icon: <Italic />,
834
- onClick: (editor) =>
835
- editor
836
- .chain()
837
- .focus()
838
- .toggleItalic()
839
- .run(),
840
- hidden: (state) => state.isTable,
841
- isActive: (state) => state.isItalic,
842
- isDisabled: (state) => !state.canItalic,
843
- },
844
- {
845
- icon: <Strikethrough />,
846
- onClick: (editor) =>
847
- editor
848
- .chain()
849
- .focus()
850
- .toggleStrike()
851
- .run(),
852
- hidden: (state) => state.isTable,
853
- isActive: (state) => state.isStrike,
854
- isDisabled: (state) => !state.canStrike,
855
- },
856
- {
857
- icon: <Code />,
858
- onClick: (editor) =>
859
- editor
860
- .chain()
861
- .focus()
862
- .toggleCode()
863
- .run(),
864
- hidden: (state) => state.isTable,
865
- isActive: (state) => state.isCode,
866
- isDisabled: (state) => !state.canCode,
867
- },
868
- {
869
- icon: <Underline />,
870
- onClick: (editor) =>
871
- editor
872
- .chain()
873
- .focus()
874
- .toggleUnderline()
875
- .run(),
876
- hidden: (state) => state.isTable,
877
- isActive: (state) => state.isUnderline,
878
- },
879
- {
880
- icon: <SubscriptIcon />,
881
- onClick: (editor) =>
882
- editor
883
- .chain()
884
- .focus()
885
- .toggleSubscript()
886
- .run(),
887
- hidden: (state) => state.isTable,
888
- isActive: (state) => state.isSubScript,
889
- },
890
- {
891
- icon: <SuperscriptIcon />,
892
- onClick: (editor) =>
893
- editor
894
- .chain()
895
- .focus()
896
- .toggleSuperscript()
897
- .run(),
898
- hidden: (state) => state.isTable,
899
- isActive: (state) => state.isSuperScript,
900
- },
901
- {
902
- icon: <ImageIcon />,
903
- onClick: (editor) =>
904
- editor
905
- .chain()
906
- .focus()
907
- .setImageUploadNode()
908
- .run(),
909
- },
910
- {
911
- icon: <TheatersIcon />,
912
- hidden: (state) => state.isTable,
913
- onClick: (editor) =>
914
- editor
915
- .chain()
916
- .focus()
917
- .insertMedia({ tag: 'video' })
918
- .run(),
919
- },
920
- {
921
- icon: <VolumeUpIcon />,
922
- hidden: (state) => state.isTable,
923
- onClick: (editor) =>
924
- editor
925
- .chain()
926
- .focus()
927
- .insertMedia({ tag: 'audio' })
928
- .run(),
929
- },
930
- {
931
- icon: <CSSIcon />,
932
- hidden: (state) => state.isTable,
933
- onClick: (editor) => editor.commands.openCSSClassDialog(),
934
- },
935
- {
936
- icon: <HeadingIcon />,
937
- hidden: (state) => state.isTable,
938
- onClick: (editor) =>
939
- editor
940
- .chain()
941
- .focus()
942
- .toggleHeading({ level: 3 })
943
- .run(),
944
- isActive: (state) => state.isHeading3,
945
- },
946
- {
947
- icon: <Functions />,
948
- onClick: (editor) =>
949
- editor
950
- .chain()
951
- .focus()
952
- .insertMath('')
953
- .run(),
954
- },
955
- {
956
- icon: <CharacterIcon letter="ñ" />,
957
- hidden: (state) => state.isTable,
958
- onClick: () => setShowPicker(spanishConfig),
959
- },
960
- {
961
- icon: <CharacterIcon letter="€" />,
962
- hidden: (state) => state.isTable,
963
- onClick: () => setShowPicker(specialConfig),
964
- },
965
- {
966
- icon: <TextAlignIcon editor={editor} />,
967
- hidden: (state) => state.isTable,
968
- onClick: () => {},
969
- },
970
- {
971
- icon: <BulletedListIcon />,
972
- hidden: (state) => state.isTable,
973
- onClick: (editor) =>
974
- editor
975
- .chain()
976
- .focus()
977
- .toggleBulletList()
978
- .run(),
979
- isActive: (state) => state.isBulletList,
980
- },
981
- {
982
- icon: <NumberedListIcon />,
983
- hidden: (state) => state.isTable,
984
- onClick: (editor) =>
985
- editor
986
- .chain()
987
- .focus()
988
- .toggleOrderedList()
989
- .run(),
990
- isActive: (state) => state.isOrderedList,
991
- },
992
- {
993
- icon: <Undo />,
994
- hidden: (state) => state.isTable,
995
- onClick: (editor) =>
996
- editor
997
- .chain()
998
- .focus()
999
- .undo()
1000
- .run(),
1001
- isDisabled: (state) => !state.canUndo,
1002
- },
1003
- {
1004
- icon: <Redo />,
1005
- hidden: (state) => state.isTable,
1006
- onClick: (editor) =>
1007
- editor
1008
- .chain()
1009
- .focus()
1010
- .redo()
1011
- .run(),
1012
- isDisabled: (state) => !state.canRedo,
1013
- },
1014
- ],
1015
- [activePlugins, editor, spanishConfig, specialConfig, setShowPicker],
1016
- );
1017
-
1018
- return (
1019
- <div className={names} style={{ ...customStyles }} onMouseDown={handleMouseDown}>
1020
- {!editorState.hideDefaultToolbar && (
1021
- <div className={classes.defaultToolbar} tabIndex="1">
1022
- <div className={classes.buttonsContainer}>
1023
- {toolbarButtons
1024
- .filter((btn) => !btn.hidden?.(editorState))
1025
- .map((btn, index) => {
1026
- const disabled = btn.isDisabled?.(editorState);
1027
- const active = btn.isActive?.(editorState);
1028
-
1029
- return (
1030
- <button
1031
- key={index}
1032
- disabled={disabled}
1033
- onClick={(e) => {
1034
- e.preventDefault();
1035
- btn.onClick(editor);
1036
- }}
1037
- className={classNames(classes.button, { [classes.active]: active })}
1038
- >
1039
- {btn.icon}
1040
- </button>
1041
- );
1042
- })}
1043
- </div>
1044
- <button
1045
- onClick={() => {
1046
- editor
1047
- .chain()
1048
- .focus()
1049
- .insertResponseArea(responseAreaProps.type)
1050
- .run();
1051
- }}
1052
- className={classes.button}
1053
- >
1054
- <ToolbarIcon />
1055
- </button>
1056
-
1057
- <DoneButton
1058
- onClick={() => {
1059
- onChange?.(editor.getHTML());
1060
- editor.commands.blur();
1061
- }}
1062
- />
1063
- </div>
1064
- )}
1065
- {showPicker && (
1066
- <CharacterPicker
1067
- editor={editor}
1068
- opts={{
1069
- ...showPicker,
1070
- renderPopOver: (ev, ch) => console.log('Show popover', ch),
1071
- closePopOver: () => console.log('Close popover'),
1072
- }}
1073
- onClose={() => setShowPicker(false)}
1074
- />
1075
- )}
1076
- </div>
1077
- );
1078
- }
1079
-
1080
- const style = (theme) => ({
1081
- defaultToolbar: {
1082
- display: 'flex',
1083
- width: '100%',
1084
- justifyContent: 'space-between',
1085
- },
1086
- buttonsContainer: {
1087
- alignItems: 'center',
1088
- display: 'flex',
1089
- width: '100%',
1090
- },
1091
- button: {
1092
- color: 'grey',
1093
- display: 'inline-flex',
1094
- padding: '2px',
1095
- background: 'none',
1096
- border: 'none',
1097
- cursor: 'pointer',
1098
- '&:hover': {
1099
- color: 'black',
1100
- },
1101
- '&:focus': {
1102
- outline: `2px solid ${theme.palette.grey[700]}`,
1103
- },
1104
- },
1105
- active: {
1106
- color: 'black',
1107
- },
1108
- disabled: {
1109
- opacity: 0.7,
1110
- cursor: 'not-allowed',
1111
- '& :hover': {
1112
- color: 'grey',
1113
- },
1114
- },
1115
- isActive: {
1116
- background: 'var(--purple)',
1117
- color: 'var(--white)',
1118
- },
1119
- toolbar: {
1120
- position: 'absolute',
1121
- zIndex: 20,
1122
- cursor: 'pointer',
1123
- justifyContent: 'space-between',
1124
- background: 'var(--editable-html-toolbar-bg, #efefef)',
1125
- minWidth: '280px',
1126
- margin: '5px 0 0 0',
1127
- padding: '2px',
1128
- boxShadow:
1129
- '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)',
1130
- boxSizing: 'border-box',
1131
- display: 'flex',
1132
- opacity: 0,
1133
- pointerEvents: 'none',
1134
- },
1135
- toolbarWithNoDone: {
1136
- minWidth: '265px',
1137
- },
1138
- toolbarTop: {
1139
- top: '-45px',
1140
- },
1141
- toolbarRight: {
1142
- right: 0,
1143
- },
1144
- fullWidth: {
1145
- width: '100%',
1146
- },
1147
- hidden: {
1148
- visibility: 'hidden',
1149
- },
1150
- autoWidth: {
1151
- width: 'auto',
1152
- },
1153
- focused: {
1154
- opacity: 1,
1155
- pointerEvents: 'auto',
1156
- },
1157
- iconRoot: {
1158
- width: '28px',
1159
- height: '28px',
1160
- padding: '4px',
1161
- verticalAlign: 'top',
1162
- },
1163
- label: {
1164
- color: 'var(--editable-html-toolbar-check, #00bb00)',
1165
- },
1166
- shared: {
1167
- display: 'flex',
1168
- },
1169
- });
1170
- const StyledMenuBar = withStyles(style, { index: 1000 })(MenuBar);
1171
-
1172
- const defaultToolbarOpts = {
1173
- position: 'bottom',
1174
- alignment: 'left',
1175
- alwaysVisible: false,
1176
- showDone: true,
1177
- doneOn: 'blur',
1178
- };
1179
-
1180
- export const EditableHtml = (props) => {
1181
- const [pendingImages, setPendingImages] = useState([]);
1182
- const [scheduled, setScheduled] = useState(false);
1183
- const { classes, toolbarOpts } = props;
1184
- const toolbarOptsToUse = {
1185
- ...defaultToolbarOpts,
1186
- ...toolbarOpts,
1187
- };
1188
- const activePluginsToUse = props.activePlugins || DEFAULT_PLUGINS;
1189
- const extensions = [
1190
- TextStyleKit,
1191
- StarterKit,
1192
- ExtendedTable,
1193
- TableRow,
1194
- TableHeader,
1195
- TableCell,
1196
- ResponseAreaExtension,
1197
- ExplicitConstructedResponseNode.configure(props.responseAreaProps),
1198
- DragInTheBlankNode.configure(props.responseAreaProps),
1199
- InlineDropdownNode.configure(props.responseAreaProps),
1200
- MathNode.configure({
1201
- toolbarOpts: toolbarOptsToUse,
1202
- }),
1203
- SubScript,
1204
- SuperScript,
1205
- TextAlign.configure({
1206
- types: ['heading', 'paragraph'],
1207
- alignments: ['left', 'right', 'center'],
1208
- }),
1209
- Image,
1210
- ImageUploadNode.configure({
1211
- toolbarOpts: toolbarOptsToUse,
1212
- imageHandling: {
1213
- disableImageAlignmentButtons: props.disableImageAlignmentButtons,
1214
- onDone: () => props.onDone?.(editor.getHTML()),
1215
- onDelete:
1216
- props.imageSupport &&
1217
- props.imageSupport.delete &&
1218
- ((node, done) => {
1219
- const { src } = node.attrs;
1220
-
1221
- props.imageSupport.delete(src, (e) => {
1222
- const newPendingImages = pendingImages.filter((img) => img.key !== node.key);
1223
- const newState = {
1224
- pendingImages: newPendingImages,
1225
- scheduled: scheduled && newPendingImages.length === 0 ? false : scheduled,
1226
- };
1227
-
1228
- setPendingImages(newState.pendingImages);
1229
- setScheduled(newState.scheduled);
1230
- done();
1231
- });
1232
- }),
1233
- insertImageRequested:
1234
- props.imageSupport &&
1235
- ((addedImage, getHandler) => {
1236
- const onFinish = (result) => {
1237
- let cb;
1238
-
1239
- if (scheduled && result) {
1240
- // finish editing only on success
1241
- cb = props.onChange;
1242
- }
1243
-
1244
- const newPendingImages = pendingImages.filter((img) => img.key !== addedImage.key);
1245
- const newState = {
1246
- pendingImages: newPendingImages,
1247
- };
1248
-
1249
- if (newPendingImages.length === 0) {
1250
- newState.scheduled = false;
1251
- }
1252
-
1253
- setPendingImages(newState.pendingImages);
1254
- setScheduled(newState.scheduled);
1255
- cb?.(editor.getHTML());
1256
- };
1257
- const callback = () => {
1258
- /**
1259
- * The handler is the object through which the outer context
1260
- * communicates file upload events like: fileChosen, cancel, progress
1261
- */
1262
- const handler = getHandler(onFinish);
1263
- props.imageSupport.add(handler);
1264
- };
1265
-
1266
- setPendingImages([...pendingImages, addedImage]);
1267
- callback();
1268
- }),
1269
- maxImageWidth: props.maxImageWidth,
1270
- maxImageHeight: props.maxImageHeight,
1271
- },
1272
- limit: 3,
1273
- }),
1274
- Media.configure({
1275
- uploadSoundSupport: props.uploadSoundSupport,
1276
- }),
1277
- CSSMark.configure({
1278
- extraCSSRules: props.extraCSSRules,
1279
- }),
1280
- ];
1281
- const editor = useEditor({
1282
- extensions,
1283
- immediatelyRender: false,
1284
- content: props.markup,
1285
- onUpdate: ({ editor, transaction }) => transaction.isDone && props.onChange?.(editor.getHTML()),
1286
- onBlur: ({ editor }) => {
1287
- if (toolbarOptsToUse.doneOn === 'blur') {
1288
- props.onChange?.(editor.getHTML());
1289
- } else {
1290
- props.onDone?.(editor.getHTML());
1291
- }
1292
- },
1293
- });
1294
-
1295
- useEffect(() => {
1296
- if (!editor) {
1297
- return;
1298
- }
1299
-
1300
- if (props.markup !== editor.getHTML()) {
1301
- editor.commands.setContent(props.markup, false); // false = don’t emit update
1302
- }
1303
- }, [props.markup, editor]);
1304
-
1305
- useEffect(() => {
1306
- // Define your variables in a JS object
1307
- const cssVariables = {
1308
- '--white': '#fff',
1309
- '--black': '#2e2b29',
1310
- '--black-contrast': '#110f0e',
1311
- '--gray-1': 'rgba(61, 37, 20, .05)',
1312
- '--gray-2': 'rgba(61, 37, 20, .08)',
1313
- '--gray-3': 'rgba(61, 37, 20, .12)',
1314
- '--gray-4': 'rgba(53, 38, 28, .3)',
1315
- '--gray-5': 'rgba(28, 25, 23, .6)',
1316
- '--green': '#22c55e',
1317
- '--purple': '#6a00f5',
1318
- '--purple-contrast': '#5800cc',
1319
- '--purple-light': 'rgba(88, 5, 255, .05)',
1320
- '--yellow-contrast': '#facc15',
1321
- '--yellow': 'rgba(250, 204, 21, .4)',
1322
- '--yellow-light': '#fffae5',
1323
- '--red': '#ff5c33',
1324
- '--red-light': '#ffebe5',
1325
- '--shadow': `0px 12px 33px 0px rgba(0, 0, 0, .06),
1326
- 0px 3.618px 9.949px 0px rgba(0, 0, 0, .04)`,
1327
- };
1328
-
1329
- Object.entries(cssVariables).forEach(([key, value]) => {
1330
- document.documentElement.style.setProperty(key, value);
1331
- });
1332
- }, []);
1333
-
1334
- const editorState = useEditorState({
1335
- editor,
1336
- selector: (ctx) => {
1337
- return {
1338
- isFocused: ctx.editor?.isFocused,
1339
- };
1340
- },
1341
- });
1342
-
1343
- const valueToSize = (v) => {
1344
- if (!v) {
1345
- return;
1346
- }
1347
- const calcRegex = /^calc\((.*)\)$/;
1348
-
1349
- if (typeof v === 'string') {
1350
- if (v.endsWith('%')) {
1351
- return undefined;
1352
- } else if (
1353
- v.endsWith('px') ||
1354
- v.endsWith('vh') ||
1355
- v.endsWith('vw') ||
1356
- v.endsWith('ch') ||
1357
- v.endsWith('em') ||
1358
- v.match(calcRegex)
1359
- ) {
1360
- return v;
1361
- } else {
1362
- const value = parseInt(v, 10);
1363
- return isNaN(value) ? value : `${value}px`;
1364
- }
1365
- }
1366
- if (typeof v === 'number') {
1367
- return `${v}px`;
1368
- }
1369
- };
1370
-
1371
- const sizeStyle = useMemo(() => {
1372
- const { minWidth, width, maxWidth, minHeight, height, maxHeight } = props;
1373
-
1374
- return {
1375
- width: valueToSize(width),
1376
- minWidth: valueToSize(minWidth),
1377
- maxWidth: valueToSize(maxWidth),
1378
- height: valueToSize(height),
1379
- minHeight: valueToSize(minHeight),
1380
- maxHeight: valueToSize(maxHeight),
1381
- };
1382
- }, [props]);
1383
-
1384
- return (
1385
- <EditorContainer {...{ ...props, activePlugins: activePluginsToUse, toolbarOpts: toolbarOptsToUse }} editorState={editorState} editor={editor}>
1386
- {editor && (
1387
- <EditorContent
1388
- style={{
1389
- minHeight: sizeStyle.minHeight,
1390
- height: sizeStyle.height,
1391
- maxHeight: sizeStyle.maxHeight,
1392
- }}
1393
- className={classes.root}
1394
- editor={editor}
1395
- />
1396
- )}
1397
- </EditorContainer>
1398
- );
1399
- };
1400
-
1401
- const StyledEditor = withStyles({
1402
- root: {
1403
- outline: 'none !important',
1404
- '& .ProseMirror': {
1405
- outline: 'none !important',
1406
- position: 'initial',
1407
- },
1408
- },
1409
- })(EditableHtml);
1
+ import StyledEditor, { EditableHtml } from './components/EditableHtml';
2
+ import { ALL_PLUGINS } from './extensions';
1410
3
 
4
+ export { EditableHtml, ALL_PLUGINS };
1411
5
  export default StyledEditor;