@pie-lib/editable-html-tip-tap 1.0.0
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.
- package/CHANGELOG.json +32 -0
- package/CHANGELOG.md +2280 -0
- package/lib/__tests__/editor.test.js +470 -0
- package/lib/__tests__/serialization.test.js +246 -0
- package/lib/__tests__/utils.js +106 -0
- package/lib/block-tags.js +25 -0
- package/lib/constants.js +16 -0
- package/lib/editor.js +1356 -0
- package/lib/extensions/MediaView.js +112 -0
- package/lib/extensions/characters.js +65 -0
- package/lib/extensions/component.js +325 -0
- package/lib/extensions/css.js +252 -0
- package/lib/extensions/custom-toolbar-wrapper.js +124 -0
- package/lib/extensions/image.js +106 -0
- package/lib/extensions/math.js +330 -0
- package/lib/extensions/media.js +276 -0
- package/lib/extensions/responseArea.js +278 -0
- package/lib/index.js +1213 -0
- package/lib/old-index.js +269 -0
- package/lib/parse-html.js +16 -0
- package/lib/plugins/characters/custom-popper.js +73 -0
- package/lib/plugins/characters/index.js +305 -0
- package/lib/plugins/characters/utils.js +381 -0
- package/lib/plugins/css/icons/index.js +37 -0
- package/lib/plugins/css/index.js +390 -0
- package/lib/plugins/customPlugin/index.js +114 -0
- package/lib/plugins/html/icons/index.js +38 -0
- package/lib/plugins/html/index.js +81 -0
- package/lib/plugins/image/__tests__/component.test.js +51 -0
- package/lib/plugins/image/__tests__/image-toolbar-logic.test.js +56 -0
- package/lib/plugins/image/__tests__/image-toolbar.test.js +26 -0
- package/lib/plugins/image/__tests__/index.test.js +98 -0
- package/lib/plugins/image/__tests__/insert-image-handler.test.js +125 -0
- package/lib/plugins/image/__tests__/mock-change.js +25 -0
- package/lib/plugins/image/alt-dialog.js +129 -0
- package/lib/plugins/image/component.js +419 -0
- package/lib/plugins/image/image-toolbar.js +177 -0
- package/lib/plugins/image/index.js +263 -0
- package/lib/plugins/image/insert-image-handler.js +117 -0
- package/lib/plugins/index.js +413 -0
- package/lib/plugins/list/__tests__/index.test.js +79 -0
- package/lib/plugins/list/index.js +334 -0
- package/lib/plugins/math/__tests__/index.test.js +300 -0
- package/lib/plugins/math/index.js +454 -0
- package/lib/plugins/media/__tests__/index.test.js +71 -0
- package/lib/plugins/media/index.js +387 -0
- package/lib/plugins/media/media-dialog.js +709 -0
- package/lib/plugins/media/media-toolbar.js +101 -0
- package/lib/plugins/media/media-wrapper.js +93 -0
- package/lib/plugins/rendering/index.js +46 -0
- package/lib/plugins/respArea/drag-in-the-blank/choice.js +289 -0
- package/lib/plugins/respArea/drag-in-the-blank/index.js +94 -0
- package/lib/plugins/respArea/explicit-constructed-response/index.js +120 -0
- package/lib/plugins/respArea/icons/index.js +95 -0
- package/lib/plugins/respArea/index.js +341 -0
- package/lib/plugins/respArea/inline-dropdown/index.js +126 -0
- package/lib/plugins/respArea/math-templated/index.js +130 -0
- package/lib/plugins/respArea/utils.js +125 -0
- package/lib/plugins/table/CustomTablePlugin.js +133 -0
- package/lib/plugins/table/__tests__/index.test.js +442 -0
- package/lib/plugins/table/__tests__/table-toolbar.test.js +54 -0
- package/lib/plugins/table/icons/index.js +69 -0
- package/lib/plugins/table/index.js +483 -0
- package/lib/plugins/table/table-toolbar.js +187 -0
- package/lib/plugins/textAlign/icons/index.js +194 -0
- package/lib/plugins/textAlign/index.js +34 -0
- package/lib/plugins/toolbar/__tests__/default-toolbar.test.js +128 -0
- package/lib/plugins/toolbar/__tests__/editor-and-toolbar.test.js +51 -0
- package/lib/plugins/toolbar/__tests__/toolbar-buttons.test.js +54 -0
- package/lib/plugins/toolbar/__tests__/toolbar.test.js +120 -0
- package/lib/plugins/toolbar/default-toolbar.js +229 -0
- package/lib/plugins/toolbar/done-button.js +53 -0
- package/lib/plugins/toolbar/editor-and-toolbar.js +286 -0
- package/lib/plugins/toolbar/index.js +34 -0
- package/lib/plugins/toolbar/toolbar-buttons.js +194 -0
- package/lib/plugins/toolbar/toolbar.js +376 -0
- package/lib/plugins/utils.js +62 -0
- package/lib/serialization.js +677 -0
- package/lib/shared/alert-dialog.js +75 -0
- package/lib/theme.js +9 -0
- package/package.json +69 -0
- package/src/__tests__/editor.test.jsx +363 -0
- package/src/__tests__/serialization.test.js +291 -0
- package/src/__tests__/utils.js +36 -0
- package/src/block-tags.js +17 -0
- package/src/constants.js +7 -0
- package/src/editor.jsx +1197 -0
- package/src/extensions/characters.js +46 -0
- package/src/extensions/component.jsx +294 -0
- package/src/extensions/css.js +217 -0
- package/src/extensions/custom-toolbar-wrapper.jsx +100 -0
- package/src/extensions/image.js +55 -0
- package/src/extensions/math.js +259 -0
- package/src/extensions/media.js +182 -0
- package/src/extensions/responseArea.js +205 -0
- package/src/index.jsx +1462 -0
- package/src/old-index.jsx +162 -0
- package/src/parse-html.js +8 -0
- package/src/plugins/README.md +27 -0
- package/src/plugins/characters/custom-popper.js +48 -0
- package/src/plugins/characters/index.jsx +284 -0
- package/src/plugins/characters/utils.js +447 -0
- package/src/plugins/css/icons/index.jsx +17 -0
- package/src/plugins/css/index.jsx +340 -0
- package/src/plugins/customPlugin/index.jsx +85 -0
- package/src/plugins/html/icons/index.jsx +19 -0
- package/src/plugins/html/index.jsx +72 -0
- package/src/plugins/image/__tests__/__snapshots__/component.test.jsx.snap +51 -0
- package/src/plugins/image/__tests__/__snapshots__/image-toolbar-logic.test.jsx.snap +27 -0
- package/src/plugins/image/__tests__/__snapshots__/image-toolbar.test.jsx.snap +44 -0
- package/src/plugins/image/__tests__/component.test.jsx +41 -0
- package/src/plugins/image/__tests__/image-toolbar-logic.test.jsx +42 -0
- package/src/plugins/image/__tests__/image-toolbar.test.jsx +11 -0
- package/src/plugins/image/__tests__/index.test.js +95 -0
- package/src/plugins/image/__tests__/insert-image-handler.test.js +113 -0
- package/src/plugins/image/__tests__/mock-change.js +15 -0
- package/src/plugins/image/alt-dialog.jsx +82 -0
- package/src/plugins/image/component.jsx +343 -0
- package/src/plugins/image/image-toolbar.jsx +100 -0
- package/src/plugins/image/index.jsx +227 -0
- package/src/plugins/image/insert-image-handler.js +79 -0
- package/src/plugins/index.jsx +377 -0
- package/src/plugins/list/__tests__/index.test.js +54 -0
- package/src/plugins/list/index.jsx +305 -0
- package/src/plugins/math/__tests__/__snapshots__/index.test.jsx.snap +48 -0
- package/src/plugins/math/__tests__/index.test.jsx +245 -0
- package/src/plugins/math/index.jsx +379 -0
- package/src/plugins/media/__tests__/index.test.js +75 -0
- package/src/plugins/media/index.jsx +325 -0
- package/src/plugins/media/media-dialog.js +624 -0
- package/src/plugins/media/media-toolbar.jsx +56 -0
- package/src/plugins/media/media-wrapper.jsx +43 -0
- package/src/plugins/rendering/index.js +31 -0
- package/src/plugins/respArea/drag-in-the-blank/choice.jsx +215 -0
- package/src/plugins/respArea/drag-in-the-blank/index.jsx +70 -0
- package/src/plugins/respArea/explicit-constructed-response/index.jsx +92 -0
- package/src/plugins/respArea/icons/index.jsx +71 -0
- package/src/plugins/respArea/index.jsx +299 -0
- package/src/plugins/respArea/inline-dropdown/index.jsx +108 -0
- package/src/plugins/respArea/math-templated/index.jsx +104 -0
- package/src/plugins/respArea/utils.jsx +90 -0
- package/src/plugins/table/CustomTablePlugin.js +113 -0
- package/src/plugins/table/__tests__/__snapshots__/table-toolbar.test.jsx.snap +44 -0
- package/src/plugins/table/__tests__/index.test.jsx +401 -0
- package/src/plugins/table/__tests__/table-toolbar.test.jsx +42 -0
- package/src/plugins/table/icons/index.jsx +53 -0
- package/src/plugins/table/index.jsx +427 -0
- package/src/plugins/table/table-toolbar.jsx +136 -0
- package/src/plugins/textAlign/icons/index.jsx +114 -0
- package/src/plugins/textAlign/index.jsx +23 -0
- package/src/plugins/toolbar/__tests__/__snapshots__/default-toolbar.test.jsx.snap +923 -0
- package/src/plugins/toolbar/__tests__/__snapshots__/editor-and-toolbar.test.jsx.snap +20 -0
- package/src/plugins/toolbar/__tests__/__snapshots__/toolbar-buttons.test.jsx.snap +36 -0
- package/src/plugins/toolbar/__tests__/__snapshots__/toolbar.test.jsx.snap +46 -0
- package/src/plugins/toolbar/__tests__/default-toolbar.test.jsx +94 -0
- package/src/plugins/toolbar/__tests__/editor-and-toolbar.test.jsx +37 -0
- package/src/plugins/toolbar/__tests__/toolbar-buttons.test.jsx +51 -0
- package/src/plugins/toolbar/__tests__/toolbar.test.jsx +106 -0
- package/src/plugins/toolbar/default-toolbar.jsx +206 -0
- package/src/plugins/toolbar/done-button.jsx +38 -0
- package/src/plugins/toolbar/editor-and-toolbar.jsx +257 -0
- package/src/plugins/toolbar/index.jsx +23 -0
- package/src/plugins/toolbar/toolbar-buttons.jsx +138 -0
- package/src/plugins/toolbar/toolbar.jsx +338 -0
- package/src/plugins/utils.js +31 -0
- package/src/serialization.jsx +621 -0
- package/src/theme.js +1 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import debug from 'debug';
|
|
3
|
+
import isUndefined from 'lodash/isUndefined';
|
|
4
|
+
|
|
5
|
+
import InlineDropdown from './inline-dropdown';
|
|
6
|
+
import DragInTheBlank from './drag-in-the-blank';
|
|
7
|
+
import ExplicitConstructedResponse from './explicit-constructed-response';
|
|
8
|
+
import MathTemplated from './math-templated';
|
|
9
|
+
import { getDefaultElement } from './utils';
|
|
10
|
+
import { ToolbarIcon } from './icons';
|
|
11
|
+
|
|
12
|
+
const log = debug('@pie-lib:editable-html:plugins:respArea');
|
|
13
|
+
|
|
14
|
+
const lastIndexMap = {};
|
|
15
|
+
const elTypesMap = {
|
|
16
|
+
'inline-dropdown': 'inline_dropdown',
|
|
17
|
+
'explicit-constructed-response': 'explicit_constructed_response',
|
|
18
|
+
'math-templated': 'math_templated',
|
|
19
|
+
'drag-in-the-blank': 'drag_in_the_blank',
|
|
20
|
+
};
|
|
21
|
+
const elTypesArray = Object.values(elTypesMap);
|
|
22
|
+
|
|
23
|
+
export default function ResponseAreaPlugin(opts) {
|
|
24
|
+
const isOfCurrentType = (d) => d.type === opts.type || d.type === elTypesMap[opts.type];
|
|
25
|
+
|
|
26
|
+
const toolbar = {
|
|
27
|
+
icon: <ToolbarIcon />,
|
|
28
|
+
buttonStyles: {
|
|
29
|
+
margin: '0 20px 0 auto',
|
|
30
|
+
},
|
|
31
|
+
onClick: (value, onChange) => {
|
|
32
|
+
log('[toolbar] onClick');
|
|
33
|
+
const change = value.change();
|
|
34
|
+
const currentRespAreaList = change.value.document.filterDescendants(isOfCurrentType);
|
|
35
|
+
|
|
36
|
+
if (currentRespAreaList.size >= opts.maxResponseAreas) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const type = opts.type.replace(/-/g, '_');
|
|
41
|
+
const prevIndex = lastIndexMap[type];
|
|
42
|
+
const newIndex = prevIndex === 0 ? prevIndex : prevIndex + 1;
|
|
43
|
+
const newInline = getDefaultElement(opts, newIndex);
|
|
44
|
+
|
|
45
|
+
lastIndexMap[type] += 1;
|
|
46
|
+
|
|
47
|
+
if (newInline) {
|
|
48
|
+
if (change.value.selection.startKey || change.value.selection.endKey) {
|
|
49
|
+
change.insertInline(newInline);
|
|
50
|
+
} else {
|
|
51
|
+
// If the markup is empty and there's no focus
|
|
52
|
+
const lastText = value.document.getLastText();
|
|
53
|
+
|
|
54
|
+
if (!lastText) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const parentNode = value.document.getParent(lastText.key);
|
|
58
|
+
|
|
59
|
+
if (parentNode) {
|
|
60
|
+
const index = parentNode.nodes.indexOf(lastText.key);
|
|
61
|
+
|
|
62
|
+
if (parentNode.isVoid) return;
|
|
63
|
+
|
|
64
|
+
change.insertNodeByKey(parentNode.key, index + 1, newInline);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (newInline.type === 'drag_in_the_blank') {
|
|
69
|
+
const nextText = change.value.document.getNextText(newInline.key);
|
|
70
|
+
|
|
71
|
+
if (nextText) {
|
|
72
|
+
change.moveFocusTo(nextText.key, 0).moveAnchorTo(nextText.key, 0);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (newInline.type === 'math_templated') {
|
|
76
|
+
const nextText = change.value.document.getNextText(newInline.key);
|
|
77
|
+
|
|
78
|
+
if (nextText) {
|
|
79
|
+
change.moveFocusTo(nextText.key, 0).moveAnchorTo(nextText.key, 0);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
onChange(change);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
customToolbar: opts.respAreaToolbar,
|
|
87
|
+
supports: (node) => node.object === 'inline' && elTypesArray.indexOf(node.type) >= 0,
|
|
88
|
+
showDone: false,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
name: 'response_area',
|
|
93
|
+
toolbar,
|
|
94
|
+
filterPlugins: (node, plugins) => {
|
|
95
|
+
if (
|
|
96
|
+
node.type === 'explicit_constructed_response' ||
|
|
97
|
+
node.type === 'math_templated' ||
|
|
98
|
+
node.type === 'drag_in_the_blank'
|
|
99
|
+
) {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return plugins.filter((p) => p.name !== 'response_area');
|
|
104
|
+
},
|
|
105
|
+
deleteNode: (e, node, value, onChange) => {
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
|
|
108
|
+
const change = value.change().removeNodeByKey(node.key);
|
|
109
|
+
|
|
110
|
+
onChange(change);
|
|
111
|
+
},
|
|
112
|
+
renderNode(props) {
|
|
113
|
+
const { attributes, node: n, isFocused } = props;
|
|
114
|
+
|
|
115
|
+
if (n.type === 'explicit_constructed_response') {
|
|
116
|
+
const data = n.data.toJSON();
|
|
117
|
+
let error;
|
|
118
|
+
|
|
119
|
+
if (opts.error) {
|
|
120
|
+
error = opts.error();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<ExplicitConstructedResponse
|
|
125
|
+
attributes={attributes}
|
|
126
|
+
isFocused={isFocused}
|
|
127
|
+
value={data.value}
|
|
128
|
+
error={error && error[data.index] && error[data.index][0]}
|
|
129
|
+
/>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (n.type === 'math_templated') {
|
|
134
|
+
const data = n.data.toJSON();
|
|
135
|
+
let error;
|
|
136
|
+
|
|
137
|
+
if (opts.error) {
|
|
138
|
+
error = opts.error();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// add 1 to index to display R 1 instead of R 0
|
|
142
|
+
const keyToDisplay = `R ${parseInt(data.index) + 1}`;
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<MathTemplated
|
|
146
|
+
attributes={attributes}
|
|
147
|
+
keyToDisplay={keyToDisplay}
|
|
148
|
+
value={data.value || ''}
|
|
149
|
+
error={error && error[data.index] && error[data.index][0]}
|
|
150
|
+
/>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (n.type === 'drag_in_the_blank') {
|
|
155
|
+
const data = n.data.toJSON();
|
|
156
|
+
|
|
157
|
+
return <DragInTheBlank attributes={attributes} data={data} n={n} nodeProps={props} opts={opts} />;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (n.type === 'inline_dropdown') {
|
|
161
|
+
const data = n.data.toJSON();
|
|
162
|
+
|
|
163
|
+
return <InlineDropdown attributes={attributes} selectedItem={data.value} />;
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
onChange(change, editor) {
|
|
167
|
+
const type = opts.type.replace(/-/g, '_');
|
|
168
|
+
|
|
169
|
+
if (isUndefined(lastIndexMap[type])) {
|
|
170
|
+
lastIndexMap[type] = 0;
|
|
171
|
+
|
|
172
|
+
change.value.document.forEachDescendant((d) => {
|
|
173
|
+
if (d.type === type) {
|
|
174
|
+
const newIndex = parseInt(d.data.get('index'), 10);
|
|
175
|
+
|
|
176
|
+
if (newIndex > lastIndexMap[type]) {
|
|
177
|
+
lastIndexMap[type] = newIndex;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!editor.value) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const currentRespAreaList = change.value.document.filterDescendants(isOfCurrentType);
|
|
188
|
+
const oldRespAreaList = editor.value.document.filterDescendants(isOfCurrentType);
|
|
189
|
+
|
|
190
|
+
toolbar.disabled = currentRespAreaList.size >= opts.maxResponseAreas;
|
|
191
|
+
|
|
192
|
+
const arrayToFilter = oldRespAreaList.size > currentRespAreaList.size ? oldRespAreaList : currentRespAreaList;
|
|
193
|
+
const arrayToUseForFilter = arrayToFilter === oldRespAreaList ? currentRespAreaList : oldRespAreaList;
|
|
194
|
+
|
|
195
|
+
const elementsWithChangedStatus = arrayToFilter.filter(
|
|
196
|
+
(d) => !arrayToUseForFilter.find((e) => e.data.get('index') === d.data.get('index')),
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
if (elementsWithChangedStatus.size && oldRespAreaList.size > currentRespAreaList.size) {
|
|
200
|
+
opts.onHandleAreaChange(elementsWithChangedStatus);
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
onDrop(event, change, editor) {
|
|
204
|
+
const closestEl = event.target.closest('[data-key]');
|
|
205
|
+
const inline = editor.value.document.findDescendant((d) => d.key === closestEl.dataset.key);
|
|
206
|
+
|
|
207
|
+
if (inline.type === 'drag_in_the_blank') {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export const serialization = {
|
|
215
|
+
deserialize(el) {
|
|
216
|
+
const type = el.dataset && el.dataset.type;
|
|
217
|
+
|
|
218
|
+
switch (type) {
|
|
219
|
+
case 'inline_dropdown':
|
|
220
|
+
return {
|
|
221
|
+
object: 'inline',
|
|
222
|
+
type: 'inline_dropdown',
|
|
223
|
+
isVoid: true,
|
|
224
|
+
data: {
|
|
225
|
+
index: el.dataset.index,
|
|
226
|
+
value: el.dataset.value,
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
case 'explicit_constructed_response':
|
|
230
|
+
return {
|
|
231
|
+
object: 'inline',
|
|
232
|
+
type: 'explicit_constructed_response',
|
|
233
|
+
isVoid: true,
|
|
234
|
+
data: {
|
|
235
|
+
index: el.dataset.index,
|
|
236
|
+
value: el.dataset.value,
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
case 'math_templated':
|
|
240
|
+
return {
|
|
241
|
+
object: 'inline',
|
|
242
|
+
type: 'math_templated',
|
|
243
|
+
isVoid: true,
|
|
244
|
+
data: {
|
|
245
|
+
index: el.dataset.index,
|
|
246
|
+
value: el.dataset.value,
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
case 'drag_in_the_blank':
|
|
250
|
+
return {
|
|
251
|
+
object: 'inline',
|
|
252
|
+
type: 'drag_in_the_blank',
|
|
253
|
+
isVoid: true,
|
|
254
|
+
data: {
|
|
255
|
+
index: el.dataset.index,
|
|
256
|
+
id: el.dataset.id,
|
|
257
|
+
value: el.dataset.value,
|
|
258
|
+
inTable: el.dataset.inTable,
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
serialize(object) {
|
|
264
|
+
if (object.object !== 'inline') {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
switch (object.type) {
|
|
269
|
+
case 'inline_dropdown': {
|
|
270
|
+
const data = object.data.toJSON();
|
|
271
|
+
|
|
272
|
+
return <span data-type="inline_dropdown" data-index={data.index} data-value={data.value} />;
|
|
273
|
+
}
|
|
274
|
+
case 'explicit_constructed_response': {
|
|
275
|
+
const data = object.data.toJSON();
|
|
276
|
+
|
|
277
|
+
return <span data-type="explicit_constructed_response" data-index={data.index} data-value={data.value} />;
|
|
278
|
+
}
|
|
279
|
+
case 'math_templated': {
|
|
280
|
+
const data = object.data.toJSON();
|
|
281
|
+
|
|
282
|
+
return <span data-type="math_templated" data-index={data.index} data-value={data.value} />;
|
|
283
|
+
}
|
|
284
|
+
case 'drag_in_the_blank': {
|
|
285
|
+
const data = object.data.toJSON();
|
|
286
|
+
|
|
287
|
+
return (
|
|
288
|
+
<span
|
|
289
|
+
data-type="drag_in_the_blank"
|
|
290
|
+
data-index={data.index}
|
|
291
|
+
data-id={data.id}
|
|
292
|
+
data-value={data.value}
|
|
293
|
+
data-in-table={data.inTable}
|
|
294
|
+
/>
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { NodeViewWrapper } from '@tiptap/react';
|
|
4
|
+
import { Chevron } from '../icons';
|
|
5
|
+
|
|
6
|
+
const InlineDropdown = (props) => {
|
|
7
|
+
const { editor, node, getPos, options, selected } = props;
|
|
8
|
+
const { attrs: attributes } = node;
|
|
9
|
+
const { value, error } = attributes;
|
|
10
|
+
// TODO: Investigate
|
|
11
|
+
// Needed because items with values inside have different positioning for some reason
|
|
12
|
+
const html = value || '<div> </div>';
|
|
13
|
+
const toolbarRef = useRef(null);
|
|
14
|
+
const [showToolbar, setShowToolbar] = useState(false);
|
|
15
|
+
const InlineDropdownToolbar = options.respAreaToolbar(node, editor, () => {});
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
setShowToolbar(selected);
|
|
19
|
+
}, [selected]);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const handleClickOutside = (event) => {
|
|
23
|
+
if (
|
|
24
|
+
toolbarRef.current &&
|
|
25
|
+
!toolbarRef.current.contains(event.target) &&
|
|
26
|
+
!event.target.closest('[data-inline-node]')
|
|
27
|
+
) {
|
|
28
|
+
setShowToolbar(false);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
if (showToolbar) {
|
|
33
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
34
|
+
} else {
|
|
35
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
39
|
+
}, [showToolbar]);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<NodeViewWrapper
|
|
43
|
+
className="inline-dropdown"
|
|
44
|
+
data-selected={selected}
|
|
45
|
+
style={{
|
|
46
|
+
display: 'inline-flex',
|
|
47
|
+
height: '50px',
|
|
48
|
+
margin: '0 5px',
|
|
49
|
+
cursor: 'pointer',
|
|
50
|
+
}}
|
|
51
|
+
>
|
|
52
|
+
<div
|
|
53
|
+
style={{
|
|
54
|
+
display: 'inline-flex',
|
|
55
|
+
minWidth: '178px',
|
|
56
|
+
height: '36px',
|
|
57
|
+
background: '#FFF',
|
|
58
|
+
border: '1px solid #C0C3CF',
|
|
59
|
+
boxSizing: 'border-box',
|
|
60
|
+
borderRadius: '3px',
|
|
61
|
+
margin: '0 4px',
|
|
62
|
+
position: 'relative',
|
|
63
|
+
alignItems: 'center',
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
<div
|
|
67
|
+
style={{
|
|
68
|
+
flex: 1,
|
|
69
|
+
overflow: 'hidden',
|
|
70
|
+
padding: '0 25px 0 8px',
|
|
71
|
+
whiteSpace: 'nowrap',
|
|
72
|
+
textOverflow: 'ellipsis',
|
|
73
|
+
}}
|
|
74
|
+
>
|
|
75
|
+
<span
|
|
76
|
+
style={{
|
|
77
|
+
display: 'inline-block',
|
|
78
|
+
verticalAlign: 'middle',
|
|
79
|
+
}}
|
|
80
|
+
dangerouslySetInnerHTML={{
|
|
81
|
+
__html: html,
|
|
82
|
+
}}
|
|
83
|
+
/>
|
|
84
|
+
</div>
|
|
85
|
+
<Chevron
|
|
86
|
+
direction="down"
|
|
87
|
+
style={{
|
|
88
|
+
position: 'absolute',
|
|
89
|
+
top: '5px',
|
|
90
|
+
right: '5px',
|
|
91
|
+
}}
|
|
92
|
+
/>
|
|
93
|
+
</div>
|
|
94
|
+
{showToolbar && (
|
|
95
|
+
<div ref={toolbarRef} className="absolute z-50 bg-white shadow-lg rounded p-2" style={{ zIndex: 1 }}>
|
|
96
|
+
<InlineDropdownToolbar />
|
|
97
|
+
</div>
|
|
98
|
+
)}
|
|
99
|
+
</NodeViewWrapper>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
InlineDropdown.propTypes = {
|
|
104
|
+
attributes: PropTypes.object,
|
|
105
|
+
selectedItem: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export default InlineDropdown;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
4
|
+
import { mq } from '@pie-lib/math-input';
|
|
5
|
+
|
|
6
|
+
const MathTemplated = ({ attributes, value, classes, keyToDisplay }) => (
|
|
7
|
+
<span {...attributes} className={classes.spanContainer}>
|
|
8
|
+
<div className={classes.responseBox}>{keyToDisplay}</div>
|
|
9
|
+
<div className={classes.mathBlock}>
|
|
10
|
+
<mq.Static latex={value} />
|
|
11
|
+
</div>
|
|
12
|
+
</span>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
MathTemplated.propTypes = {
|
|
16
|
+
attributes: PropTypes.object,
|
|
17
|
+
classes: PropTypes.object.isRequired,
|
|
18
|
+
value: PropTypes.string,
|
|
19
|
+
keyToDisplay: PropTypes.string,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const styles = (theme) => ({
|
|
23
|
+
responseBox: {
|
|
24
|
+
background: theme.palette.grey['A100'],
|
|
25
|
+
color: theme.palette.grey['A700'],
|
|
26
|
+
display: 'inline-flex',
|
|
27
|
+
borderRight: '2px solid #C0C3CF',
|
|
28
|
+
boxSizing: 'border-box',
|
|
29
|
+
overflow: 'hidden',
|
|
30
|
+
fontSize: '12px',
|
|
31
|
+
minHeight: '36px',
|
|
32
|
+
height: '100%',
|
|
33
|
+
alignItems: 'center',
|
|
34
|
+
fontFamily: 'Symbola, Times New Roman, serif',
|
|
35
|
+
padding: '0 2px',
|
|
36
|
+
},
|
|
37
|
+
spanContainer: {
|
|
38
|
+
display: 'inline-flex',
|
|
39
|
+
border: '1px solid #C0C3CF',
|
|
40
|
+
margin: '1px 5px',
|
|
41
|
+
cursor: 'pointer',
|
|
42
|
+
alignItems: 'center',
|
|
43
|
+
justifyContent: 'center',
|
|
44
|
+
minWidth: '50px',
|
|
45
|
+
minHeight: '36px',
|
|
46
|
+
height: 'fit-content',
|
|
47
|
+
},
|
|
48
|
+
mathBlock: {
|
|
49
|
+
flex: 8,
|
|
50
|
+
color: 'var(--pie-text, black)',
|
|
51
|
+
padding: '4px !important',
|
|
52
|
+
display: 'flex',
|
|
53
|
+
alignItems: 'center',
|
|
54
|
+
justifyContent: 'center',
|
|
55
|
+
backgroundColor: 'var(--pie-background, rgba(255, 255, 255, 0))',
|
|
56
|
+
'& > .mq-math-mode sup.mq-nthroot': {
|
|
57
|
+
fontSize: '70% !important',
|
|
58
|
+
verticalAlign: '1em !important',
|
|
59
|
+
},
|
|
60
|
+
'& > .mq-math-mode .mq-sqrt-stem': {
|
|
61
|
+
borderTop: '0.07em solid',
|
|
62
|
+
marginLeft: '-1.5px',
|
|
63
|
+
marginTop: '-2px !important',
|
|
64
|
+
paddingTop: '5px !important',
|
|
65
|
+
},
|
|
66
|
+
'& .mq-overarrow-inner': {
|
|
67
|
+
paddingTop: '0 !important',
|
|
68
|
+
border: 'none !important',
|
|
69
|
+
},
|
|
70
|
+
'& .mq-overarrow.mq-arrow-both': {
|
|
71
|
+
marginTop: '0px',
|
|
72
|
+
minWidth: '1.23em',
|
|
73
|
+
'& *': {
|
|
74
|
+
lineHeight: '1 !important',
|
|
75
|
+
},
|
|
76
|
+
'&:before': {
|
|
77
|
+
top: '-0.4em',
|
|
78
|
+
left: '-1px',
|
|
79
|
+
},
|
|
80
|
+
'&:after': {
|
|
81
|
+
top: '0px !important',
|
|
82
|
+
position: 'absolute !important',
|
|
83
|
+
right: '-2px',
|
|
84
|
+
},
|
|
85
|
+
'&.mq-empty:after': {
|
|
86
|
+
top: '-0.45em',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
'& .mq-overarrow.mq-arrow-right': {
|
|
90
|
+
'&:before': {
|
|
91
|
+
top: '-0.4em',
|
|
92
|
+
right: '-1px',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
'& .mq-overarrow-inner-right': {
|
|
96
|
+
display: 'none !important',
|
|
97
|
+
},
|
|
98
|
+
'& .mq-overarrow-inner-left': {
|
|
99
|
+
display: 'none !important',
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
export default withStyles(styles)(MathTemplated);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom';
|
|
3
|
+
import { Inline } from 'slate';
|
|
4
|
+
import Snackbar from '@material-ui/core/Snackbar';
|
|
5
|
+
|
|
6
|
+
export const isNumber = (val) => !isNaN(parseFloat(val)) && isFinite(val);
|
|
7
|
+
|
|
8
|
+
export const insertSnackBar = (message) => {
|
|
9
|
+
const prevSnacks = document.querySelectorAll('.response-area-alert');
|
|
10
|
+
|
|
11
|
+
prevSnacks.forEach((s) => s.remove());
|
|
12
|
+
|
|
13
|
+
const newEl = document.createElement('div');
|
|
14
|
+
|
|
15
|
+
newEl.className = 'response-area-alert';
|
|
16
|
+
|
|
17
|
+
const el = (
|
|
18
|
+
<Snackbar
|
|
19
|
+
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
|
|
20
|
+
open={true}
|
|
21
|
+
ContentProps={{
|
|
22
|
+
'aria-describedby': 'message-id',
|
|
23
|
+
}}
|
|
24
|
+
message={<span id="message-id">{message}</span>}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
ReactDOM.render(el, newEl);
|
|
29
|
+
|
|
30
|
+
document.body.appendChild(newEl);
|
|
31
|
+
|
|
32
|
+
setTimeout(() => {
|
|
33
|
+
newEl.remove();
|
|
34
|
+
}, 2000);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const defaultECR = (index) =>
|
|
38
|
+
Inline.create({
|
|
39
|
+
type: 'explicit_constructed_response',
|
|
40
|
+
isVoid: true,
|
|
41
|
+
data: {
|
|
42
|
+
index,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
export const defaultMT = (index) =>
|
|
46
|
+
Inline.create({
|
|
47
|
+
type: 'math_templated',
|
|
48
|
+
isVoid: true,
|
|
49
|
+
data: {
|
|
50
|
+
index,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export const defaultDIB = (opts, index) =>
|
|
55
|
+
Inline.create({
|
|
56
|
+
type: 'drag_in_the_blank',
|
|
57
|
+
isVoid: true,
|
|
58
|
+
data: {
|
|
59
|
+
index,
|
|
60
|
+
duplicates: opts.options.duplicates,
|
|
61
|
+
value: null,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export const defaultIDD = (index) =>
|
|
66
|
+
Inline.create({
|
|
67
|
+
object: 'inline',
|
|
68
|
+
type: 'inline_dropdown',
|
|
69
|
+
isVoid: true,
|
|
70
|
+
data: {
|
|
71
|
+
index,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export const getDefaultElement = (opts, index) => {
|
|
76
|
+
switch (opts.type) {
|
|
77
|
+
case 'explicit-constructed-response':
|
|
78
|
+
return defaultECR(index);
|
|
79
|
+
|
|
80
|
+
case 'math-templated':
|
|
81
|
+
return defaultMT(index);
|
|
82
|
+
|
|
83
|
+
case 'drag-in-the-blank':
|
|
84
|
+
return defaultDIB(opts, index);
|
|
85
|
+
|
|
86
|
+
default:
|
|
87
|
+
// inline-dropdown
|
|
88
|
+
return defaultIDD(index);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import EditTable from 'slate-edit-table';
|
|
2
|
+
import { isSelectionInTable } from 'slate-edit-table/dist/utils';
|
|
3
|
+
import { onEnter, onModEnter, onTab, onUpDown } from 'slate-edit-table/dist/handlers';
|
|
4
|
+
import TableOptions from 'slate-edit-table/dist/options';
|
|
5
|
+
import { clearCell } from 'slate-edit-table/dist/changes';
|
|
6
|
+
|
|
7
|
+
function onBackspace(event, change, editor, opts) {
|
|
8
|
+
const { value } = change;
|
|
9
|
+
const { startBlock, endBlock, selection, document } = value;
|
|
10
|
+
|
|
11
|
+
const startCell = document.getClosest(startBlock.key, opts.isCell);
|
|
12
|
+
const endCell = document.getClosest(endBlock.key, opts.isCell);
|
|
13
|
+
|
|
14
|
+
const startBlockIndex = startCell?.nodes?.findIndex((block) => block.key == startBlock.key);
|
|
15
|
+
|
|
16
|
+
// If a cursor is collapsed at the start of the first block, do nothing
|
|
17
|
+
if (startBlockIndex === 0 && selection.isAtStartOf(startBlock)) {
|
|
18
|
+
if (startBlock.isVoid) {
|
|
19
|
+
// Delete the block normally if it is a void block
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
return change;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// If "normal" deletion, we continue
|
|
28
|
+
if (startCell === endCell) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// If cursor is between multiple blocks,
|
|
33
|
+
// we clear the content of the cells.
|
|
34
|
+
event.preventDefault();
|
|
35
|
+
|
|
36
|
+
const { blocks } = value;
|
|
37
|
+
|
|
38
|
+
// Get all cells that contains the selection
|
|
39
|
+
const cells = blocks
|
|
40
|
+
.map((node) =>
|
|
41
|
+
node.type === opts.typeCell ? node : document.getClosest(node.key, (a) => a.type === opts.typeCell),
|
|
42
|
+
)
|
|
43
|
+
.toSet();
|
|
44
|
+
|
|
45
|
+
// If the cursor is at the very end of the first cell, ignore it.
|
|
46
|
+
// If the cursor is at the very start of the last cell, ignore it.
|
|
47
|
+
// This behavior is to compensate hanging selection behaviors:
|
|
48
|
+
// https://github.com/ianstormtaylor/slate/pull/1605
|
|
49
|
+
const ignoreFirstCell = value.selection.collapseToStart().isAtEndOf(cells.first());
|
|
50
|
+
const ignoreLastCell = value.selection.collapseToEnd().isAtStartOf(cells.last());
|
|
51
|
+
|
|
52
|
+
let cellsToClear = cells;
|
|
53
|
+
if (ignoreFirstCell) {
|
|
54
|
+
cellsToClear = cellsToClear.rest();
|
|
55
|
+
}
|
|
56
|
+
if (ignoreLastCell) {
|
|
57
|
+
cellsToClear = cellsToClear.butLast();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Clear all the selection
|
|
61
|
+
cellsToClear.forEach((cell) => clearCell(opts, change, cell));
|
|
62
|
+
|
|
63
|
+
// Update the selection properly, and avoid reset of selection
|
|
64
|
+
const updatedStartCell = change.value.document.getDescendant(cellsToClear.first().key);
|
|
65
|
+
return change.collapseToStartOf(updatedStartCell);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const KEY_ENTER = 'Enter';
|
|
69
|
+
const KEY_TAB = 'Tab';
|
|
70
|
+
const KEY_BACKSPACE = 'Backspace';
|
|
71
|
+
const KEY_DOWN = 'ArrowDown';
|
|
72
|
+
const KEY_UP = 'ArrowUp';
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* User is pressing a key in the editor
|
|
76
|
+
*/
|
|
77
|
+
function onKeyDown(opts, event, change, editor) {
|
|
78
|
+
// Only handle events in cells
|
|
79
|
+
if (!isSelectionInTable(opts, change.value)) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Build arguments list
|
|
84
|
+
const args = [event, change, editor, opts];
|
|
85
|
+
|
|
86
|
+
switch (event.key) {
|
|
87
|
+
case KEY_ENTER:
|
|
88
|
+
if (event.metaKey && opts.exitBlockType) {
|
|
89
|
+
return onModEnter(...args);
|
|
90
|
+
}
|
|
91
|
+
return onEnter(...args);
|
|
92
|
+
|
|
93
|
+
case KEY_TAB:
|
|
94
|
+
return onTab(...args);
|
|
95
|
+
case KEY_BACKSPACE:
|
|
96
|
+
return onBackspace(...args);
|
|
97
|
+
case KEY_DOWN:
|
|
98
|
+
case KEY_UP:
|
|
99
|
+
return onUpDown(...args);
|
|
100
|
+
default:
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export default (opts) => {
|
|
106
|
+
const core = EditTable(opts);
|
|
107
|
+
|
|
108
|
+
const tableOpts = new TableOptions(opts);
|
|
109
|
+
|
|
110
|
+
core.onKeyDown = onKeyDown.bind(null, tableOpts);
|
|
111
|
+
|
|
112
|
+
return core;
|
|
113
|
+
};
|