@pie-lib/editable-html-tip-tap 1.0.10 → 1.0.12

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.
@@ -117,7 +117,327 @@ export const specialConfig = {
117
117
  label: 'Ñ',
118
118
  },
119
119
  ],
120
- // remaining rows kept identical to original utils.js...
120
+ [
121
+ {
122
+ unicode: 'U+00A3',
123
+ description: 'POUND SIGN',
124
+ write: '£',
125
+ label: '£',
126
+ },
127
+ {
128
+ unicode: 'U+00AB',
129
+ description: 'LEFT-POINTING DOUBLE ANGLE QUOTATION MARK',
130
+ write: '«',
131
+ label: '«',
132
+ },
133
+ {
134
+ unicode: 'U+005E',
135
+ description: 'CIRCUMFLEX ACCENT',
136
+ write: '^',
137
+ label: '^',
138
+ extraProps: { style: { gridRow: 'span 2' } },
139
+ },
140
+ {
141
+ unicode: 'U+00E2',
142
+ description: 'LATIN SMALL LETTER A WITH CIRCUMFLEX',
143
+ write: 'â',
144
+ label: 'â',
145
+ },
146
+ {
147
+ unicode: 'U+00EA',
148
+ description: 'LATIN SMALL LETTER E WITH CIRCUMFLEX',
149
+ write: 'ê',
150
+ label: 'ê',
151
+ },
152
+ {
153
+ unicode: 'U+00EE',
154
+ description: 'LATIN SMALL LETTER I WITH CIRCUMFLEX',
155
+ write: 'î',
156
+ label: 'î',
157
+ },
158
+ {
159
+ unicode: 'U+00F4',
160
+ description: 'LATIN SMALL LETTER O WITH CIRCUMFLEX',
161
+ write: 'ô',
162
+ label: 'ô',
163
+ },
164
+ {
165
+ unicode: 'U+00FB',
166
+ description: 'LATIN SMALL LETTER U WITH CIRCUMFLEX',
167
+ write: 'û',
168
+ label: 'û',
169
+ },
170
+ {
171
+ unicode: 'U+00E7',
172
+ description: 'LATIN SMALL LETTER C WITH CEDILLA',
173
+ write: 'ç',
174
+ label: 'ç',
175
+ },
176
+ ],
177
+ [
178
+ {
179
+ unicode: 'U+00A5',
180
+ description: 'YEN SIGN',
181
+ write: '¥',
182
+ label: '¥',
183
+ },
184
+ {
185
+ unicode: 'U+00BB',
186
+ description: 'RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK',
187
+ write: '»',
188
+ label: '»',
189
+ },
190
+ {
191
+ unicode: 'U+00C2',
192
+ description: 'LATIN CAPITAL LETTER A WITH CIRCUMFLEX',
193
+ write: 'Â',
194
+ label: 'Â',
195
+ },
196
+ {
197
+ unicode: 'U+00CA',
198
+ description: 'LATIN CAPITAL LETTER E WITH CIRCUMFLEX',
199
+ write: 'Ê',
200
+ label: 'Ê',
201
+ },
202
+ {
203
+ unicode: 'U+00CE',
204
+ description: 'LATIN CAPITAL LETTER I WITH CIRCUMFLEX',
205
+ write: 'Î',
206
+ label: 'Î',
207
+ },
208
+ {
209
+ unicode: 'U+00D4',
210
+ description: 'LATIN CAPITAL LETTER O WITH CIRCUMFLEX',
211
+ write: 'Ô',
212
+ label: 'Ô',
213
+ },
214
+ {
215
+ unicode: 'U+00DB',
216
+ description: 'LATIN CAPITAL LETTER U WITH CIRCUMFLEX',
217
+ write: 'Û',
218
+ label: 'Û',
219
+ },
220
+ {
221
+ unicode: 'U+00C7',
222
+ description: 'LATIN CAPITAL LETTER C WITH CEDILLA',
223
+ write: 'Ç',
224
+ label: 'Ç',
225
+ },
226
+ ],
227
+ [
228
+ {
229
+ unicode: 'U+200A',
230
+ description: 'HAIR SPACE',
231
+ write: String.fromCodePoint('0x200A'),
232
+ label: ' ',
233
+ },
234
+ {
235
+ unicode: 'U+00A7',
236
+ description: 'SECTION SIGN',
237
+ write: '§',
238
+ label: '§',
239
+ },
240
+ {
241
+ unicode: 'U+00A8',
242
+ description: 'DIAERESIS',
243
+ write: '¨',
244
+ label: '¨',
245
+ extraProps: { style: { gridRow: 'span 2' } },
246
+ },
247
+ {
248
+ unicode: 'U+00E4',
249
+ description: 'LATIN SMALL LETTER A WITH DIAERESIS',
250
+ write: 'ä',
251
+ label: 'ä',
252
+ },
253
+ {
254
+ unicode: 'U+00EB',
255
+ description: 'LATIN SMALL LETTER E WITH DIAERESIS',
256
+ write: 'ë',
257
+ label: 'ë',
258
+ },
259
+ {
260
+ unicode: 'U+00EF',
261
+ description: 'LATIN SMALL LETTER I WITH DIAERESIS',
262
+ write: 'ï',
263
+ label: 'ï',
264
+ },
265
+ {
266
+ unicode: 'U+00F6',
267
+ description: 'LATIN SMALL LETTER O WITH DIAERESIS',
268
+ write: 'ö',
269
+ label: 'ö',
270
+ },
271
+ {
272
+ unicode: 'U+00FC',
273
+ description: 'LATIN SMALL LETTER U WITH DIAERESIS',
274
+ write: 'ü',
275
+ label: 'ü',
276
+ },
277
+ {
278
+ unicode: 'U+00DF',
279
+ description: 'LATIN SMALL LETTER SHARP S',
280
+ write: 'ß',
281
+ label: 'ß',
282
+ },
283
+ ],
284
+ [
285
+ {
286
+ unicode: 'U+2009',
287
+ description: 'THIN SPACE',
288
+ write: String.fromCodePoint('0x2009'),
289
+ label: ' ',
290
+ },
291
+ {
292
+ unicode: 'U+2026',
293
+ description: 'HORIZONTAL ELLIPSIS',
294
+ write: '…',
295
+ label: '…',
296
+ },
297
+ {
298
+ unicode: 'U+00C4',
299
+ description: 'LATIN CAPITAL LETTER A WITH DIAERESIS',
300
+ write: 'Ä',
301
+ label: 'Ä',
302
+ },
303
+ {
304
+ unicode: 'U+00CB',
305
+ description: 'LATIN CAPITAL LETTER E WITH DIAERESIS',
306
+ write: 'Ë',
307
+ label: 'Ë',
308
+ },
309
+ {
310
+ unicode: 'U+00CF',
311
+ description: 'LATIN CAPITAL LETTER I WITH DIAERESIS',
312
+ write: 'Ï',
313
+ label: 'Ï',
314
+ },
315
+ {
316
+ unicode: 'U+00D6',
317
+ description: 'LATIN CAPITAL LETTER O WITH DIAERESIS',
318
+ write: 'Ö',
319
+ label: 'Ö',
320
+ },
321
+ {
322
+ unicode: 'U+00DC',
323
+ description: 'LATIN CAPITAL LETTER U WITH DIAERESIS',
324
+ write: 'Ü',
325
+ label: 'Ü',
326
+ },
327
+ {
328
+ unicode: 'U+2212',
329
+ description: 'MINUS SIGN',
330
+ write: '−',
331
+ label: '−',
332
+ },
333
+ ],
334
+ [
335
+ {
336
+ unicode: 'U+00A0',
337
+ description: 'NO-BREAK SPACE',
338
+ write: String.fromCodePoint('0x00A0'),
339
+ label: ' ',
340
+ },
341
+ {
342
+ unicode: 'U+2022',
343
+ description: 'BULLET',
344
+ write: '•',
345
+ label: '•',
346
+ },
347
+ {
348
+ unicode: 'U+0060',
349
+ description: 'GRAVE ACCENT',
350
+ write: '`',
351
+ label: '`',
352
+ extraProps: { style: { gridRow: 'span 2' } },
353
+ },
354
+ {
355
+ unicode: 'U+00E0',
356
+ description: 'LATIN SMALL LETTER A WITH GRAVE',
357
+ write: 'à',
358
+ label: 'à',
359
+ },
360
+ {
361
+ unicode: 'U+00E8',
362
+ description: 'LATIN SMALL LETTER E WITH GRAVE',
363
+ write: 'è',
364
+ label: 'è',
365
+ },
366
+ {
367
+ unicode: 'U+00EC',
368
+ description: 'LATIN SMALL LETTER I WITH GRAVE',
369
+ write: 'ì',
370
+ label: 'ì',
371
+ },
372
+ {
373
+ unicode: 'U+00F2',
374
+ description: 'LATIN SMALL LETTER O WITH GRAVE',
375
+ write: 'ò',
376
+ label: 'ò',
377
+ },
378
+ {
379
+ unicode: 'U+00F9',
380
+ description: 'LATIN SMALL LETTER U WITH GRAVE',
381
+ write: 'ù',
382
+ label: 'ù',
383
+ },
384
+ {
385
+ unicode: 'U+2013',
386
+ description: 'EN DASH',
387
+ write: '–',
388
+ label: '–',
389
+ },
390
+ ],
391
+ [
392
+ {
393
+ unicode: 'U+2003',
394
+ description: 'EM SPACE',
395
+ write: String.fromCodePoint('0x2003'),
396
+ label: ' ',
397
+ },
398
+ {
399
+ unicode: 'U+25E6',
400
+ description: 'WHITE BULLET',
401
+ write: '◦',
402
+ label: '◦',
403
+ },
404
+ {
405
+ unicode: 'U+00C0',
406
+ description: 'LATIN CAPITAL LETTER A WITH GRAVE',
407
+ write: 'À',
408
+ label: 'À',
409
+ },
410
+ {
411
+ unicode: 'U+00C8',
412
+ description: 'LATIN CAPITAL LETTER E WITH GRAVE',
413
+ write: 'È',
414
+ label: 'È',
415
+ },
416
+ {
417
+ unicode: 'U+00CC',
418
+ description: 'LATIN CAPITAL LETTER I WITH GRAVE',
419
+ write: 'Ì',
420
+ label: 'Ì',
421
+ },
422
+ {
423
+ unicode: 'U+00D2',
424
+ description: 'LATIN CAPITAL LETTER O WITH GRAVE',
425
+ write: 'Ò',
426
+ label: 'Ò',
427
+ },
428
+ {
429
+ unicode: 'U+00D9',
430
+ description: 'LATIN CAPITAL LETTER U WITH GRAVE',
431
+ write: 'Ù',
432
+ label: 'Ù',
433
+ },
434
+ {
435
+ unicode: 'U+2014',
436
+ description: 'EM DASH',
437
+ write: '—',
438
+ label: '—',
439
+ },
440
+ ],
121
441
  ],
122
442
  };
123
443
 
@@ -18,8 +18,17 @@ const ExplicitConstructedResponse = (props) => {
18
18
  };
19
19
 
20
20
  useEffect(() => {
21
- setShowToolbar(selected);
22
- }, [selected]);
21
+ const { selection } = editor.state;
22
+ const onlyThisNodeSelected = selection.from + node.nodeSize === selection.to;
23
+
24
+ if (selected) {
25
+ if (onlyThisNodeSelected) {
26
+ setShowToolbar(selected);
27
+ }
28
+ } else {
29
+ setShowToolbar(selected);
30
+ }
31
+ }, [editor, node, selected]);
23
32
 
24
33
  useEffect(() => {
25
34
  const handleClickOutside = (event) => {
@@ -15,8 +15,17 @@ const InlineDropdown = (props) => {
15
15
  const InlineDropdownToolbar = options.respAreaToolbar(node, editor, () => {});
16
16
 
17
17
  useEffect(() => {
18
- setShowToolbar(selected);
19
- }, [selected]);
18
+ const { selection } = editor.state;
19
+ const onlyThisNodeSelected = selection.from + node.nodeSize === selection.to;
20
+
21
+ if (selected) {
22
+ if (onlyThisNodeSelected) {
23
+ setShowToolbar(selected);
24
+ }
25
+ } else {
26
+ setShowToolbar(selected);
27
+ }
28
+ }, [editor, node, selected]);
20
29
 
21
30
  useEffect(() => {
22
31
  const handleClickOutside = (event) => {
@@ -56,8 +56,17 @@ function ImageComponent(props) {
56
56
  }, [editor, node.attrs, getPercentFromWidth]);
57
57
 
58
58
  useEffect(() => {
59
- setShowToolbar(selected);
60
- }, [selected]);
59
+ const { selection } = editor.state;
60
+ const onlyThisNodeSelected = selection.from + node.nodeSize === selection.to;
61
+
62
+ if (selected) {
63
+ if (onlyThisNodeSelected) {
64
+ setShowToolbar(selected);
65
+ }
66
+ } else {
67
+ setShowToolbar(selected);
68
+ }
69
+ }, [editor, node, selected]);
61
70
 
62
71
  useEffect(() => {
63
72
  options.imageHandling.insertImageRequested(node, (finish) => new InsertImageHandler(editor, node, finish));
@@ -3,8 +3,6 @@ import { ReactNodeViewRenderer } from '@tiptap/react';
3
3
  import React from 'react';
4
4
  import ImageComponent from './component';
5
5
 
6
- // ---- Tiptap Extension ---- //
7
-
8
6
  export const ImageUploadNode = Node.create({
9
7
  name: 'imageUploadNode',
10
8
 
@@ -34,7 +32,6 @@ export const ImageUploadNode = Node.create({
34
32
  ];
35
33
  },
36
34
 
37
- // ✅ No `0` here!
38
35
  renderHTML({ HTMLAttributes }) {
39
36
  return ['img', mergeAttributes(HTMLAttributes, { 'data-type': 'image-upload-node' })];
40
37
  },
@@ -4,6 +4,8 @@ import { NodeViewWrapper, ReactRenderer, ReactNodeViewRenderer } from '@tiptap/r
4
4
  import { Plugin, PluginKey, NodeSelection, TextSelection } from 'prosemirror-state';
5
5
  import { MathPreview, MathToolbar } from '@pie-lib/math-toolbar';
6
6
  import { wrapMath, mmlToLatex, renderMath } from '@pie-lib/math-rendering';
7
+ import ReactDOM from 'react-dom';
8
+ import CustomPopper from '../components/characters/custom-popper';
7
9
 
8
10
  const ensureTextAfterMathPluginKey = new PluginKey('ensureTextAfterMath');
9
11
 
@@ -118,7 +120,6 @@ export const MathNode = Node.create({
118
120
  addCommands() {
119
121
  return {
120
122
  insertMath: (latex = '') => ({ tr, editor, dispatch }) => {
121
- // 2) Now the editor.view.state reflects the insertion
122
123
  const { state } = editor.view;
123
124
  const node = state.schema.nodes.math.create({
124
125
  latex,
@@ -172,6 +173,7 @@ export const MathNodeView = (props) => {
172
173
  const { node, updateAttributes, editor, selected, options } = props;
173
174
  const [showToolbar, setShowToolbar] = useState(selected);
174
175
  const toolbarRef = useRef(null);
176
+ const [position, setPosition] = useState({ top: 0, left: 0 });
175
177
 
176
178
  const latex = node.attrs.latex || '';
177
179
 
@@ -186,6 +188,15 @@ export const MathNodeView = (props) => {
186
188
  }, [showToolbar]);
187
189
 
188
190
  useEffect(() => {
191
+ // Calculate position relative to selection
192
+ const bodyRect = document.body.getBoundingClientRect();
193
+ const { from } = editor.state.selection;
194
+ const start = editor.view.coordsAtPos(from);
195
+ setPosition({
196
+ top: start.top + Math.abs(bodyRect.top) + 40, // shift above
197
+ left: start.left,
198
+ });
199
+
189
200
  const handleClickOutside = (event) => {
190
201
  if (
191
202
  toolbarRef.current &&
@@ -203,7 +214,7 @@ export const MathNodeView = (props) => {
203
214
  }
204
215
 
205
216
  return () => document.removeEventListener('mousedown', handleClickOutside);
206
- }, [showToolbar]);
217
+ }, [editor, showToolbar]);
207
218
 
208
219
  const handleChange = (newLatex) => {
209
220
  updateAttributes({ latex: newLatex });
@@ -237,23 +248,24 @@ export const MathNodeView = (props) => {
237
248
  <div onClick={() => setShowToolbar(true)} contentEditable={false}>
238
249
  <MathPreview latex={latex} />
239
250
  </div>
240
-
241
- {showToolbar && (
242
- <div
243
- ref={toolbarRef}
244
- style={{
245
- position: 'absolute',
246
- top: '100%',
247
- left: 0,
248
- zIndex: 20,
249
- background: 'var(--editable-html-toolbar-bg, #efefef)',
250
- boxShadow:
251
- '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)',
252
- }}
253
- >
254
- <MathToolbar latex={latex} autoFocus onChange={handleChange} onDone={handleDone} keypadMode="basic" />
255
- </div>
256
- )}
251
+ {showToolbar &&
252
+ ReactDOM.createPortal(
253
+ <div
254
+ ref={toolbarRef}
255
+ style={{
256
+ position: 'absolute',
257
+ top: `${position.top}px`,
258
+ left: `${position.left}px`,
259
+ zIndex: 20,
260
+ background: 'var(--editable-html-toolbar-bg, #efefef)',
261
+ boxShadow:
262
+ '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)',
263
+ }}
264
+ >
265
+ <MathToolbar latex={latex} autoFocus onChange={handleChange} onDone={handleDone} keypadMode="basic" />
266
+ </div>,
267
+ document.body,
268
+ )}
257
269
  </NodeViewWrapper>
258
270
  );
259
271
  };
@@ -1,12 +1,9 @@
1
- // InlineNodes.js
2
1
  import React from 'react';
3
2
  import { Node, ReactNodeViewRenderer, ReactRenderer } from '@tiptap/react';
4
3
  import ExplicitConstructedResponse from '../components/respArea/ExplicitConstructedResponse';
5
4
  import DragInTheBlank from '../components/respArea/DragInTheBlank/DragInTheBlank';
6
5
  import InlineDropdown from '../components/respArea/InlineDropdown';
7
6
  import { Extension } from '@tiptap/core';
8
- import { MathToolbar } from '@pie-lib/math-toolbar';
9
- import tippy from 'tippy.js';
10
7
 
11
8
  export const ResponseAreaExtension = Extension.create({
12
9
  name: 'responseArea',
@@ -117,40 +117,9 @@ const styles = (theme) => ({
117
117
  editorHolder: {
118
118
  position: 'relative',
119
119
  padding: '0px',
120
- // overflowY: 'auto',
121
- overflow: 'visible',
120
+ overflowY: 'auto',
122
121
  color: color.text(),
123
122
  backgroundColor: color.background(),
124
- '&::before': {
125
- left: '0',
126
- right: '0',
127
- bottom: '0',
128
- height: '1px',
129
- content: '""',
130
- position: 'absolute',
131
- transition: 'background-color 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
132
- pointerEvents: 'none',
133
- backgroundColor: 'rgba(0, 0, 0, 0.42)',
134
- },
135
- '&::after': {
136
- left: '0',
137
- right: '0',
138
- bottom: '0',
139
- height: '1px',
140
- content: '""',
141
- position: 'absolute',
142
- transform: 'scaleX(0)',
143
- transition: 'transform 200ms cubic-bezier(0.0, 0.0, 0.2, 1) 0ms, background-color 200ms linear',
144
- backgroundColor: 'rgba(0, 0, 0, 0.42)',
145
- },
146
- },
147
- disabledUnderline: {
148
- '&::before': {
149
- display: 'none',
150
- },
151
- '&::after': {
152
- display: 'none',
153
- },
154
123
  },
155
124
  disabledScrollbar: {
156
125
  '&::-webkit-scrollbar': {
@@ -159,33 +128,6 @@ const styles = (theme) => ({
159
128
  scrollbarWidth: 'none',
160
129
  '-ms-overflow-style': 'none',
161
130
  },
162
- readOnly: {
163
- '&::before': {
164
- background: 'transparent',
165
- backgroundSize: '5px 1px',
166
- backgroundImage: 'linear-gradient(to right, rgba(0, 0, 0, 0.42) 33%, transparent 0%)',
167
- backgroundRepeat: 'repeat-x',
168
- backgroundPosition: 'left top',
169
- },
170
- '&::after': {
171
- left: '0',
172
- right: '0',
173
- bottom: '0',
174
- height: '1px',
175
- content: '""',
176
- position: 'absolute',
177
- transform: 'scaleX(0)',
178
- transition: 'transform 200ms cubic-bezier(0.0, 0.0, 0.2, 1) 0ms, background-color 0ms linear',
179
- backgroundColor: 'rgba(0, 0, 0, 0)',
180
- },
181
- '&:hover': {
182
- '&::after': {
183
- transform: 'scaleX(0)',
184
- backgroundColor: theme.palette.common.black,
185
- height: '2px',
186
- },
187
- },
188
- },
189
131
  error: {
190
132
  border: `2px solid ${theme.palette.error.main} !important`,
191
133
  },