@seorii/tiptap 0.3.0-next.9 → 0.3.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/dist/i18n/index.d.ts +106 -6
- package/dist/i18n/index.js +56 -11
- package/dist/plugin/command/emoji.d.ts +3 -17
- package/dist/plugin/command/emoji.js +51 -24
- package/dist/plugin/command/stores.svelte.d.ts +52 -0
- package/dist/plugin/command/stores.svelte.js +69 -0
- package/dist/plugin/command/suggest.d.ts +6 -19
- package/dist/plugin/command/suggest.js +133 -51
- package/dist/plugin/embed.d.ts +2 -2
- package/dist/plugin/embed.js +5 -1
- package/dist/plugin/image/dragdrop.d.ts +2 -0
- package/dist/plugin/image/dragdrop.js +66 -2
- package/dist/plugin/image/index.js +3 -2
- package/dist/plugin/indent.js +0 -1
- package/dist/plugin/orderedlist/index.d.ts +1 -1
- package/dist/plugin/orderedlist/index.js +1 -1
- package/dist/plugin/orderedlist/{korean.scss → korean.css} +2 -2
- package/dist/plugin/table/index.d.ts +1 -1
- package/dist/plugin/table/index.js +19 -11
- package/dist/plugin/table/style/{cell.scss → cell.css} +6 -5
- package/dist/plugin/table/style/{grip.scss → grip.css} +14 -19
- package/dist/plugin/table/style/resize.css +28 -0
- package/dist/plugin/table/style/{table.scss → table.css} +15 -17
- package/dist/plugin/table/style.css +4 -0
- package/dist/plugin/table/tableCell/index.js +2 -4
- package/dist/plugin/table/tableHeader/index.js +1 -2
- package/dist/tiptap/Bubble.svelte +104 -70
- package/dist/tiptap/Bubble.svelte.d.ts +8 -6
- package/dist/tiptap/Command.svelte +160 -158
- package/dist/tiptap/Command.svelte.d.ts +2 -3
- package/dist/tiptap/Floating.svelte +51 -24
- package/dist/tiptap/Floating.svelte.d.ts +1 -0
- package/dist/tiptap/TipTap.svelte +210 -133
- package/dist/tiptap/TipTap.svelte.d.ts +7 -3
- package/dist/tiptap/ToolbarButton.svelte +30 -10
- package/dist/tiptap/ToolbarButton.svelte.d.ts +10 -6
- package/dist/tiptap/setMath.d.ts +2 -1
- package/dist/tiptap/setMath.js +74 -12
- package/dist/tiptap/tiptap.d.ts +9 -1
- package/dist/tiptap/tiptap.js +170 -16
- package/package.json +63 -57
- package/dist/plugin/command/stores.d.ts +0 -13
- package/dist/plugin/command/stores.js +0 -7
- package/dist/plugin/table/style/resize.scss +0 -26
- package/dist/plugin/table/style.scss +0 -4
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { setContext, untrack } from 'svelte';
|
|
3
3
|
import sanitizeHtml from 'sanitize-html';
|
|
4
4
|
import '@seorii/prosemirror-math/style.css';
|
|
5
5
|
import Bubble from './Bubble.svelte';
|
|
6
6
|
import Floating from './Floating.svelte';
|
|
7
7
|
import Command from './Command.svelte';
|
|
8
|
-
import {
|
|
9
|
-
|
|
8
|
+
import {
|
|
9
|
+
countSlashItems,
|
|
10
|
+
flattenSlashItems,
|
|
11
|
+
normalizeSlashIndex,
|
|
12
|
+
slashState
|
|
13
|
+
} from '../plugin/command/stores.svelte';
|
|
14
|
+
import {
|
|
15
|
+
I18N_CONTEXT,
|
|
16
|
+
setLocale as setI18nLocale,
|
|
17
|
+
translateWithLocale,
|
|
18
|
+
type I18nTranslate
|
|
19
|
+
} from '../i18n';
|
|
10
20
|
import type { UploadFn } from '../plugin/image/dragdrop';
|
|
11
21
|
import { fallbackUpload } from '../plugin/image/dragdrop';
|
|
12
22
|
import { Render } from 'nunui';
|
|
@@ -22,11 +32,13 @@
|
|
|
22
32
|
style?: string;
|
|
23
33
|
blocks?: any[];
|
|
24
34
|
placeholder?: string;
|
|
35
|
+
locale?: string;
|
|
25
36
|
sanitize?: Record<string, any>;
|
|
26
37
|
colors?: string[];
|
|
27
38
|
bubble?: any;
|
|
28
39
|
preloader?: any;
|
|
29
40
|
crossorigin?: 'anonymous' | 'use-credentials';
|
|
41
|
+
codeBlockLanguageLabels?: Record<string, string>;
|
|
30
42
|
};
|
|
31
43
|
|
|
32
44
|
let {
|
|
@@ -39,7 +51,8 @@
|
|
|
39
51
|
imageUpload = fallbackUpload,
|
|
40
52
|
style = '',
|
|
41
53
|
blocks = [],
|
|
42
|
-
placeholder
|
|
54
|
+
placeholder,
|
|
55
|
+
locale,
|
|
43
56
|
sanitize = {},
|
|
44
57
|
colors = [
|
|
45
58
|
'#ef5350', //red
|
|
@@ -54,8 +67,11 @@
|
|
|
54
67
|
bubble = null,
|
|
55
68
|
preloader,
|
|
56
69
|
crossorigin = 'anonymous',
|
|
70
|
+
codeBlockLanguageLabels = {}
|
|
57
71
|
}: Props = $props();
|
|
58
72
|
|
|
73
|
+
const scopedI18n: I18nTranslate = (...args) => translateWithLocale(locale, ...args);
|
|
74
|
+
|
|
59
75
|
const san = (body: string) =>
|
|
60
76
|
sanitizeHtml(body || '', {
|
|
61
77
|
...(sanitize || {}),
|
|
@@ -89,11 +105,16 @@
|
|
|
89
105
|
|
|
90
106
|
const tiptap = $state({ v: null as any, c: 0 });
|
|
91
107
|
setContext('editor', tiptap);
|
|
108
|
+
setContext(I18N_CONTEXT, scopedI18n);
|
|
92
109
|
let element: Element,
|
|
93
110
|
fullscreen = $state(false),
|
|
94
111
|
mounted = $state(false),
|
|
95
112
|
last = $state('');
|
|
96
113
|
|
|
114
|
+
$effect(() => {
|
|
115
|
+
setI18nLocale(locale);
|
|
116
|
+
});
|
|
117
|
+
|
|
97
118
|
$effect(() => {
|
|
98
119
|
if (tiptap.v) tiptap.v.setEditable(editable);
|
|
99
120
|
});
|
|
@@ -111,14 +132,16 @@
|
|
|
111
132
|
Promise.all([import('./tiptap'), import('@justinribeiro/lite-youtube')]).then(
|
|
112
133
|
([{ default: tt }]) => {
|
|
113
134
|
if (!untrack(() => mounted)) return;
|
|
135
|
+
const editorPlaceholder = placeholder ?? scopedI18n('placeholder');
|
|
114
136
|
tiptap.v = ref = tt(element, r, {
|
|
115
|
-
placeholder,
|
|
137
|
+
placeholder: editorPlaceholder,
|
|
116
138
|
editable,
|
|
117
139
|
onTransaction: () => {
|
|
118
140
|
tiptap.v = ref = tiptap.v;
|
|
119
141
|
tiptap.c++;
|
|
120
142
|
},
|
|
121
143
|
crossorigin,
|
|
144
|
+
codeBlockLanguageLabels,
|
|
122
145
|
...options
|
|
123
146
|
});
|
|
124
147
|
tiptap.v.on('update', ({ editor: tiptap }: any) => {
|
|
@@ -150,29 +173,35 @@
|
|
|
150
173
|
tiptap.v?.commands?.setContent?.(body);
|
|
151
174
|
});
|
|
152
175
|
|
|
153
|
-
let selectedIndex = $state(0);
|
|
154
|
-
$effect(() => {
|
|
155
|
-
if (!slashVisible) selectedIndex = 0;
|
|
156
|
-
});
|
|
157
|
-
|
|
158
176
|
function handleKeydown(event: KeyboardEvent) {
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
|
|
177
|
+
if (event.defaultPrevented) return true;
|
|
178
|
+
if (!slashState.visible) return false;
|
|
179
|
+
const count = countSlashItems();
|
|
180
|
+
if (!count) return false;
|
|
181
|
+
|
|
182
|
+
if (event.key === 'Tab') {
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
slashState.selectedIndex += event.shiftKey ? -1 : 1;
|
|
185
|
+
normalizeSlashIndex();
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
|
|
162
189
|
if (event.key === 'ArrowUp') {
|
|
163
190
|
event.preventDefault();
|
|
164
|
-
selectedIndex
|
|
191
|
+
slashState.selectedIndex -= 1;
|
|
192
|
+
normalizeSlashIndex();
|
|
165
193
|
return true;
|
|
166
194
|
}
|
|
167
195
|
if (event.key === 'ArrowDown') {
|
|
168
196
|
event.preventDefault();
|
|
169
|
-
selectedIndex
|
|
197
|
+
slashState.selectedIndex += 1;
|
|
198
|
+
normalizeSlashIndex();
|
|
170
199
|
return true;
|
|
171
200
|
}
|
|
172
201
|
|
|
173
202
|
if (event.key === 'Enter') {
|
|
174
203
|
event.preventDefault();
|
|
175
|
-
selectItem(selectedIndex);
|
|
204
|
+
selectItem(slashState.selectedIndex);
|
|
176
205
|
return true;
|
|
177
206
|
}
|
|
178
207
|
|
|
@@ -180,13 +209,10 @@
|
|
|
180
209
|
}
|
|
181
210
|
|
|
182
211
|
function selectItem(index: number) {
|
|
183
|
-
const item = (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
let range = $slashProps.range;
|
|
188
|
-
item.command({ editor: tiptap.v, range });
|
|
189
|
-
}
|
|
212
|
+
const item = flattenSlashItems()[index];
|
|
213
|
+
const { editor, range } = slashState.props;
|
|
214
|
+
if (!item || !editor || !range) return;
|
|
215
|
+
item.command({ editor, range });
|
|
190
216
|
}
|
|
191
217
|
</script>
|
|
192
218
|
|
|
@@ -207,7 +233,7 @@
|
|
|
207
233
|
{/if}
|
|
208
234
|
</div>
|
|
209
235
|
{#if editable}
|
|
210
|
-
<Command
|
|
236
|
+
<Command />
|
|
211
237
|
<Floating />
|
|
212
238
|
{/if}
|
|
213
239
|
{#if editable || mark}
|
|
@@ -217,112 +243,163 @@
|
|
|
217
243
|
{/if}
|
|
218
244
|
</main>
|
|
219
245
|
|
|
220
|
-
<style>
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
.editable :global(.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
div > :global(div)
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
246
|
+
<style>
|
|
247
|
+
main {
|
|
248
|
+
position: relative;
|
|
249
|
+
overscroll-behavior: none;
|
|
250
|
+
--shadow:
|
|
251
|
+
0 1px 2px rgba(127, 127, 127, 0.07), 0 2px 4px rgba(127, 127, 127, 0.07),
|
|
252
|
+
0 4px 8px rgba(127, 127, 127, 0.07), 0 8px 16px rgba(127, 127, 127, 0.07),
|
|
253
|
+
0 16px 32px rgba(127, 127, 127, 0.07), 0 32px 64px rgba(127, 127, 127, 0.07);
|
|
254
|
+
&.fullscreen {
|
|
255
|
+
z-index: 999999999;
|
|
256
|
+
position: fixed;
|
|
257
|
+
top: 0;
|
|
258
|
+
left: 0;
|
|
259
|
+
right: 0;
|
|
260
|
+
bottom: 0;
|
|
261
|
+
background: var(--surface);
|
|
262
|
+
padding: 82px 12px 12px 12px;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
& .wrapper {
|
|
266
|
+
position: relative;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
main :global(.tiptap-code-block-toolbar) {
|
|
271
|
+
position: absolute;
|
|
272
|
+
top: 8px;
|
|
273
|
+
right: 8px;
|
|
274
|
+
z-index: 1;
|
|
275
|
+
display: flex;
|
|
276
|
+
justify-content: flex-end;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
main:not(.editable) :global(.tiptap-code-block-toolbar) {
|
|
280
|
+
visibility: hidden;
|
|
281
|
+
pointer-events: none;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.target > :global(div) > :global(*:first-child) {
|
|
285
|
+
margin-top: 0 !important;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.target > :global(div) > :global(*:last-child) {
|
|
289
|
+
margin-bottom: 0 !important;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.editable :global(.ProseMirror-selectednode img) {
|
|
293
|
+
transition: all 0.2s ease-in-out;
|
|
294
|
+
filter: drop-shadow(0 0 0.75rem var(--primary-light13));
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.editable :global(.iframe-wrapper.ProseMirror-selectednode) {
|
|
298
|
+
outline: 3px solid var(--primary);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.editable :global(lite-youtube.ProseMirror-selectednode) {
|
|
302
|
+
outline: 3px solid var(--primary);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
div > :global(div) {
|
|
306
|
+
outline: none !important;
|
|
307
|
+
& :global(.ProseMirror) :global(p.is-editor-empty:first-child::before) {
|
|
308
|
+
color: var(--on-surface, #000);
|
|
309
|
+
opacity: 0.7;
|
|
310
|
+
content: attr(data-placeholder);
|
|
311
|
+
float: left;
|
|
312
|
+
height: 0;
|
|
313
|
+
pointer-events: none;
|
|
314
|
+
transition: 0.2s opacity ease-in-out;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
& :global(.ProseMirror-focused) :global(p.is-editor-empty:first-child::before) {
|
|
318
|
+
opacity: 0;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
& :global(a) {
|
|
322
|
+
cursor: pointer;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
& :global(img) {
|
|
326
|
+
transition: all 0.2s ease-in-out;
|
|
327
|
+
max-width: 100%;
|
|
328
|
+
border-radius: 12px;
|
|
329
|
+
position: relative;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
& :global(code.inline) {
|
|
333
|
+
background: var(--primary-light1);
|
|
334
|
+
padding: 2px 4px;
|
|
335
|
+
border-radius: 4px;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
& :global(pre) {
|
|
339
|
+
background: var(--primary-light1);
|
|
340
|
+
padding: 12px;
|
|
341
|
+
border-radius: 12px;
|
|
342
|
+
max-width: 100%;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
& :global(.tiptap-code-block) {
|
|
346
|
+
max-width: 100%;
|
|
347
|
+
position: relative;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
& :global(.tiptap-code-block-language) {
|
|
351
|
+
padding: 2px 6px;
|
|
352
|
+
border-radius: 8px;
|
|
353
|
+
border: 1px solid var(--primary-light2, #ddd);
|
|
354
|
+
outline: 1px solid transparent;
|
|
355
|
+
outline-offset: 0;
|
|
356
|
+
background: var(--surface, #fff);
|
|
357
|
+
color: var(--on-surface, #000);
|
|
358
|
+
font-size: 0.75em;
|
|
359
|
+
|
|
360
|
+
&:focus,
|
|
361
|
+
&:focus-visible {
|
|
362
|
+
outline-color: var(--primary-light9, #89a2d9);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
& :global(table) {
|
|
367
|
+
border-collapse: collapse;
|
|
368
|
+
width: 100%;
|
|
369
|
+
margin: 8px 0;
|
|
370
|
+
border: 1px solid var(--primary-light1);
|
|
371
|
+
border-radius: 12px;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
& :global(th),
|
|
375
|
+
& :global(td) {
|
|
376
|
+
padding: 8px;
|
|
377
|
+
border: 1px solid var(--primary-light1);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
& :global(.math-render) {
|
|
381
|
+
cursor: initial;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
& :global(lite-youtube) {
|
|
385
|
+
border-radius: 12px;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
& :global(.iframe-wrapper) {
|
|
389
|
+
position: relative;
|
|
390
|
+
padding-bottom: 12px;
|
|
391
|
+
overflow: hidden;
|
|
392
|
+
width: 100%;
|
|
393
|
+
height: 600px;
|
|
394
|
+
border-radius: 12px;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
& :global(iframe) {
|
|
398
|
+
position: absolute;
|
|
399
|
+
top: 0;
|
|
400
|
+
left: 0;
|
|
401
|
+
width: 100%;
|
|
402
|
+
height: 100%;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '@seorii/prosemirror-math/style.css';
|
|
2
2
|
import type { UploadFn } from '../plugin/image/dragdrop';
|
|
3
|
-
|
|
3
|
+
type Props = {
|
|
4
4
|
body: string;
|
|
5
5
|
editable?: boolean;
|
|
6
6
|
mark?: boolean;
|
|
@@ -11,10 +11,14 @@ declare const TipTap: import("svelte").Component<{
|
|
|
11
11
|
style?: string;
|
|
12
12
|
blocks?: any[];
|
|
13
13
|
placeholder?: string;
|
|
14
|
+
locale?: string;
|
|
14
15
|
sanitize?: Record<string, any>;
|
|
15
16
|
colors?: string[];
|
|
16
17
|
bubble?: any;
|
|
17
18
|
preloader?: any;
|
|
18
|
-
crossorigin?:
|
|
19
|
-
|
|
19
|
+
crossorigin?: 'anonymous' | 'use-credentials';
|
|
20
|
+
codeBlockLanguageLabels?: Record<string, string>;
|
|
21
|
+
};
|
|
22
|
+
declare const TipTap: import("svelte").Component<Props, {}, "body" | "ref" | "loaded">;
|
|
23
|
+
type TipTap = ReturnType<typeof TipTap>;
|
|
20
24
|
export default TipTap;
|
|
@@ -2,36 +2,56 @@
|
|
|
2
2
|
import { getContext } from 'svelte';
|
|
3
3
|
import { Button, IconButton, Render } from 'nunui';
|
|
4
4
|
|
|
5
|
-
const editor = getContext<{ v: any }>('editor');
|
|
5
|
+
const editor = getContext<{ v: any; c: number }>('editor');
|
|
6
6
|
const tiptap = $derived(editor.v);
|
|
7
7
|
|
|
8
|
+
type Props = {
|
|
9
|
+
prop?: string;
|
|
10
|
+
attrs?: Record<string, unknown>;
|
|
11
|
+
label?: string;
|
|
12
|
+
icon?: string;
|
|
13
|
+
methodName?: string;
|
|
14
|
+
tooltip?: string | Record<string, any>;
|
|
15
|
+
handler?: (() => void) | null;
|
|
16
|
+
children?: any;
|
|
17
|
+
[key: string]: unknown;
|
|
18
|
+
};
|
|
19
|
+
|
|
8
20
|
let {
|
|
9
21
|
prop = '',
|
|
10
|
-
attrs =
|
|
22
|
+
attrs = {},
|
|
11
23
|
label = '',
|
|
12
24
|
icon = '',
|
|
13
25
|
methodName = 'toggle' + prop.charAt(0).toUpperCase() + prop.slice(1),
|
|
14
26
|
tooltip,
|
|
15
|
-
handler,
|
|
27
|
+
handler = null,
|
|
28
|
+
children,
|
|
16
29
|
...rest
|
|
17
|
-
} = $props();
|
|
30
|
+
}: Props = $props();
|
|
18
31
|
|
|
19
|
-
const isActive = $derived(() => {
|
|
20
|
-
|
|
32
|
+
const isActive = $derived.by(() => {
|
|
33
|
+
// Recompute active styles on every editor transaction (selection/mark changes)
|
|
34
|
+
editor.c;
|
|
35
|
+
return !!(prop && tiptap?.isActive(prop, attrs));
|
|
21
36
|
});
|
|
22
37
|
|
|
23
38
|
function toggle() {
|
|
24
39
|
if (!tiptap) return;
|
|
25
|
-
//tiptap.chain().focus().clearNodes().run()
|
|
26
40
|
if (handler) return handler();
|
|
27
|
-
setTimeout(() =>
|
|
41
|
+
setTimeout(() => {
|
|
42
|
+
const chain = tiptap.chain().focus() as Record<
|
|
43
|
+
string,
|
|
44
|
+
(attrs?: unknown) => { run?: () => void }
|
|
45
|
+
>;
|
|
46
|
+
chain[methodName]?.(attrs)?.run?.();
|
|
47
|
+
}, 0);
|
|
28
48
|
}
|
|
29
49
|
</script>
|
|
30
50
|
|
|
31
51
|
{#if icon}
|
|
32
|
-
<IconButton size="1.2em" {icon} active={isActive
|
|
52
|
+
<IconButton size="1.2em" {icon} active={isActive} onclick={toggle} {tooltip} tabindex={0} />
|
|
33
53
|
{:else}
|
|
34
|
-
<Button outlined={!isActive
|
|
54
|
+
<Button outlined={!isActive} onclick={handler || toggle} small {...rest}>
|
|
35
55
|
{label}
|
|
36
56
|
<Render it={children} />
|
|
37
57
|
</Button>
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
type Props = {
|
|
2
2
|
prop?: string;
|
|
3
|
-
attrs?: string
|
|
3
|
+
attrs?: Record<string, unknown>;
|
|
4
4
|
label?: string;
|
|
5
5
|
icon?: string;
|
|
6
|
-
methodName?:
|
|
7
|
-
tooltip
|
|
8
|
-
handler
|
|
9
|
-
|
|
6
|
+
methodName?: string;
|
|
7
|
+
tooltip?: string | Record<string, any>;
|
|
8
|
+
handler?: (() => void) | null;
|
|
9
|
+
children?: any;
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
};
|
|
12
|
+
declare const ToolbarButton: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type ToolbarButton = ReturnType<typeof ToolbarButton>;
|
|
10
14
|
export default ToolbarButton;
|
package/dist/tiptap/setMath.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Editor } from '@tiptap/core';
|
|
2
|
+
export default function setMath(tiptap: Editor): void;
|
package/dist/tiptap/setMath.js
CHANGED
|
@@ -1,17 +1,79 @@
|
|
|
1
|
+
import { NodeSelection } from '@tiptap/pm/state';
|
|
2
|
+
const pushUniqueTarget = (targets, from, to, text) => {
|
|
3
|
+
if (targets.some((target) => target.from === from && target.to === to))
|
|
4
|
+
return;
|
|
5
|
+
targets.push({ from, to, text });
|
|
6
|
+
};
|
|
7
|
+
const collectMathTargets = (state, mathInline) => {
|
|
8
|
+
const targets = [];
|
|
9
|
+
const { selection } = state;
|
|
10
|
+
if (selection instanceof NodeSelection && selection.node.type === mathInline) {
|
|
11
|
+
pushUniqueTarget(targets, selection.from, selection.to, selection.node.textContent);
|
|
12
|
+
}
|
|
13
|
+
if (selection.$from.parent.type === mathInline) {
|
|
14
|
+
const depth = selection.$from.depth;
|
|
15
|
+
const from = selection.$from.before(depth);
|
|
16
|
+
const to = selection.$from.after(depth);
|
|
17
|
+
pushUniqueTarget(targets, from, to, selection.$from.parent.textContent);
|
|
18
|
+
}
|
|
19
|
+
state.doc.nodesBetween(selection.from, selection.to, (node, position) => {
|
|
20
|
+
if (node.type !== mathInline)
|
|
21
|
+
return;
|
|
22
|
+
pushUniqueTarget(targets, position, position + node.nodeSize, node.textContent);
|
|
23
|
+
return false;
|
|
24
|
+
});
|
|
25
|
+
return targets;
|
|
26
|
+
};
|
|
27
|
+
const unwrapMath = ({ state, tr }, mathInline) => {
|
|
28
|
+
const targets = collectMathTargets(state, mathInline);
|
|
29
|
+
if (!targets.length)
|
|
30
|
+
return false;
|
|
31
|
+
for (const { from, to, text } of targets.sort((a, b) => b.from - a.from)) {
|
|
32
|
+
if (!text.length) {
|
|
33
|
+
tr.delete(from, to);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
tr.replaceWith(from, to, state.schema.text(text));
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
};
|
|
40
|
+
const wrapSelectionAsMath = ({ state, tr }, mathInline) => {
|
|
41
|
+
const { selection } = state;
|
|
42
|
+
if (selection.empty)
|
|
43
|
+
return false;
|
|
44
|
+
const targets = [];
|
|
45
|
+
state.doc.nodesBetween(selection.from, selection.to, (node, position) => {
|
|
46
|
+
if (!node.isTextblock)
|
|
47
|
+
return;
|
|
48
|
+
const contentFrom = position + 1;
|
|
49
|
+
const contentTo = position + node.nodeSize - 1;
|
|
50
|
+
const from = Math.max(selection.from, contentFrom);
|
|
51
|
+
const to = Math.min(selection.to, contentTo);
|
|
52
|
+
if (from >= to)
|
|
53
|
+
return;
|
|
54
|
+
const text = state.doc.textBetween(from, to, '');
|
|
55
|
+
if (!text.length)
|
|
56
|
+
return;
|
|
57
|
+
targets.push({ from, to, text });
|
|
58
|
+
});
|
|
59
|
+
if (!targets.length)
|
|
60
|
+
return false;
|
|
61
|
+
for (const { from, to, text } of targets.sort((a, b) => b.from - a.from)) {
|
|
62
|
+
const newNode = mathInline.create(null, state.schema.text(text));
|
|
63
|
+
tr.replaceWith(from, to, newNode);
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
};
|
|
1
67
|
export default function setMath(tiptap) {
|
|
2
|
-
const { selection } = tiptap.state;
|
|
3
68
|
tiptap
|
|
4
69
|
.chain()
|
|
5
|
-
.command((
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const newNode = state.schema.nodes.math_inline.create(null, state.schema.text(updatedText));
|
|
14
|
-
tr = tr.replaceWith(startPosition, endPosition, newNode);
|
|
15
|
-
}))
|
|
70
|
+
.command((props) => {
|
|
71
|
+
const mathInline = props.state.schema.nodes.math_inline;
|
|
72
|
+
if (!mathInline || props.state.selection.empty)
|
|
73
|
+
return false;
|
|
74
|
+
if (unwrapMath(props, mathInline))
|
|
75
|
+
return true;
|
|
76
|
+
return wrapSelectionAsMath(props, mathInline);
|
|
77
|
+
})
|
|
16
78
|
.run();
|
|
17
79
|
}
|
package/dist/tiptap/tiptap.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
import { Editor } from '@tiptap/core';
|
|
2
|
-
|
|
2
|
+
type CodeBlockLanguageLabelMap = Record<string, string>;
|
|
3
|
+
type CrossOrigin = 'anonymous' | 'use-credentials' | undefined;
|
|
4
|
+
declare const _default: (element: Element, content: string, { placeholder, plugins, crossorigin, codeBlockLanguageLabels, ...props }?: {
|
|
5
|
+
placeholder?: string;
|
|
6
|
+
plugins?: any[];
|
|
7
|
+
crossorigin?: CrossOrigin;
|
|
8
|
+
codeBlockLanguageLabels?: CodeBlockLanguageLabelMap;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}) => Editor;
|
|
3
11
|
export default _default;
|