@pie-lib/editable-html-tip-tap 1.1.1-next.2 → 1.1.1-next.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.
- package/lib/__tests__/EditableHtml.test.js +1 -11
- package/lib/__tests__/constants.test.js +1 -8
- package/lib/__tests__/extensions.test.js +1 -6
- package/lib/__tests__/index.test.js +1 -12
- package/lib/components/CharacterPicker.js +5 -24
- package/lib/components/CharacterPicker.js.map +1 -1
- package/lib/components/EditableHtml.js +1 -18
- package/lib/components/EditableHtml.js.map +1 -1
- package/lib/components/MenuBar.js +1 -18
- package/lib/components/TiptapContainer.js +26 -23
- package/lib/components/TiptapContainer.js.map +1 -1
- package/lib/components/__tests__/AltDialog.test.js +1 -1
- package/lib/components/__tests__/CharacterPicker.test.js +4 -12
- package/lib/components/__tests__/DragInTheBlank.test.js +1 -15
- package/lib/components/__tests__/ExplicitConstructedResponse.test.js +1 -11
- package/lib/components/__tests__/ImageToolbar.test.js +1 -11
- package/lib/components/__tests__/InlineDropdown.test.js +1 -11
- package/lib/components/__tests__/MediaDialog.test.js +1 -3
- package/lib/components/__tests__/MediaToolbar.test.js +1 -1
- package/lib/components/__tests__/MenuBar.test.js +1 -1
- package/lib/components/__tests__/TableIcons.test.js +1 -1
- package/lib/components/__tests__/TextAlign.test.js +1 -8
- package/lib/components/__tests__/characterUtils.test.js +1 -12
- package/lib/components/__tests__/choice.test.js +1 -1
- package/lib/components/__tests__/done-button.test.js +1 -1
- package/lib/components/__tests__/toolbar-buttons.test.js +1 -1
- package/lib/components/characters/characterUtils.js +1 -3
- package/lib/components/characters/custom-popper.js +1 -2
- package/lib/components/common/done-button.js +1 -2
- package/lib/components/common/toolbar-buttons.js +1 -13
- package/lib/components/icons/CssIcon.js +1 -2
- package/lib/components/icons/RespArea.js +1 -11
- package/lib/components/icons/TableIcons.js +1 -2
- package/lib/components/icons/TextAlign.js +1 -8
- package/lib/components/image/AltDialog.js +1 -6
- package/lib/components/image/ImageToolbar.js +1 -14
- package/lib/components/image/InsertImageHandler.js +1 -11
- package/lib/components/media/MediaDialog.js +1 -20
- package/lib/components/media/MediaToolbar.js +1 -3
- package/lib/components/media/MediaWrapper.js +1 -12
- package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js +3 -13
- package/lib/components/respArea/DragInTheBlank/choice.js +1 -9
- package/lib/components/respArea/ExplicitConstructedResponse.js +1 -9
- package/lib/components/respArea/InlineDropdown.js +1 -9
- package/lib/components/respArea/ToolbarIcon.js +1 -11
- package/lib/constants.js +1 -2
- package/lib/extensions/__tests__/css.test.js +1 -5
- package/lib/extensions/__tests__/custom-toolbar-wrapper.test.js +1 -11
- package/lib/extensions/__tests__/extended-table.test.js +1 -23
- package/lib/extensions/__tests__/image-component.test.js +1 -11
- package/lib/extensions/__tests__/image.test.js +1 -15
- package/lib/extensions/__tests__/media.test.js +1 -5
- package/lib/extensions/__tests__/responseArea.test.js +1 -3
- package/lib/extensions/css.js +1 -12
- package/lib/extensions/custom-toolbar-wrapper.js +2 -20
- package/lib/extensions/extended-table.js +2 -6
- package/lib/extensions/extended-table.js.map +1 -1
- package/lib/extensions/image-component.js +5 -22
- package/lib/extensions/image.js +1 -12
- package/lib/extensions/image.js.map +1 -1
- package/lib/extensions/index.js +4 -13
- package/lib/extensions/math.js +1 -17
- package/lib/extensions/media.js +1 -16
- package/lib/extensions/responseArea.js +1 -24
- package/lib/index.js +1 -8
- package/lib/styles/editorContainerStyles.js +1 -3
- package/lib/theme.js +1 -2
- package/lib/utils/size.js +1 -7
- package/package.json +9 -9
- package/src/__tests__/constants.test.js +1 -2
- package/src/__tests__/extensions.test.js +1 -1
- package/src/__tests__/index.test.jsx +1 -1
- package/src/components/CharacterPicker.jsx +2 -6
- package/src/components/EditableHtml.jsx +1 -1
- package/src/components/MenuBar.jsx +32 -186
- package/src/components/TiptapContainer.jsx +25 -5
- package/src/components/__tests__/AltDialog.test.jsx +1 -1
- package/src/components/__tests__/CharacterPicker.test.jsx +5 -3
- package/src/components/__tests__/DragInTheBlank.test.jsx +1 -1
- package/src/components/__tests__/ExplicitConstructedResponse.test.jsx +1 -1
- package/src/components/__tests__/ImageToolbar.test.jsx +1 -1
- package/src/components/__tests__/InlineDropdown.test.jsx +1 -1
- package/src/components/__tests__/MediaDialog.test.jsx +1 -1
- package/src/components/__tests__/MediaToolbar.test.jsx +1 -1
- package/src/components/__tests__/MenuBar.test.jsx +1 -1
- package/src/components/__tests__/TableIcons.test.jsx +1 -1
- package/src/components/__tests__/TextAlign.test.jsx +2 -2
- package/src/components/__tests__/choice.test.jsx +1 -1
- package/src/components/__tests__/done-button.test.jsx +2 -2
- package/src/components/__tests__/toolbar-buttons.test.jsx +2 -2
- package/src/components/image/AltDialog.jsx +1 -1
- package/src/components/image/InsertImageHandler.js +1 -1
- package/src/components/media/MediaDialog.js +4 -15
- package/src/components/respArea/DragInTheBlank/DragInTheBlank.jsx +1 -1
- package/src/components/respArea/DragInTheBlank/choice.jsx +34 -27
- package/src/components/respArea/ExplicitConstructedResponse.jsx +1 -1
- package/src/components/respArea/InlineDropdown.jsx +2 -3
- package/src/extensions/__tests__/custom-toolbar-wrapper.test.jsx +1 -1
- package/src/extensions/__tests__/extended-table.test.js +0 -21
- package/src/extensions/__tests__/image-component.test.jsx +1 -1
- package/src/extensions/__tests__/media.test.js +1 -1
- package/src/extensions/__tests__/responseArea.test.js +2 -2
- package/src/extensions/css.js +25 -23
- package/src/extensions/custom-toolbar-wrapper.jsx +1 -2
- package/src/extensions/extended-table.js +3 -1
- package/src/extensions/image-component.jsx +2 -2
- package/src/extensions/image.js +1 -1
- package/src/extensions/index.js +1 -3
- package/src/extensions/math.js +29 -27
- package/src/extensions/media.js +12 -14
- package/src/extensions/responseArea.js +91 -86
- package/src/styles/editorContainerStyles.js +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { useDraggable, useDroppable } from '@dnd-kit/core';
|
|
4
4
|
import { color } from '@pie-lib/render-ui';
|
|
@@ -107,38 +107,45 @@ BlankContent.propTypes = {
|
|
|
107
107
|
};
|
|
108
108
|
|
|
109
109
|
function DragDropChoice({ value, disabled, instanceId, children, n, onChange, removeResponse, duplicates, pos }) {
|
|
110
|
-
const {
|
|
111
|
-
|
|
110
|
+
const {
|
|
111
|
+
attributes: dragAttributes,
|
|
112
|
+
listeners: dragListeners,
|
|
113
|
+
setNodeRef: setDragNodeRef,
|
|
114
|
+
isDragging,
|
|
115
|
+
} = useDraggable({
|
|
116
|
+
id: `drag-${n.index}`,
|
|
117
|
+
disabled: disabled || !value?.value,
|
|
118
|
+
data: {
|
|
112
119
|
id: `drag-${n.index}`,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
onDrop: (draggedData, dropData) => {
|
|
125
|
-
// check if we're dropping into a blank
|
|
126
|
-
const isValidBlank = dropData?.type === 'drag-in-the-blank-drop-choice';
|
|
127
|
-
|
|
128
|
-
if (!isValidBlank) return;
|
|
120
|
+
value,
|
|
121
|
+
instanceId,
|
|
122
|
+
n,
|
|
123
|
+
pos,
|
|
124
|
+
opts: { duplicates },
|
|
125
|
+
type: 'drag-in-the-blank-placed-choice',
|
|
126
|
+
fromChoice: !value,
|
|
127
|
+
onRemove: (draggedData) => removeResponse(draggedData),
|
|
128
|
+
onDrop: (draggedData, dropData) => {
|
|
129
|
+
// check if we're dropping into a blank
|
|
130
|
+
const isValidBlank = dropData?.type === 'drag-in-the-blank-drop-choice';
|
|
129
131
|
|
|
130
|
-
|
|
131
|
-
onChange(draggedData);
|
|
132
|
+
if (!isValidBlank) return;
|
|
132
133
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
// place into blank
|
|
135
|
+
onChange(draggedData);
|
|
136
|
+
|
|
137
|
+
if (!duplicates && draggedData.fromChoice) {
|
|
138
|
+
removeResponse(draggedData);
|
|
139
|
+
}
|
|
137
140
|
},
|
|
138
141
|
},
|
|
139
|
-
);
|
|
142
|
+
});
|
|
140
143
|
|
|
141
|
-
const {
|
|
144
|
+
const {
|
|
145
|
+
setNodeRef: setDropNodeRef,
|
|
146
|
+
isOver,
|
|
147
|
+
active: dragItem,
|
|
148
|
+
} = useDroppable({
|
|
142
149
|
id: `drop-${n.index}`,
|
|
143
150
|
data: {
|
|
144
151
|
type: 'drag-in-the-blank-drop-choice',
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useState } from
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { NodeViewWrapper } from '@tiptap/react';
|
|
4
4
|
import { Chevron } from '../icons/RespArea';
|
|
5
|
-
import ReactDOM from
|
|
6
|
-
import { MathToolbar } from "@pie-lib/math-toolbar";
|
|
5
|
+
import ReactDOM from 'react-dom';
|
|
7
6
|
|
|
8
7
|
const InlineDropdown = (props) => {
|
|
9
8
|
const { editor, node, getPos, options, selected } = props;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { fireEvent, render } from '@testing-library/react';
|
|
3
3
|
import CustomToolbarWrapper from '../custom-toolbar-wrapper';
|
|
4
4
|
|
|
5
5
|
jest.mock('../../components/common/done-button', () => ({
|
|
@@ -32,10 +32,6 @@ describe('ExtendedTable', () => {
|
|
|
32
32
|
expect(mockParent).toHaveBeenCalledWith(props);
|
|
33
33
|
expect(result[0]).toBe('table');
|
|
34
34
|
expect(result[1]).toHaveProperty('style');
|
|
35
|
-
expect(result[1].style).toContain('width: 100%');
|
|
36
|
-
expect(result[1].style).toContain('table-layout: fixed');
|
|
37
|
-
expect(result[1].style).toContain('border-collapse: collapse');
|
|
38
|
-
expect(result[1].border).toBe('1');
|
|
39
35
|
});
|
|
40
36
|
|
|
41
37
|
it('renders table with custom border', () => {
|
|
@@ -54,23 +50,6 @@ describe('ExtendedTable', () => {
|
|
|
54
50
|
expect(result[1].border).toBe('2');
|
|
55
51
|
});
|
|
56
52
|
|
|
57
|
-
it('preserves existing styles', () => {
|
|
58
|
-
const props = {
|
|
59
|
-
HTMLAttributes: { border: '1' },
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const mockParent = jest.fn(() => ['table', { style: 'margin: 10px;' }]);
|
|
63
|
-
|
|
64
|
-
const context = {
|
|
65
|
-
parent: mockParent,
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const result = ExtendedTable.renderHTML.call(context, props);
|
|
69
|
-
|
|
70
|
-
expect(result[1].style).toContain('margin: 10px;');
|
|
71
|
-
expect(result[1].style).toContain('width: 100%');
|
|
72
|
-
});
|
|
73
|
-
|
|
74
53
|
it('handles styles ending with semicolon', () => {
|
|
75
54
|
const props = {
|
|
76
55
|
HTMLAttributes: {},
|
package/src/extensions/css.js
CHANGED
|
@@ -188,31 +188,33 @@ export const CSSMark = Mark.create({
|
|
|
188
188
|
|
|
189
189
|
addCommands() {
|
|
190
190
|
return {
|
|
191
|
-
setCSSClass:
|
|
192
|
-
|
|
193
|
-
|
|
191
|
+
setCSSClass:
|
|
192
|
+
(className) =>
|
|
193
|
+
({ commands }) => {
|
|
194
|
+
return commands.setMark(this.name, { class: className });
|
|
195
|
+
},
|
|
194
196
|
|
|
195
|
-
unsetCSSClass:
|
|
196
|
-
|
|
197
|
-
|
|
197
|
+
unsetCSSClass:
|
|
198
|
+
() =>
|
|
199
|
+
({ commands }) => {
|
|
200
|
+
return commands.unsetMark(this.name);
|
|
201
|
+
},
|
|
198
202
|
|
|
199
|
-
openCSSClassDialog:
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
.focus()
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
});
|
|
215
|
-
},
|
|
203
|
+
openCSSClassDialog:
|
|
204
|
+
() =>
|
|
205
|
+
({ editor }) => {
|
|
206
|
+
insertDialog({
|
|
207
|
+
editor,
|
|
208
|
+
selectedText: editor.state.doc.textBetween(editor.state.selection.from, editor.state.selection.to),
|
|
209
|
+
parentNode: editor.state.selection.$from.nodeAfter,
|
|
210
|
+
opts: this.options.extraCSSRules,
|
|
211
|
+
callback: (className) => {
|
|
212
|
+
if (className) {
|
|
213
|
+
editor.chain().focus().setCSSClass(className).run();
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
},
|
|
216
218
|
};
|
|
217
219
|
},
|
|
218
220
|
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import IconButton from '@mui/material/IconButton';
|
|
3
3
|
import Delete from '@mui/icons-material/Delete';
|
|
4
|
-
import classNames from 'classnames';
|
|
5
4
|
import { PIE_TOOLBAR__CLASS } from '../constants';
|
|
6
5
|
import { styled } from '@mui/material/styles';
|
|
7
6
|
import { DoneButton } from '../components/common/done-button';
|
|
@@ -12,7 +12,9 @@ const ExtendedTable = Table.extend({
|
|
|
12
12
|
|
|
13
13
|
const previousStyle = `${originalTable[1].style}${originalTable[1].style.match(/.*; */) ? '' : ';'}`;
|
|
14
14
|
|
|
15
|
-
originalTable[1].style = `${previousStyle}
|
|
15
|
+
originalTable[1].style = `${previousStyle}
|
|
16
|
+
color: var(--pie-text, black);
|
|
17
|
+
background-color: var(--pie-background, rgba(255, 255, 255))`;
|
|
16
18
|
originalTable[1].border = border ? border : '1';
|
|
17
19
|
|
|
18
20
|
return originalTable;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import isEqual from 'lodash
|
|
3
|
+
import { isEqual } from 'lodash-es';
|
|
4
4
|
import debug from 'debug';
|
|
5
5
|
import LinearProgress from '@mui/material/LinearProgress';
|
|
6
6
|
import { styled } from '@mui/material/styles';
|
package/src/extensions/image.js
CHANGED
package/src/extensions/index.js
CHANGED
package/src/extensions/math.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
2
|
import ReactDOM from 'react-dom';
|
|
3
|
-
import {
|
|
4
|
-
import { NodeViewWrapper,
|
|
5
|
-
import { Plugin, PluginKey,
|
|
3
|
+
import { Node } from '@tiptap/core';
|
|
4
|
+
import { NodeViewWrapper, ReactNodeViewRenderer } from '@tiptap/react';
|
|
5
|
+
import { NodeSelection, Plugin, PluginKey, TextSelection } from 'prosemirror-state';
|
|
6
6
|
import { MathPreview, MathToolbar } from '@pie-lib/math-toolbar';
|
|
7
|
-
import { wrapMath
|
|
7
|
+
import { wrapMath } from '@pie-lib/math-rendering';
|
|
8
8
|
|
|
9
9
|
const ensureTextAfterMathPluginKey = new PluginKey('ensureTextAfterMath');
|
|
10
10
|
|
|
@@ -118,30 +118,32 @@ export const MathNode = Node.create({
|
|
|
118
118
|
|
|
119
119
|
addCommands() {
|
|
120
120
|
return {
|
|
121
|
-
insertMath:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
121
|
+
insertMath:
|
|
122
|
+
(latex = '') =>
|
|
123
|
+
({ tr, editor, dispatch }) => {
|
|
124
|
+
const { state } = editor.view;
|
|
125
|
+
const node = state.schema.nodes.math.create({
|
|
126
|
+
latex,
|
|
127
|
+
});
|
|
128
|
+
const { selection } = state;
|
|
129
|
+
|
|
130
|
+
// The inserted node is typically just before the cursor
|
|
131
|
+
const pos = selection.$from.pos;
|
|
132
|
+
|
|
133
|
+
tr.insert(pos, node);
|
|
134
|
+
|
|
135
|
+
if (node?.type?.name === this.name) {
|
|
136
|
+
// Create a NodeSelection from the current doc
|
|
137
|
+
const sel = NodeSelection.create(tr.doc, selection.$from.pos);
|
|
138
|
+
|
|
139
|
+
// Build a fresh transaction from the current state and set the selection
|
|
140
|
+
tr.setSelection(sel);
|
|
141
|
+
}
|
|
140
142
|
|
|
141
|
-
|
|
143
|
+
dispatch(tr);
|
|
142
144
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
+
return true;
|
|
146
|
+
},
|
|
145
147
|
// insertMath: (latex = '') => ({ commands }) => {
|
|
146
148
|
// return commands.insertContent({
|
|
147
149
|
// type: this.name,
|
package/src/extensions/media.js
CHANGED
|
@@ -78,12 +78,16 @@ export const Media = Node.create({
|
|
|
78
78
|
|
|
79
79
|
addCommands() {
|
|
80
80
|
return {
|
|
81
|
-
insertMedia:
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
insertMedia:
|
|
82
|
+
(attrs) =>
|
|
83
|
+
({ commands }) => {
|
|
84
|
+
return commands.insertContent({ type: this.name, attrs });
|
|
85
|
+
},
|
|
86
|
+
updateMedia:
|
|
87
|
+
(attrs) =>
|
|
88
|
+
({ commands }) => {
|
|
89
|
+
return commands.updateAttributes(this.name, attrs);
|
|
90
|
+
},
|
|
87
91
|
};
|
|
88
92
|
},
|
|
89
93
|
|
|
@@ -143,10 +147,7 @@ export default function MediaNodeView({ editor, node, updateAttributes, deleteNo
|
|
|
143
147
|
updateAttributes(data);
|
|
144
148
|
}
|
|
145
149
|
|
|
146
|
-
editor
|
|
147
|
-
.chain()
|
|
148
|
-
.focus()
|
|
149
|
-
.run();
|
|
150
|
+
editor.chain().focus().run();
|
|
150
151
|
},
|
|
151
152
|
});
|
|
152
153
|
};
|
|
@@ -163,10 +164,7 @@ export default function MediaNodeView({ editor, node, updateAttributes, deleteNo
|
|
|
163
164
|
deleteNode();
|
|
164
165
|
}
|
|
165
166
|
|
|
166
|
-
editor
|
|
167
|
-
.chain()
|
|
168
|
-
.focus()
|
|
169
|
-
.run();
|
|
167
|
+
editor.chain().focus().run();
|
|
170
168
|
},
|
|
171
169
|
});
|
|
172
170
|
}, []);
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Plugin, PluginKey, TextSelection
|
|
2
|
+
import { NodeSelection, Plugin, PluginKey, TextSelection } from 'prosemirror-state';
|
|
3
3
|
import { Extension } from '@tiptap/core';
|
|
4
4
|
import { Node, ReactNodeViewRenderer } from '@tiptap/react';
|
|
5
5
|
import ExplicitConstructedResponse from '../components/respArea/ExplicitConstructedResponse';
|
|
6
6
|
import DragInTheBlank from '../components/respArea/DragInTheBlank/DragInTheBlank';
|
|
7
7
|
import InlineDropdown from '../components/respArea/InlineDropdown';
|
|
8
|
-
import PropTypes from 'prop-types';
|
|
9
8
|
|
|
10
9
|
const lastIndexMap = {};
|
|
11
10
|
|
|
@@ -130,94 +129,100 @@ export const ResponseAreaExtension = Extension.create({
|
|
|
130
129
|
|
|
131
130
|
addCommands() {
|
|
132
131
|
return {
|
|
133
|
-
insertResponseArea:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// --- Slate: indexing logic (kept identical) ---
|
|
143
|
-
if (lastIndexMap[typeName] === undefined) lastIndexMap[typeName] = 0;
|
|
144
|
-
|
|
145
|
-
const prevIndex = lastIndexMap[typeName];
|
|
146
|
-
const newIndex = prevIndex === 0 ? prevIndex : prevIndex + 1;
|
|
147
|
-
|
|
148
|
-
// Slate increments map even if newIndex === 0
|
|
149
|
-
lastIndexMap[typeName] += 1;
|
|
150
|
-
|
|
151
|
-
const newInline = getDefaultNode({
|
|
152
|
-
schema: state.schema,
|
|
153
|
-
typeName,
|
|
154
|
-
index: newIndex,
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
if (!newInline) return false;
|
|
158
|
-
|
|
159
|
-
// --- Insert logic ---
|
|
160
|
-
const { selection } = state;
|
|
161
|
-
let insertPos = selection.from;
|
|
162
|
-
|
|
163
|
-
// If we're in a NodeSelection, insert before/after is ambiguous;
|
|
164
|
-
// We'll insert at its "from" (like your current code).
|
|
165
|
-
// If insertion fails, we fallback to end of doc.
|
|
166
|
-
const tryInsertAt = (pos) => {
|
|
167
|
-
try {
|
|
168
|
-
tr.insert(pos, newInline);
|
|
169
|
-
return pos;
|
|
170
|
-
} catch (e) {
|
|
171
|
-
return null;
|
|
132
|
+
insertResponseArea:
|
|
133
|
+
(type) =>
|
|
134
|
+
({ tr, state, dispatch, commands }) => {
|
|
135
|
+
const typeName = normalizeType(type);
|
|
136
|
+
|
|
137
|
+
// --- Slate: currentRespAreaList + max check ---
|
|
138
|
+
const currentCount = countNodesOfType(state.doc, typeName);
|
|
139
|
+
if (currentCount >= this.options.maxResponseAreas) {
|
|
140
|
+
return false;
|
|
172
141
|
}
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
let usedPos = tryInsertAt(insertPos);
|
|
176
|
-
|
|
177
|
-
// Slate branch: "markup empty and there's no focus"
|
|
178
|
-
// ProseMirror doesn't expose "no focus" the same way, so the closest
|
|
179
|
-
// equivalent fallback is inserting at end of document.
|
|
180
|
-
if (usedPos == null) {
|
|
181
|
-
usedPos = tryInsertAt(tr.doc.content.size);
|
|
182
|
-
}
|
|
183
|
-
if (usedPos == null) return false;
|
|
184
|
-
|
|
185
|
-
// Optionally select the node you just inserted (like your original command)
|
|
186
|
-
// tr.setSelection(NodeSelection.create(tr.doc, usedPos))
|
|
187
|
-
|
|
188
|
-
// --- Cursor move behavior for certain types (Slate: moveFocusTo next text) ---
|
|
189
|
-
if (
|
|
190
|
-
['math_templated', 'inline_dropdown', 'drag_in_the_blank', 'explicit_constructed_response'].includes(typeName)
|
|
191
|
-
) {
|
|
192
|
-
tr.setSelection(NodeSelection.create(tr.doc, usedPos));
|
|
193
|
-
} else {
|
|
194
|
-
// Default: put cursor after inserted node
|
|
195
|
-
const after = usedPos + newInline.nodeSize;
|
|
196
|
-
tr.setSelection(selectionAfterPos(tr.doc, after));
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (dispatch) {
|
|
200
|
-
commands.focus();
|
|
201
|
-
dispatch(tr);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return true;
|
|
205
|
-
},
|
|
206
|
-
refreshResponseArea: () => ({ tr, state, commands, dispatch }) => {
|
|
207
|
-
const { selection } = state;
|
|
208
|
-
const node = selection.$from.nodeAfter;
|
|
209
|
-
const nodePos = selection.from;
|
|
210
142
|
|
|
211
|
-
|
|
212
|
-
|
|
143
|
+
// --- Slate: indexing logic (kept identical) ---
|
|
144
|
+
if (lastIndexMap[typeName] === undefined) lastIndexMap[typeName] = 0;
|
|
145
|
+
|
|
146
|
+
const prevIndex = lastIndexMap[typeName];
|
|
147
|
+
const newIndex = prevIndex === 0 ? prevIndex : prevIndex + 1;
|
|
148
|
+
|
|
149
|
+
// Slate increments map even if newIndex === 0
|
|
150
|
+
lastIndexMap[typeName] += 1;
|
|
151
|
+
|
|
152
|
+
const newInline = getDefaultNode({
|
|
153
|
+
schema: state.schema,
|
|
154
|
+
typeName,
|
|
155
|
+
index: newIndex,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
if (!newInline) return false;
|
|
159
|
+
|
|
160
|
+
// --- Insert logic ---
|
|
161
|
+
const { selection } = state;
|
|
162
|
+
let insertPos = selection.from;
|
|
163
|
+
|
|
164
|
+
// If we're in a NodeSelection, insert before/after is ambiguous;
|
|
165
|
+
// We'll insert at its "from" (like your current code).
|
|
166
|
+
// If insertion fails, we fallback to end of doc.
|
|
167
|
+
const tryInsertAt = (pos) => {
|
|
168
|
+
try {
|
|
169
|
+
tr.insert(pos, newInline);
|
|
170
|
+
return pos;
|
|
171
|
+
} catch (e) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
213
175
|
|
|
214
|
-
|
|
215
|
-
commands.focus();
|
|
216
|
-
dispatch(tr);
|
|
217
|
-
}
|
|
176
|
+
let usedPos = tryInsertAt(insertPos);
|
|
218
177
|
|
|
219
|
-
|
|
220
|
-
|
|
178
|
+
// Slate branch: "markup empty and there's no focus"
|
|
179
|
+
// ProseMirror doesn't expose "no focus" the same way, so the closest
|
|
180
|
+
// equivalent fallback is inserting at end of document.
|
|
181
|
+
if (usedPos == null) {
|
|
182
|
+
usedPos = tryInsertAt(tr.doc.content.size);
|
|
183
|
+
}
|
|
184
|
+
if (usedPos == null) return false;
|
|
185
|
+
|
|
186
|
+
// Optionally select the node you just inserted (like your original command)
|
|
187
|
+
// tr.setSelection(NodeSelection.create(tr.doc, usedPos))
|
|
188
|
+
|
|
189
|
+
// --- Cursor move behavior for certain types (Slate: moveFocusTo next text) ---
|
|
190
|
+
if (
|
|
191
|
+
['math_templated', 'inline_dropdown', 'drag_in_the_blank', 'explicit_constructed_response'].includes(
|
|
192
|
+
typeName,
|
|
193
|
+
)
|
|
194
|
+
) {
|
|
195
|
+
tr.setSelection(NodeSelection.create(tr.doc, usedPos));
|
|
196
|
+
} else {
|
|
197
|
+
// Default: put cursor after inserted node
|
|
198
|
+
const after = usedPos + newInline.nodeSize;
|
|
199
|
+
tr.setSelection(selectionAfterPos(tr.doc, after));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (dispatch) {
|
|
203
|
+
commands.focus();
|
|
204
|
+
dispatch(tr);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return true;
|
|
208
|
+
},
|
|
209
|
+
refreshResponseArea:
|
|
210
|
+
() =>
|
|
211
|
+
({ tr, state, commands, dispatch }) => {
|
|
212
|
+
const { selection } = state;
|
|
213
|
+
const node = selection.$from.nodeAfter;
|
|
214
|
+
const nodePos = selection.from;
|
|
215
|
+
|
|
216
|
+
tr.setNodeMarkup(nodePos, undefined, { ...node.attrs, updated: `${Date.now()}` });
|
|
217
|
+
tr.setSelection(NodeSelection.create(tr.doc, nodePos));
|
|
218
|
+
|
|
219
|
+
if (dispatch) {
|
|
220
|
+
commands.focus();
|
|
221
|
+
dispatch(tr);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return true;
|
|
225
|
+
},
|
|
221
226
|
};
|
|
222
227
|
},
|
|
223
228
|
});
|