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

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 (165) hide show
  1. package/lib/components/CharacterPicker.js +221 -0
  2. package/lib/components/CharacterPicker.js.map +1 -0
  3. package/lib/components/EditableHtml.js +323 -0
  4. package/lib/components/EditableHtml.js.map +1 -0
  5. package/lib/components/MenuBar.js +694 -0
  6. package/lib/components/MenuBar.js.map +1 -0
  7. package/lib/components/TiptapContainer.js +90 -0
  8. package/lib/components/TiptapContainer.js.map +1 -0
  9. package/lib/components/buttons/done-button.js +53 -0
  10. package/lib/components/characters/characterUtils.js +112 -0
  11. package/lib/components/characters/characterUtils.js.map +1 -0
  12. package/lib/components/characters/custom-popper.js +73 -0
  13. package/lib/components/characters/custom-popper.js.map +1 -0
  14. package/lib/components/common/done-button.js +53 -0
  15. package/lib/components/common/done-button.js.map +1 -0
  16. package/lib/components/common/toolbar-buttons.js +194 -0
  17. package/lib/components/icons/CssIcon.js +37 -0
  18. package/lib/components/icons/CssIcon.js.map +1 -0
  19. package/lib/components/icons/RespArea.js +95 -0
  20. package/lib/components/icons/RespArea.js.map +1 -0
  21. package/lib/components/icons/TableIcons.js +69 -0
  22. package/lib/components/icons/TableIcons.js.map +1 -0
  23. package/lib/components/icons/TextAlign.js +194 -0
  24. package/lib/components/icons/TextAlign.js.map +1 -0
  25. package/lib/components/icons/index.js +194 -0
  26. package/lib/components/image/AltDialog.js +129 -0
  27. package/lib/components/image/ImageToolbar.js +177 -0
  28. package/lib/components/image/ImageToolbar.js.map +1 -0
  29. package/lib/components/image/InsertImageHandler.js +115 -0
  30. package/lib/components/image/InsertImageHandler.js.map +1 -0
  31. package/lib/components/image/alt-dialog.js +2 -0
  32. package/lib/components/media/MediaDialog.js +709 -0
  33. package/lib/components/media/MediaDialog.js.map +1 -0
  34. package/lib/components/media/MediaToolbar.js +101 -0
  35. package/lib/components/media/MediaToolbar.js.map +1 -0
  36. package/lib/components/media/MediaWrapper.js +93 -0
  37. package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js +94 -0
  38. package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js.map +1 -0
  39. package/lib/components/respArea/DragInTheBlank/choice.js +289 -0
  40. package/lib/components/respArea/DragInTheBlank/choice.js.map +1 -0
  41. package/lib/components/respArea/DragInTheBlank.js +94 -0
  42. package/lib/components/respArea/ExplicitConstructedResponse.js +120 -0
  43. package/lib/components/respArea/ExplicitConstructedResponse.js.map +1 -0
  44. package/lib/components/respArea/InlineDropdown.js +126 -0
  45. package/lib/components/respArea/InlineDropdown.js.map +1 -0
  46. package/lib/components/respArea/ToolbarIcon.js +105 -0
  47. package/lib/components/respArea/ToolbarIcon.js.map +1 -0
  48. package/lib/components/respArea/choice.js +2 -0
  49. package/lib/constants.js.map +1 -0
  50. package/lib/extensions/component.js +5 -5
  51. package/lib/extensions/component.js.map +1 -0
  52. package/lib/extensions/css.js.map +1 -0
  53. package/lib/extensions/custom-toolbar-wrapper.js +2 -4
  54. package/lib/extensions/custom-toolbar-wrapper.js.map +1 -0
  55. package/lib/extensions/extended-table.js +30 -0
  56. package/lib/extensions/extended-table.js.map +1 -0
  57. package/lib/extensions/image.js +2 -8
  58. package/lib/extensions/image.js.map +1 -0
  59. package/lib/extensions/index.js +52 -0
  60. package/lib/extensions/index.js.map +1 -0
  61. package/lib/extensions/math.js.map +1 -0
  62. package/lib/extensions/media.js +7 -7
  63. package/lib/extensions/media.js.map +1 -0
  64. package/lib/extensions/responseArea.js +7 -7
  65. package/lib/extensions/responseArea.js.map +1 -0
  66. package/lib/index.js +16 -1481
  67. package/lib/index.js.map +1 -0
  68. package/lib/plugins/index.js +8 -80
  69. package/lib/styles/editorContainerStyles.js +200 -0
  70. package/lib/styles/editorContainerStyles.js.map +1 -0
  71. package/lib/theme.js.map +1 -0
  72. package/lib/utils/size.js +34 -0
  73. package/lib/utils/size.js.map +1 -0
  74. package/package.json +1 -1
  75. package/src/components/CharacterPicker.jsx +185 -0
  76. package/src/components/EditableHtml.jsx +306 -0
  77. package/src/components/MenuBar.jsx +630 -0
  78. package/src/components/TiptapContainer.jsx +96 -0
  79. package/src/components/characters/characterUtils.js +127 -0
  80. package/src/{plugins/image/image-toolbar.jsx → components/image/ImageToolbar.jsx} +2 -2
  81. package/src/{plugins/image/insert-image-handler.js → components/image/InsertImageHandler.js} +0 -1
  82. package/src/{plugins/media/media-dialog.js → components/media/MediaDialog.js} +2 -2
  83. package/src/{plugins/respArea/drag-in-the-blank → components/respArea/DragInTheBlank}/choice.jsx +1 -1
  84. package/src/{plugins/respArea/inline-dropdown/index.jsx → components/respArea/InlineDropdown.jsx} +1 -1
  85. package/src/components/respArea/ToolbarIcon.jsx +68 -0
  86. package/src/extensions/component.jsx +2 -2
  87. package/src/extensions/custom-toolbar-wrapper.jsx +6 -7
  88. package/src/extensions/extended-table.js +27 -0
  89. package/src/extensions/image.js +2 -2
  90. package/src/extensions/index.js +76 -0
  91. package/src/extensions/media.js +12 -7
  92. package/src/extensions/responseArea.js +7 -7
  93. package/src/index.jsx +3 -1440
  94. package/src/styles/editorContainerStyles.js +203 -0
  95. package/src/utils/size.js +32 -0
  96. package/src/__tests__/editor.test.jsx +0 -363
  97. package/src/__tests__/serialization.test.js +0 -291
  98. package/src/block-tags.js +0 -17
  99. package/src/editor.jsx +0 -1197
  100. package/src/extensions/characters.js +0 -46
  101. package/src/old-index.jsx +0 -162
  102. package/src/parse-html.js +0 -8
  103. package/src/plugins/README.md +0 -27
  104. package/src/plugins/characters/index.jsx +0 -284
  105. package/src/plugins/characters/utils.js +0 -447
  106. package/src/plugins/css/index.jsx +0 -340
  107. package/src/plugins/customPlugin/index.jsx +0 -85
  108. package/src/plugins/html/icons/index.jsx +0 -19
  109. package/src/plugins/html/index.jsx +0 -72
  110. package/src/plugins/image/__tests__/__snapshots__/component.test.jsx.snap +0 -51
  111. package/src/plugins/image/__tests__/__snapshots__/image-toolbar-logic.test.jsx.snap +0 -27
  112. package/src/plugins/image/__tests__/__snapshots__/image-toolbar.test.jsx.snap +0 -44
  113. package/src/plugins/image/__tests__/component.test.jsx +0 -41
  114. package/src/plugins/image/__tests__/image-toolbar-logic.test.jsx +0 -42
  115. package/src/plugins/image/__tests__/image-toolbar.test.jsx +0 -11
  116. package/src/plugins/image/__tests__/index.test.js +0 -95
  117. package/src/plugins/image/__tests__/insert-image-handler.test.js +0 -113
  118. package/src/plugins/image/__tests__/mock-change.js +0 -15
  119. package/src/plugins/image/component.jsx +0 -343
  120. package/src/plugins/image/index.jsx +0 -227
  121. package/src/plugins/index.jsx +0 -377
  122. package/src/plugins/list/__tests__/index.test.js +0 -54
  123. package/src/plugins/list/index.jsx +0 -305
  124. package/src/plugins/math/__tests__/__snapshots__/index.test.jsx.snap +0 -48
  125. package/src/plugins/math/__tests__/index.test.jsx +0 -245
  126. package/src/plugins/math/index.jsx +0 -379
  127. package/src/plugins/media/__tests__/index.test.js +0 -75
  128. package/src/plugins/media/index.jsx +0 -325
  129. package/src/plugins/rendering/index.js +0 -31
  130. package/src/plugins/respArea/index.jsx +0 -299
  131. package/src/plugins/respArea/math-templated/index.jsx +0 -104
  132. package/src/plugins/respArea/utils.jsx +0 -90
  133. package/src/plugins/table/CustomTablePlugin.js +0 -113
  134. package/src/plugins/table/__tests__/__snapshots__/table-toolbar.test.jsx.snap +0 -44
  135. package/src/plugins/table/__tests__/index.test.jsx +0 -401
  136. package/src/plugins/table/__tests__/table-toolbar.test.jsx +0 -42
  137. package/src/plugins/table/index.jsx +0 -427
  138. package/src/plugins/table/table-toolbar.jsx +0 -136
  139. package/src/plugins/textAlign/index.jsx +0 -23
  140. package/src/plugins/toolbar/__tests__/__snapshots__/default-toolbar.test.jsx.snap +0 -923
  141. package/src/plugins/toolbar/__tests__/__snapshots__/editor-and-toolbar.test.jsx.snap +0 -20
  142. package/src/plugins/toolbar/__tests__/__snapshots__/toolbar-buttons.test.jsx.snap +0 -36
  143. package/src/plugins/toolbar/__tests__/__snapshots__/toolbar.test.jsx.snap +0 -46
  144. package/src/plugins/toolbar/__tests__/default-toolbar.test.jsx +0 -94
  145. package/src/plugins/toolbar/__tests__/editor-and-toolbar.test.jsx +0 -37
  146. package/src/plugins/toolbar/__tests__/toolbar-buttons.test.jsx +0 -51
  147. package/src/plugins/toolbar/__tests__/toolbar.test.jsx +0 -106
  148. package/src/plugins/toolbar/default-toolbar.jsx +0 -206
  149. package/src/plugins/toolbar/editor-and-toolbar.jsx +0 -257
  150. package/src/plugins/toolbar/index.jsx +0 -23
  151. package/src/plugins/toolbar/toolbar.jsx +0 -338
  152. package/src/plugins/utils.js +0 -31
  153. package/src/serialization.jsx +0 -621
  154. /package/src/{plugins → components}/characters/custom-popper.js +0 -0
  155. /package/src/{plugins/toolbar → components/common}/done-button.jsx +0 -0
  156. /package/src/{plugins/toolbar → components/common}/toolbar-buttons.jsx +0 -0
  157. /package/src/{plugins/css/icons/index.jsx → components/icons/CssIcon.jsx} +0 -0
  158. /package/src/{plugins/respArea/icons/index.jsx → components/icons/RespArea.jsx} +0 -0
  159. /package/src/{plugins/table/icons/index.jsx → components/icons/TableIcons.jsx} +0 -0
  160. /package/src/{plugins/textAlign/icons/index.jsx → components/icons/TextAlign.jsx} +0 -0
  161. /package/src/{plugins/image/alt-dialog.jsx → components/image/AltDialog.jsx} +0 -0
  162. /package/src/{plugins/media/media-toolbar.jsx → components/media/MediaToolbar.jsx} +0 -0
  163. /package/src/{plugins/media/media-wrapper.jsx → components/media/MediaWrapper.jsx} +0 -0
  164. /package/src/{plugins/respArea/drag-in-the-blank/index.jsx → components/respArea/DragInTheBlank/DragInTheBlank.jsx} +0 -0
  165. /package/src/{plugins/respArea/explicit-constructed-response/index.jsx → components/respArea/ExplicitConstructedResponse.jsx} +0 -0
@@ -1,227 +0,0 @@
1
- import { Data, Inline } from 'slate';
2
-
3
- import Image from '@material-ui/icons/Image';
4
- import ImageComponent from './component';
5
- import ImageToolbar from './image-toolbar';
6
- import InsertImageHandler from './insert-image-handler';
7
- import React from 'react';
8
- import debug from 'debug';
9
-
10
- const log = debug('@pie-lib:editable-html:plugins:image');
11
-
12
- export default function ImagePlugin(opts) {
13
- const toolbar = opts.insertImageRequested && {
14
- icon: <Image />,
15
- ariaLabel: 'Insert Image',
16
- onClick: (value, onChange) => {
17
- log('[toolbar] onClick');
18
- const inline = Inline.create({
19
- type: 'image',
20
- isVoid: true,
21
- data: {
22
- loaded: false,
23
- src: undefined,
24
- },
25
- });
26
-
27
- const change = value.change().insertInline(inline);
28
-
29
- onChange(change);
30
- opts.insertImageRequested(
31
- inline,
32
- (onFinish, getValue) => new InsertImageHandler(inline, onFinish, getValue, onChange),
33
- );
34
- },
35
- supports: (node) => node.object === 'inline' && node.type === 'image',
36
- customToolbar: (node, value, onToolbarDone) => {
37
- const alignment = node.data.get('alignment');
38
- const alt = node.data.get('alt');
39
- const imageLoaded = node.data.get('loaded') !== false;
40
- const onChange = (newValues, done) => {
41
- const update = {
42
- ...node.data.toObject(),
43
- ...newValues,
44
- };
45
-
46
- const change = value.change().setNodeByKey(node.key, { data: update });
47
- onToolbarDone(change, done);
48
- };
49
-
50
- const Tb = () => (
51
- <ImageToolbar
52
- disableImageAlignmentButtons={opts.disableImageAlignmentButtons}
53
- alt={alt}
54
- imageLoaded={imageLoaded}
55
- alignment={alignment || 'left'}
56
- onChange={onChange}
57
- />
58
- );
59
- return Tb;
60
- },
61
- showDone: true,
62
- };
63
-
64
- return {
65
- name: 'image',
66
- toolbar,
67
- deleteNode: (e, node, value, onChange) => {
68
- e.preventDefault();
69
- if (opts.onDelete) {
70
- const update = node.data.merge(Data.create({ deleteStatus: 'pending' }));
71
-
72
- let change = value.change().setNodeByKey(node.key, { data: update });
73
-
74
- onChange(change);
75
- opts.onDelete(node, (err, v) => {
76
- if (!err) {
77
- change = v.change().removeNodeByKey(node.key);
78
- } else {
79
- log('[error]: ', err);
80
- change = v.change().setNodeByKey(node.key, node.data.merge(Data.create({ deleteStatus: 'failed' })));
81
- }
82
- onChange(change);
83
- });
84
- } else {
85
- let change = value.change().removeNodeByKey(node.key);
86
- onChange(change);
87
- }
88
- },
89
- stopReset: (value) => {
90
- const imgPendingInsertion = value.document.findDescendant((n) => {
91
- if (n.type !== 'image') {
92
- return;
93
- }
94
- return n.data.get('loaded') === false;
95
- });
96
- /** don't reset if there is an image pending insertion */
97
- return imgPendingInsertion !== undefined && imgPendingInsertion !== null;
98
- },
99
- renderNode(props) {
100
- if (props.node.type === 'image') {
101
- const all = Object.assign(
102
- {
103
- onDelete: opts.onDelete,
104
- onFocus: opts.onFocus,
105
- onBlur: opts.onBlur,
106
- maxImageWidth: opts.maxImageWidth,
107
- maxImageHeight: opts.maxImageHeight,
108
- },
109
- props,
110
- );
111
- return <ImageComponent {...all} />;
112
- }
113
- },
114
- normalizeNode: (node) => {
115
- const textNodeMap = {};
116
- const updateNodesArray = [];
117
- let index = 0;
118
-
119
- if (node.object !== 'document') return;
120
-
121
- node.findDescendant((d) => {
122
- if (d.object === 'text') {
123
- textNodeMap[index] = d;
124
- }
125
-
126
- if (d.type === 'image') {
127
- if (index > 0 && textNodeMap[index - 1] && textNodeMap[index - 1].text === '') {
128
- updateNodesArray.push(textNodeMap[index - 1]);
129
- }
130
- }
131
-
132
- index++;
133
- });
134
-
135
- if (!updateNodesArray.length) return;
136
-
137
- return (change) => {
138
- change.withoutNormalization(() => {
139
- updateNodesArray.forEach((n) => change.insertTextByKey(n.key, 0, ' '));
140
- });
141
- };
142
- },
143
- };
144
- }
145
-
146
- export const serialization = {
147
- deserialize(el /*, next*/) {
148
- const name = el.tagName.toLowerCase();
149
- if (name !== 'img') return;
150
-
151
- log('deserialize: ', name);
152
- const style = el.style || { width: '', height: '', margin: '', justifyContent: '' };
153
- const width = parseInt(style.width.replace('px', ''), 10) || null;
154
- const height = parseInt(style.height.replace('px', ''), 10) || null;
155
-
156
- const out = {
157
- object: 'inline',
158
- type: 'image',
159
- isVoid: true,
160
- data: {
161
- src: el.getAttribute('src'),
162
- width,
163
- height,
164
- margin: el.style.margin,
165
- justifyContent: el.style.justifyContent,
166
- alignment: el.getAttribute('alignment'),
167
- alt: el.getAttribute('alt'),
168
- },
169
- };
170
- log('return object: ', out);
171
- return out;
172
- },
173
- serialize(object /*, children*/) {
174
- if (object.type !== 'image') return;
175
-
176
- const { data } = object;
177
- const src = data.get('src');
178
- const width = data.get('width');
179
- const height = data.get('height');
180
- const alignment = data.get('alignment') || 'left';
181
- const margin = data.get('margin');
182
- const justifyContent = data.get('margin');
183
- const alt = data.get('alt');
184
- const style = {};
185
- if (width) {
186
- style.width = `${width}px`;
187
- }
188
-
189
- if (height) {
190
- style.height = `${height}px`;
191
- }
192
-
193
- style.margin = margin;
194
- style.justifyContent = justifyContent;
195
-
196
- if (alignment) {
197
- switch (alignment) {
198
- case 'left':
199
- style.justifyContent = 'flex-start';
200
- style.margin = '0';
201
- break;
202
- case 'center':
203
- style.justifyContent = 'center';
204
- style.margin = '0 auto';
205
- break;
206
- case 'right':
207
- style.justifyContent = 'flex-end';
208
- style.margin = 'auto 0 0 auto';
209
- break;
210
- default:
211
- style.justifyContent = 'flex-start';
212
- break;
213
- }
214
- }
215
-
216
- style.objectFit = 'contain';
217
-
218
- const props = {
219
- src,
220
- style,
221
- alignment,
222
- alt,
223
- };
224
-
225
- return <img {...props} />;
226
- },
227
- };
@@ -1,377 +0,0 @@
1
- import Hotkeys from 'slate-hotkeys';
2
- import { IS_IOS } from 'slate-dev-environment';
3
- import { Mark } from 'slate';
4
- import Bold from '@material-ui/icons/FormatBold';
5
- import FormatQuote from '@material-ui/icons/FormatQuote';
6
- //import Code from '@material-ui/icons/Code';
7
- import BulletedListIcon from '@material-ui/icons/FormatListBulleted';
8
- import NumberedListIcon from '@material-ui/icons/FormatListNumbered';
9
- import GridOn from '@material-ui/icons/GridOn';
10
- import Image from '@material-ui/icons/Image';
11
- import Redo from '@material-ui/icons/Redo';
12
- import Undo from '@material-ui/icons/Undo';
13
- import ImagePlugin from './image';
14
- import MediaPlugin from './media';
15
- import CharactersPlugin from './characters';
16
- import Italic from '@material-ui/icons/FormatItalic';
17
- import MathPlugin from './math';
18
- import React from 'react';
19
- import Strikethrough from '@material-ui/icons/FormatStrikethrough';
20
- import ToolbarPlugin from './toolbar';
21
- import Underline from '@material-ui/icons/FormatUnderlined';
22
- import compact from 'lodash/compact';
23
- import isEmpty from 'lodash/isEmpty';
24
- import SoftBreakPlugin from 'slate-soft-break';
25
- import debug from 'debug';
26
- import List from './list';
27
- import TablePlugin from './table';
28
- import RespAreaPlugin from './respArea';
29
- import HtmlPlugin from './html';
30
- import CSSPlugin from './css';
31
- import CustomPlugin from './customPlugin';
32
- import RenderingPlugin from './rendering';
33
- import TextAlign from './textAlign';
34
-
35
- const log = debug('@pie-lib:editable-html:plugins');
36
-
37
- export const SuperscriptIcon = () => (
38
- <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="none">
39
- <path
40
- 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"
41
- fill="currentColor"
42
- />
43
- </svg>
44
- );
45
-
46
- export const SubscriptIcon = () => (
47
- <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="none">
48
- <path
49
- 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"
50
- fill="currentColor"
51
- />
52
- </svg>
53
- );
54
-
55
- export const HeadingIcon = () => (
56
- <svg
57
- width="30"
58
- height="28"
59
- viewBox="0 0 30 28"
60
- fill="none"
61
- xmlns="http://www.w3.org/2000/svg"
62
- style={{ width: '20px', height: '18px' }}
63
- >
64
- <path
65
- 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"
66
- fill="currentColor"
67
- />
68
- </svg>
69
- );
70
- const STYLES_MAP = {
71
- h3: {
72
- fontSize: 'inherit',
73
- fontWeight: 'inherit',
74
- },
75
- blockquote: {
76
- background: '#f9f9f9',
77
- borderLeft: '5px solid #ccc',
78
- margin: '1.5em 10px',
79
- padding: '.5em 10px',
80
- },
81
- };
82
-
83
- function MarkHotkey(options) {
84
- const { type, key, icon, tag } = options;
85
-
86
- // Return our "plugin" object, containing the `onKeyDown` handler.
87
- return {
88
- name: type,
89
- toolbar: {
90
- isMark: true,
91
- type,
92
- icon,
93
- onToggle: (change) => {
94
- log('[onToggleMark] type: ', type);
95
- const { selection } = change.value;
96
-
97
- if (['blockquote', 'h3'].includes(type)) {
98
- const texts = change.value.document.getTextsAtRangeAsArray(selection);
99
- const onlyOneText = texts.length === 1;
100
- let hasMark = false;
101
- let sameMark = true;
102
-
103
- texts.forEach((t) => {
104
- const marks = t.getMarksAsArray();
105
- const markIsThere = marks.find((m) => m.type === type);
106
-
107
- if (!markIsThere) {
108
- // not all texts have this mark
109
- sameMark = false;
110
- } else {
111
- // at least one mark
112
- hasMark = true;
113
- }
114
- });
115
-
116
- const shouldContinue = onlyOneText || sameMark || !hasMark;
117
-
118
- if (!shouldContinue) {
119
- return change;
120
- }
121
-
122
- if (selection.startKey === selection.endKey && selection.anchorOffset === selection.focusOffset) {
123
- const textNode = change.value.document.getNode(selection.startKey);
124
-
125
- // select the whole line if there is no selection
126
- change.moveFocusTo(textNode.key, 0).moveAnchorTo(textNode.key, textNode.text.length);
127
-
128
- // remove toggle
129
- const hasMark = change.value.activeMarks.find((entry) => {
130
- return entry.type === type;
131
- });
132
-
133
- if (hasMark) {
134
- change.removeMark(hasMark);
135
- } else {
136
- const newMark = Mark.create(type);
137
-
138
- change.addMark(newMark);
139
- }
140
-
141
- // move focus to end of text
142
- return change
143
- .moveFocusTo(textNode.key, textNode.text.length)
144
- .moveAnchorTo(textNode.key, textNode.text.length);
145
- }
146
- }
147
-
148
- return change.toggleMark(type);
149
- },
150
- },
151
- renderMark(props) {
152
- if (props.mark.type === type) {
153
- const { data } = props.node || {};
154
- const jsonData = data?.toJSON() || {};
155
- const K = tag || type;
156
- const additionalStyles = STYLES_MAP[K];
157
-
158
- if (additionalStyles) {
159
- if (!jsonData.attributes) {
160
- jsonData.attributes = {};
161
- }
162
-
163
- jsonData.attributes.style = {
164
- ...jsonData.attributes.style,
165
- ...additionalStyles,
166
- };
167
- }
168
-
169
- return <K {...jsonData.attributes}>{props.children}</K>;
170
- }
171
- },
172
- onKeyDown(event, change) {
173
- // Check that the key pressed matches our `key` option.
174
- if (!event.metaKey || event.key != key) return;
175
-
176
- // Prevent the default characters from being inserted.
177
- event.preventDefault();
178
-
179
- // Toggle the mark `type`.
180
- change.toggleMark(type);
181
- return true;
182
- },
183
- };
184
- }
185
-
186
- export const ALL_PLUGINS = [
187
- 'bold',
188
- // 'code',
189
- 'html',
190
- 'extraCSSRules',
191
- 'italic',
192
- 'underline',
193
- 'strikethrough',
194
- 'bulleted-list',
195
- 'numbered-list',
196
- 'image',
197
- 'math',
198
- 'languageCharacters',
199
- 'text-align',
200
- 'blockquote',
201
- 'h3',
202
- 'table',
203
- 'video',
204
- 'audio',
205
- 'responseArea',
206
- 'redo',
207
- 'undo',
208
- 'superscript',
209
- 'subscript',
210
- ];
211
-
212
- export const DEFAULT_PLUGINS = ALL_PLUGINS.filter((plug) => !['responseArea', 'h3', 'blockquote'].includes(plug));
213
-
214
- export const EXTENSIONS_LIST = [
215
- GridOn,
216
- Bold,
217
- Italic,
218
- Strikethrough,
219
- Underline,
220
- SuperscriptIcon,
221
- SubscriptIcon,
222
- Image,
223
- HeadingIcon,
224
- BulletedListIcon,
225
- NumberedListIcon,
226
- FormatQuote,
227
- ];
228
-
229
- const ICON_MAP = {
230
- undo: Undo,
231
- redo: Redo,
232
- };
233
- function UndoRedo(type) {
234
- const IconToUse = ICON_MAP[type];
235
-
236
- return {
237
- name: type,
238
- toolbar: {
239
- type,
240
- icon: <IconToUse />,
241
- ariaLabel: type === 'undo' ? 'Undo (revert the last action)' : 'Redo (reapply the last undone action)',
242
- onClick: (value, onChange) => {
243
- const change = value.change();
244
-
245
- onChange(change[type]());
246
- },
247
- },
248
- };
249
- }
250
-
251
- function EnterHandlingPlugin() {
252
- return {
253
- name: 'enterHandling',
254
- onKeyDown: (event, change) => {
255
- if (Hotkeys.isSplitBlock(event) && !IS_IOS) {
256
- if (change.value.isInVoid) {
257
- return change.collapseToStartOfNextText();
258
- }
259
-
260
- change.splitBlock();
261
-
262
- const range = change.value.selection;
263
- const newBlock = change.value.document.getClosestBlock(range.startKey);
264
-
265
- if (newBlock.type !== 'paragraph') {
266
- change.setNodeByKey(newBlock.key, {
267
- type: 'paragraph',
268
- });
269
- }
270
-
271
- return change;
272
- }
273
-
274
- return undefined;
275
- },
276
- };
277
- }
278
-
279
- export const buildPlugins = (activePlugins, customPlugins, opts) => {
280
- log('[buildPlugins] opts: ', opts);
281
-
282
- activePlugins = activePlugins || DEFAULT_PLUGINS;
283
-
284
- const addIf = (key) => activePlugins.includes(key) && key;
285
-
286
- const imagePlugin = opts.image && opts.image.onDelete && ImagePlugin(opts.image);
287
- const mathPlugin = opts.math && MathPlugin(opts.math);
288
- const respAreaPlugin =
289
- opts.responseArea && opts.responseArea.type && RespAreaPlugin(opts.responseArea, compact([mathPlugin]));
290
- const cssPlugin = !isEmpty(opts.extraCSSRules) && CSSPlugin(opts.extraCSSRules);
291
-
292
- const languageCharactersPlugins = (opts?.languageCharacters || []).map((config) =>
293
- CharactersPlugin({
294
- ...config,
295
- keyPadCharacterRef: opts.keyPadCharacterRef,
296
- setKeypadInteraction: opts.setKeypadInteraction,
297
- }),
298
- );
299
-
300
- const tablePlugins = [imagePlugin, mathPlugin, respAreaPlugin, ...languageCharactersPlugins];
301
-
302
- if (opts.responseArea && opts.responseArea.type === 'math-templated') {
303
- tablePlugins.push(respAreaPlugin);
304
- }
305
-
306
- let builtCustomPlugins = [];
307
-
308
- customPlugins?.forEach((customPlugin) => {
309
- const { event, icon, iconType, iconAlt } = customPlugin || {};
310
-
311
- function isValidEventName(eventName) {
312
- // Check if eventName is a non-empty string
313
- if (typeof eventName !== 'string' || eventName.length === 0) {
314
- return false;
315
- }
316
-
317
- // Regular expression to match valid event names (only alphanumeric characters and underscore)
318
- const regex = /^[a-zA-Z0-9_]+$/;
319
-
320
- // Check if the eventName matches the regular expression
321
- return regex.test(eventName);
322
- }
323
-
324
- if (!isValidEventName(event)) {
325
- console.error(`The event name: ${event} is not a valid event name!`);
326
- return;
327
- }
328
-
329
- if (!icon && !iconType && !iconAlt) {
330
- console.error('Your custom button requires icon, iconType and iconAlt');
331
- return;
332
- }
333
-
334
- builtCustomPlugins.push(CustomPlugin('custom-plugin', customPlugin));
335
- });
336
-
337
- return compact([
338
- addIf('table', TablePlugin(opts.table, compact(tablePlugins))),
339
- addIf('bold', MarkHotkey({ key: 'b', type: 'bold', icon: <Bold />, tag: 'strong' })),
340
- // addIf('code', MarkHotkey({ key: '`', type: 'code', icon: <Code /> })),
341
- addIf('italic', MarkHotkey({ key: 'i', type: 'italic', icon: <Italic />, tag: 'em' })),
342
- addIf(
343
- 'strikethrough',
344
- MarkHotkey({
345
- key: '~',
346
- type: 'strikethrough',
347
- icon: <Strikethrough />,
348
- tag: 'del',
349
- }),
350
- ),
351
- addIf('underline', MarkHotkey({ key: 'u', type: 'underline', icon: <Underline />, tag: 'u' })),
352
- // icon should be modifies accordingly
353
- addIf('superscript', MarkHotkey({ type: 'sup', icon: <SuperscriptIcon />, tag: 'sup' })),
354
- // icon should be modifies accordingly
355
- addIf('subscript', MarkHotkey({ type: 'sub', icon: <SubscriptIcon />, tag: 'sub' })),
356
- addIf('image', imagePlugin),
357
- addIf('video', MediaPlugin('video', opts.media)),
358
- addIf('audio', MediaPlugin('audio', opts.media)),
359
- addIf('math', mathPlugin),
360
- ...languageCharactersPlugins.map((plugin) => addIf('languageCharacters', plugin)),
361
- addIf('text-align', TextAlign(opts.textAlign)),
362
- addIf('blockquote', MarkHotkey({ key: 'q', type: 'blockquote', icon: <FormatQuote />, tag: 'blockquote' })),
363
- addIf('h3', MarkHotkey({ key: 'h3', type: 'h3', icon: <HeadingIcon />, tag: 'h3' })),
364
- addIf('bulleted-list', List({ key: 'l', type: 'ul_list', icon: <BulletedListIcon /> })),
365
- addIf('numbered-list', List({ key: 'n', type: 'ol_list', icon: <NumberedListIcon /> })),
366
- addIf('undo', UndoRedo('undo')),
367
- addIf('redo', UndoRedo('redo')),
368
- ToolbarPlugin(opts.toolbar),
369
- SoftBreakPlugin({ shift: true }),
370
- ...builtCustomPlugins,
371
- addIf('responseArea', respAreaPlugin),
372
- cssPlugin,
373
- addIf('html', HtmlPlugin(opts.html)),
374
- EnterHandlingPlugin(),
375
- RenderingPlugin(),
376
- ]);
377
- };
@@ -1,54 +0,0 @@
1
- import React from 'react';
2
-
3
- import List, { serialization } from '../index';
4
- import debug from 'debug';
5
-
6
- const log = debug('@pie-lib:editable-html:test:plugins:list');
7
-
8
- describe('ListPlugin', () => {
9
- let next;
10
-
11
- describe('deserialize', () => {
12
- next = jest.fn();
13
-
14
- const assertDeserialize = (tagName, expectedType) => {
15
- it(`should deserialize ${tagName} to ${expectedType}`, () => {
16
- const out = serialization.deserialize({ tagName, children: [], childNodes: [] }, next);
17
-
18
- expect(out).toMatchObject({ object: 'block', type: expectedType });
19
- expect(next).toHaveBeenCalledWith([]);
20
- });
21
- };
22
- assertDeserialize('ul', 'ul_list');
23
- assertDeserialize('ol', 'ol_list');
24
- assertDeserialize('li', 'list_item');
25
- });
26
-
27
- describe('serialize', () => {
28
- const assertSerialize = (type, expectedType) => {
29
- it(`should serialize ${type} to ${expectedType}`, () => {
30
- const out = serialization.serialize({ object: 'block', type }, {});
31
- log('out: ', out);
32
- expect(out.type).toMatch(expectedType);
33
- });
34
- };
35
- assertSerialize('ul_list', 'ul');
36
- assertSerialize('ol_list', 'ol');
37
- assertSerialize('list_item', 'li');
38
- });
39
-
40
- describe('renderNode', () => {
41
- let plugin = List({});
42
-
43
- const assertRenderNode = (type, expectedType) => {
44
- it(`should renderNode ${type} to ${expectedType}`, () => {
45
- const out = plugin.renderNode({ node: { type } });
46
- expect(out.type).toMatch(expectedType);
47
- });
48
- };
49
-
50
- assertRenderNode('ul_list', 'ul');
51
- assertRenderNode('ol_list', 'ol');
52
- assertRenderNode('list_item', 'li');
53
- });
54
- });