@seorii/tiptap 0.3.0-next.9 → 0.4.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.
Files changed (55) hide show
  1. package/dist/i18n/index.d.ts +106 -6
  2. package/dist/i18n/index.js +56 -11
  3. package/dist/index.d.ts +2 -1
  4. package/dist/index.js +2 -1
  5. package/dist/plugin/command/emoji.d.ts +3 -17
  6. package/dist/plugin/command/emoji.js +51 -24
  7. package/dist/plugin/command/stores.svelte.d.ts +52 -0
  8. package/dist/plugin/command/stores.svelte.js +69 -0
  9. package/dist/plugin/command/suggest.d.ts +6 -19
  10. package/dist/plugin/command/suggest.js +149 -49
  11. package/dist/plugin/embed.d.ts +2 -2
  12. package/dist/plugin/embed.js +6 -2
  13. package/dist/plugin/iframe.js +1 -1
  14. package/dist/plugin/image/dragdrop.d.ts +2 -0
  15. package/dist/plugin/image/dragdrop.js +126 -15
  16. package/dist/plugin/image/index.js +4 -3
  17. package/dist/plugin/indent.js +0 -1
  18. package/dist/plugin/orderedlist/index.d.ts +1 -1
  19. package/dist/plugin/orderedlist/index.js +1 -1
  20. package/dist/plugin/orderedlist/{korean.scss → korean.css} +2 -2
  21. package/dist/plugin/resize/index.d.ts +8 -0
  22. package/dist/plugin/resize/index.js +454 -0
  23. package/dist/plugin/table/index.d.ts +1 -1
  24. package/dist/plugin/table/index.js +19 -11
  25. package/dist/plugin/table/style/{cell.scss → cell.css} +6 -5
  26. package/dist/plugin/table/style/{grip.scss → grip.css} +14 -19
  27. package/dist/plugin/table/style/resize.css +28 -0
  28. package/dist/plugin/table/style/{table.scss → table.css} +15 -17
  29. package/dist/plugin/table/style.css +4 -0
  30. package/dist/plugin/table/tableCell/index.js +2 -4
  31. package/dist/plugin/table/tableHeader/index.js +1 -2
  32. package/dist/plugin/upload/skeleton/UploadSkeleton.svelte +97 -0
  33. package/dist/plugin/upload/skeleton/UploadSkeleton.svelte.d.ts +5 -0
  34. package/dist/plugin/upload/skeleton/index.d.ts +30 -0
  35. package/dist/plugin/upload/skeleton/index.js +147 -0
  36. package/dist/plugin/youtube.js +1 -1
  37. package/dist/tiptap/Bubble.svelte +231 -92
  38. package/dist/tiptap/Bubble.svelte.d.ts +9 -6
  39. package/dist/tiptap/Command.svelte +160 -158
  40. package/dist/tiptap/Command.svelte.d.ts +2 -3
  41. package/dist/tiptap/Floating.svelte +51 -24
  42. package/dist/tiptap/Floating.svelte.d.ts +1 -0
  43. package/dist/tiptap/TipTap.svelte +302 -140
  44. package/dist/tiptap/TipTap.svelte.d.ts +10 -3
  45. package/dist/tiptap/ToolbarButton.svelte +30 -10
  46. package/dist/tiptap/ToolbarButton.svelte.d.ts +10 -6
  47. package/dist/tiptap/setMath.d.ts +2 -1
  48. package/dist/tiptap/setMath.js +74 -12
  49. package/dist/tiptap/tiptap.d.ts +9 -1
  50. package/dist/tiptap/tiptap.js +172 -16
  51. package/package.json +63 -57
  52. package/dist/plugin/command/stores.d.ts +0 -13
  53. package/dist/plugin/command/stores.js +0 -7
  54. package/dist/plugin/table/style/resize.scss +0 -26
  55. package/dist/plugin/table/style.scss +0 -4
@@ -1,14 +1,25 @@
1
1
  <script lang="ts">
2
- import { onMount, onDestroy, setContext, untrack } from 'svelte';
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 { slashItems, slashProps, slashVisible } from '../plugin/command/stores';
9
- import i18n from '../i18n';
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';
22
+ import MediaResize, { type ResizeOptions } from '../plugin/resize';
12
23
  import { Render } from 'nunui';
13
24
 
14
25
  type Props = {
@@ -22,11 +33,15 @@
22
33
  style?: string;
23
34
  blocks?: any[];
24
35
  placeholder?: string;
36
+ locale?: string;
25
37
  sanitize?: Record<string, any>;
26
38
  colors?: string[];
27
39
  bubble?: any;
40
+ bubbleDocked?: boolean;
28
41
  preloader?: any;
29
42
  crossorigin?: 'anonymous' | 'use-credentials';
43
+ codeBlockLanguageLabels?: Record<string, string>;
44
+ resize?: boolean | ResizeOptions;
30
45
  };
31
46
 
32
47
  let {
@@ -39,7 +54,8 @@
39
54
  imageUpload = fallbackUpload,
40
55
  style = '',
41
56
  blocks = [],
42
- placeholder = i18n('placeholder'),
57
+ placeholder,
58
+ locale,
43
59
  sanitize = {},
44
60
  colors = [
45
61
  '#ef5350', //red
@@ -52,10 +68,23 @@
52
68
  '#ab47bc' //purple
53
69
  ],
54
70
  bubble = null,
71
+ bubbleDocked = false,
55
72
  preloader,
56
73
  crossorigin = 'anonymous',
74
+ codeBlockLanguageLabels = {},
75
+ resize = true
57
76
  }: Props = $props();
58
77
 
78
+ const scopedI18n: I18nTranslate = (...args) => translateWithLocale(locale, ...args);
79
+ const resizeDataAttrs = [
80
+ 'data-resize-handler',
81
+ 'data-resize-target',
82
+ 'data-resize-min-height',
83
+ 'data-resize-max-height',
84
+ 'data-bubble-menu',
85
+ 'data-hide-bubble-menu'
86
+ ];
87
+
59
88
  const san = (body: string) =>
60
89
  sanitizeHtml(body || '', {
61
90
  ...(sanitize || {}),
@@ -69,18 +98,26 @@
69
98
  'embed',
70
99
  'mark',
71
100
  'code',
101
+ 'tiptap-upload-skeleton',
72
102
  ...(sanitize.allowedTags || [])
73
103
  ]),
74
104
  allowedStyles: '*' as any,
75
105
  allowedAttributes: {
76
106
  '*': ['style', 'class'],
77
107
  a: ['href', 'name', 'target'],
78
- img: ['src', 'srcset', 'alt', 'title', 'width', 'height', 'loading'],
79
- iframe: ['src', 'width', 'height', 'frameborder', 'allowfullscreen'],
108
+ img: ['src', 'srcset', 'alt', 'title', 'width', 'height', 'loading', ...resizeDataAttrs],
109
+ iframe: ['src', 'width', 'height', 'frameborder', 'allowfullscreen', ...resizeDataAttrs],
80
110
  th: ['colwidth', 'colspan', 'rowspan'],
81
111
  td: ['colwidth', 'colspan', 'rowspan'],
82
- 'lite-youtube': ['videoid', 'params', 'nocookie', 'title', 'provider'],
83
- embed: ['src', 'type', 'frameborder', 'allowfullscreen'],
112
+ 'lite-youtube': ['videoid', 'params', 'nocookie', 'title', 'provider', ...resizeDataAttrs],
113
+ embed: ['src', 'type', 'frameborder', 'allowfullscreen', ...resizeDataAttrs],
114
+ 'tiptap-upload-skeleton': [
115
+ 'data-upload-id',
116
+ 'data-upload-kind',
117
+ 'data-upload-height',
118
+ 'data-bubble-menu',
119
+ 'data-hide-bubble-menu'
120
+ ],
84
121
  mark: ['style', 'data-color'],
85
122
  code: ['class'],
86
123
  ...(sanitize.allowedAttributes || [])
@@ -89,11 +126,16 @@
89
126
 
90
127
  const tiptap = $state({ v: null as any, c: 0 });
91
128
  setContext('editor', tiptap);
129
+ setContext(I18N_CONTEXT, scopedI18n);
92
130
  let element: Element,
93
131
  fullscreen = $state(false),
94
132
  mounted = $state(false),
95
133
  last = $state('');
96
134
 
135
+ $effect(() => {
136
+ setI18nLocale(locale);
137
+ });
138
+
97
139
  $effect(() => {
98
140
  if (tiptap.v) tiptap.v.setEditable(editable);
99
141
  });
@@ -111,15 +153,27 @@
111
153
  Promise.all([import('./tiptap'), import('@justinribeiro/lite-youtube')]).then(
112
154
  ([{ default: tt }]) => {
113
155
  if (!untrack(() => mounted)) return;
156
+ const editorPlaceholder = placeholder ?? scopedI18n('placeholder');
157
+ const optionPlugins = Array.isArray(options.plugins)
158
+ ? [...options.plugins]
159
+ : options.plugins
160
+ ? [options.plugins]
161
+ : [];
162
+ if (resize) {
163
+ const resizeOptions = typeof resize === 'object' ? resize : {};
164
+ optionPlugins.unshift(MediaResize.configure(resizeOptions));
165
+ }
114
166
  tiptap.v = ref = tt(element, r, {
115
- placeholder,
167
+ placeholder: editorPlaceholder,
116
168
  editable,
117
169
  onTransaction: () => {
118
170
  tiptap.v = ref = tiptap.v;
119
171
  tiptap.c++;
120
172
  },
121
173
  crossorigin,
122
- ...options
174
+ codeBlockLanguageLabels,
175
+ ...options,
176
+ plugins: optionPlugins
123
177
  });
124
178
  tiptap.v.on('update', ({ editor: tiptap }: any) => {
125
179
  let content = tiptap.getHTML(),
@@ -150,29 +204,35 @@
150
204
  tiptap.v?.commands?.setContent?.(body);
151
205
  });
152
206
 
153
- let selectedIndex = $state(0);
154
- $effect(() => {
155
- if (!slashVisible) selectedIndex = 0;
156
- });
157
-
158
207
  function handleKeydown(event: KeyboardEvent) {
159
- if (!$slashVisible) return;
160
- let count = $slashItems.length;
161
- if (($slashItems[0] as any)?.list) count = $slashItems.reduce((acc, item) => acc + (item as any).list.length, 0);
208
+ if (event.defaultPrevented) return true;
209
+ if (!slashState.visible) return false;
210
+ const count = countSlashItems();
211
+ if (!count) return false;
212
+
213
+ if (event.key === 'Tab') {
214
+ event.preventDefault();
215
+ slashState.selectedIndex += event.shiftKey ? -1 : 1;
216
+ normalizeSlashIndex();
217
+ return true;
218
+ }
219
+
162
220
  if (event.key === 'ArrowUp') {
163
221
  event.preventDefault();
164
- selectedIndex = (selectedIndex + count - 1) % count;
222
+ slashState.selectedIndex -= 1;
223
+ normalizeSlashIndex();
165
224
  return true;
166
225
  }
167
226
  if (event.key === 'ArrowDown') {
168
227
  event.preventDefault();
169
- selectedIndex = (selectedIndex + 1) % count;
228
+ slashState.selectedIndex += 1;
229
+ normalizeSlashIndex();
170
230
  return true;
171
231
  }
172
232
 
173
233
  if (event.key === 'Enter') {
174
234
  event.preventDefault();
175
- selectItem(selectedIndex);
235
+ selectItem(slashState.selectedIndex);
176
236
  return true;
177
237
  }
178
238
 
@@ -180,17 +240,19 @@
180
240
  }
181
241
 
182
242
  function selectItem(index: number) {
183
- const item = ($slashItems[0] as any)?.list
184
- ? $slashItems.map((i: any) => i.list).flat()[index]
185
- : $slashItems[index];
186
- if (item) {
187
- let range = $slashProps.range;
188
- item.command({ editor: tiptap.v, range });
189
- }
243
+ const item = flattenSlashItems()[index];
244
+ const { editor, range } = slashState.props;
245
+ if (!item || !editor || !range) return;
246
+ item.command({ editor, range });
190
247
  }
191
248
  </script>
192
249
 
193
250
  <main class:fullscreen class:editable {style}>
251
+ {#if bubbleDocked && (editable || mark)}
252
+ <Bubble {colors} {editable} override={bubble} docked={bubbleDocked}>
253
+ <Render it={bubble} />
254
+ </Bubble>
255
+ {/if}
194
256
  <div class="wrapper">
195
257
  <!-- svelte-ignore a11y_no_static_element_interactions -->
196
258
  <div bind:this={element} class="target" onkeydown={handleKeydown}></div>
@@ -207,122 +269,222 @@
207
269
  {/if}
208
270
  </div>
209
271
  {#if editable}
210
- <Command {selectedIndex} />
272
+ <Command />
211
273
  <Floating />
212
274
  {/if}
213
- {#if editable || mark}
214
- <Bubble {colors} {editable} override={bubble}>
275
+ {#if !bubbleDocked && (editable || mark)}
276
+ <Bubble {colors} {editable} override={bubble} docked={bubbleDocked}>
215
277
  <Render it={bubble} />
216
278
  </Bubble>
217
279
  {/if}
218
280
  </main>
219
281
 
220
- <style>main {
221
- position: relative;
222
- overscroll-behavior: none;
223
- --shadow: 0 1px 2px rgba(127, 127, 127, 0.07), 0 2px 4px rgba(127, 127, 127, 0.07),
224
- 0 4px 8px rgba(127, 127, 127, 0.07), 0 8px 16px rgba(127, 127, 127, 0.07),
225
- 0 16px 32px rgba(127, 127, 127, 0.07), 0 32px 64px rgba(127, 127, 127, 0.07);
226
- }
227
- main.fullscreen {
228
- z-index: 999999999;
229
- position: fixed;
230
- top: 0;
231
- left: 0;
232
- right: 0;
233
- bottom: 0;
234
- background: var(--surface);
235
- padding: 82px 12px 12px 12px;
236
- }
237
- main .wrapper {
238
- position: relative;
239
- }
240
-
241
- .target > :global(div) > :global(*:first-child) {
242
- margin-top: 0 !important;
243
- }
244
- .target > :global(div) > :global(*:last-child) {
245
- margin-bottom: 0 !important;
246
- }
247
-
248
- .editable :global(.ProseMirror-selectednode img) {
249
- transition: all 0.2s ease-in-out;
250
- filter: drop-shadow(0 0 0.75rem var(--primary-light13));
251
- }
252
-
253
- .editable :global(.iframe-wrapper.ProseMirror-selectednode) {
254
- outline: 3px solid var(--primary);
255
- }
256
-
257
- .editable :global(lite-youtube.ProseMirror-selectednode) {
258
- outline: 3px solid var(--primary);
259
- }
260
-
261
- div > :global(div) {
262
- outline: none !important;
263
- }
264
- div > :global(div) :global(.ProseMirror) :global(p.is-editor-empty:first-child::before) {
265
- color: var(--on-surface, #000);
266
- opacity: 0.7;
267
- content: attr(data-placeholder);
268
- float: left;
269
- height: 0;
270
- pointer-events: none;
271
- transition: 0.2s opacity ease-in-out;
272
- }
273
- div > :global(div) :global(.ProseMirror-focused) :global(p.is-editor-empty:first-child::before) {
274
- opacity: 0;
275
- }
276
- div > :global(div) :global(a) {
277
- cursor: pointer;
278
- }
279
- div > :global(div) :global(img) {
280
- transition: all 0.2s ease-in-out;
281
- max-width: 100%;
282
- border-radius: 12px;
283
- position: relative;
284
- }
285
- div > :global(div) :global(code.inline) {
286
- background: var(--primary-light1);
287
- padding: 2px 4px;
288
- border-radius: 4px;
289
- }
290
- div > :global(div) :global(pre) {
291
- background: var(--primary-light1);
292
- padding: 12px;
293
- border-radius: 12px;
294
- max-width: 100%;
295
- }
296
- div > :global(div) :global(table) {
297
- border-collapse: collapse;
298
- width: 100%;
299
- margin: 8px 0;
300
- border: 1px solid var(--primary-light1);
301
- border-radius: 12px;
302
- }
303
- div > :global(div) :global(th),
304
- div > :global(div) :global(td) {
305
- padding: 8px;
306
- border: 1px solid var(--primary-light1);
307
- }
308
- div > :global(div) :global(.math-render) {
309
- cursor: initial;
310
- }
311
- div > :global(div) :global(lite-youtube) {
312
- border-radius: 12px;
313
- }
314
- div > :global(div) :global(.iframe-wrapper) {
315
- position: relative;
316
- padding-bottom: 12px;
317
- overflow: hidden;
318
- width: 100%;
319
- height: 600px;
320
- border-radius: 12px;
321
- }
322
- div > :global(div) :global(iframe) {
323
- position: absolute;
324
- top: 0;
325
- left: 0;
326
- width: 100%;
327
- height: 100%;
328
- }</style>
282
+ <style>
283
+ main {
284
+ position: relative;
285
+ overscroll-behavior: none;
286
+ --shadow:
287
+ 0 1px 2px rgba(127, 127, 127, 0.07), 0 2px 4px rgba(127, 127, 127, 0.07),
288
+ 0 4px 8px rgba(127, 127, 127, 0.07), 0 8px 16px rgba(127, 127, 127, 0.07),
289
+ 0 16px 32px rgba(127, 127, 127, 0.07), 0 32px 64px rgba(127, 127, 127, 0.07);
290
+ &.fullscreen {
291
+ z-index: 999999999;
292
+ position: fixed;
293
+ top: 0;
294
+ left: 0;
295
+ right: 0;
296
+ bottom: 0;
297
+ background: var(--surface);
298
+ padding: 82px 12px 12px 12px;
299
+ }
300
+
301
+ & .wrapper {
302
+ position: relative;
303
+ }
304
+ }
305
+
306
+ main :global(.tiptap-code-block-toolbar) {
307
+ position: absolute;
308
+ top: 8px;
309
+ right: 8px;
310
+ z-index: 1;
311
+ display: flex;
312
+ justify-content: flex-end;
313
+ }
314
+
315
+ main:not(.editable) :global(.tiptap-code-block-toolbar) {
316
+ visibility: hidden;
317
+ pointer-events: none;
318
+ }
319
+
320
+ .target > :global(div) > :global(*:first-child) {
321
+ margin-top: 0 !important;
322
+ }
323
+
324
+ .target > :global(div) > :global(*:last-child) {
325
+ margin-bottom: 0 !important;
326
+ }
327
+
328
+ .editable :global(.ProseMirror-selectednode img) {
329
+ transition: all 0.2s ease-in-out;
330
+ outline: 3px solid var(--primary);
331
+ outline-offset: 2px;
332
+ filter: none;
333
+ }
334
+
335
+ .editable :global(.iframe-wrapper.ProseMirror-selectednode) {
336
+ outline: 3px solid var(--primary);
337
+ }
338
+
339
+ .editable :global(lite-youtube.ProseMirror-selectednode) {
340
+ outline: 3px solid var(--primary);
341
+ }
342
+
343
+ .editable :global(.tiptap-media-resize-anchor) {
344
+ width: 100%;
345
+ display: flex;
346
+ justify-content: center;
347
+ margin: 6px 0 2px;
348
+ line-height: 0;
349
+ pointer-events: none;
350
+ }
351
+
352
+ .editable :global(.tiptap-media-resize-handle) {
353
+ appearance: none;
354
+ -webkit-appearance: none;
355
+ display: block;
356
+ width: 42px;
357
+ height: 8px;
358
+ margin: 0;
359
+ padding: 0;
360
+ border: 1px solid var(--primary-light3, rgba(120, 120, 120, 0.45));
361
+ border-radius: 999px;
362
+ background: var(--primary-light6, rgba(120, 120, 120, 0.2));
363
+ cursor: ns-resize;
364
+ pointer-events: auto;
365
+ transition:
366
+ background-color 0.15s ease,
367
+ border-color 0.15s ease,
368
+ transform 0.15s ease;
369
+ }
370
+
371
+ .editable :global(.tiptap-media-resize-handle:hover),
372
+ .editable :global(.tiptap-media-resize-handle:focus-visible) {
373
+ background: var(--primary-light4, rgba(120, 120, 120, 0.35));
374
+ border-color: var(--primary-light2, rgba(100, 100, 100, 0.55));
375
+ outline: none;
376
+ }
377
+
378
+ .editable :global(.tiptap-media-resize-handle:active) {
379
+ transform: translateY(1px);
380
+ }
381
+
382
+ .editable :global(.tiptap-media-resize-proxy) {
383
+ width: 100%;
384
+ border-radius: 12px;
385
+ background: var(--primary-light4, rgba(120, 120, 120, 0.35));
386
+ opacity: 0.55;
387
+ pointer-events: none;
388
+ }
389
+
390
+ div > :global(div) {
391
+ outline: none !important;
392
+ & :global(.ProseMirror) :global(p.is-editor-empty:first-child::before) {
393
+ color: var(--on-surface, #000);
394
+ opacity: 0.7;
395
+ content: attr(data-placeholder);
396
+ float: left;
397
+ height: 0;
398
+ pointer-events: none;
399
+ transition: 0.2s opacity ease-in-out;
400
+ }
401
+
402
+ & :global(.ProseMirror-focused) :global(p.is-editor-empty:first-child::before) {
403
+ opacity: 0;
404
+ }
405
+
406
+ & :global(a) {
407
+ cursor: pointer;
408
+ }
409
+
410
+ & :global(img) {
411
+ transition: all 0.2s ease-in-out;
412
+ max-width: 100%;
413
+ border-radius: 12px;
414
+ position: relative;
415
+ }
416
+
417
+ & :global(code.inline) {
418
+ background: var(--primary-light1);
419
+ padding: 2px 4px;
420
+ border-radius: 4px;
421
+ }
422
+
423
+ & :global(pre) {
424
+ background: var(--primary-light1);
425
+ padding: 12px;
426
+ border-radius: 12px;
427
+ max-width: 100%;
428
+ }
429
+
430
+ & :global(.tiptap-code-block) {
431
+ max-width: 100%;
432
+ position: relative;
433
+ }
434
+
435
+ & :global(.tiptap-code-block-language) {
436
+ padding: 2px 6px;
437
+ border-radius: 8px;
438
+ border: 1px solid var(--primary-light2, #ddd);
439
+ outline: 1px solid transparent;
440
+ outline-offset: 0;
441
+ background: var(--surface, #fff);
442
+ color: var(--on-surface, #000);
443
+ font-size: 0.75em;
444
+
445
+ &:focus,
446
+ &:focus-visible {
447
+ outline-color: var(--primary-light9, #89a2d9);
448
+ }
449
+ }
450
+
451
+ & :global(table) {
452
+ border-collapse: collapse;
453
+ width: 100%;
454
+ margin: 8px 0;
455
+ border: 1px solid var(--primary-light1);
456
+ border-radius: 12px;
457
+ }
458
+
459
+ & :global(th),
460
+ & :global(td) {
461
+ padding: 8px;
462
+ border: 1px solid var(--primary-light1);
463
+ }
464
+
465
+ & :global(.math-render) {
466
+ cursor: initial;
467
+ }
468
+
469
+ & :global(lite-youtube) {
470
+ border-radius: 12px;
471
+ }
472
+
473
+ & :global(.iframe-wrapper) {
474
+ position: relative;
475
+ padding-bottom: 12px;
476
+ overflow: hidden;
477
+ width: 100%;
478
+ height: 600px;
479
+ border-radius: 12px;
480
+ }
481
+
482
+ & :global(iframe) {
483
+ position: absolute;
484
+ top: 0;
485
+ left: 0;
486
+ width: 100%;
487
+ height: 100%;
488
+ }
489
+ }
490
+ </style>
@@ -1,6 +1,7 @@
1
1
  import '@seorii/prosemirror-math/style.css';
2
2
  import type { UploadFn } from '../plugin/image/dragdrop';
3
- declare const TipTap: import("svelte").Component<{
3
+ import { type ResizeOptions } from '../plugin/resize';
4
+ type Props = {
4
5
  body: string;
5
6
  editable?: boolean;
6
7
  mark?: boolean;
@@ -11,10 +12,16 @@ declare const TipTap: import("svelte").Component<{
11
12
  style?: string;
12
13
  blocks?: any[];
13
14
  placeholder?: string;
15
+ locale?: string;
14
16
  sanitize?: Record<string, any>;
15
17
  colors?: string[];
16
18
  bubble?: any;
19
+ bubbleDocked?: boolean;
17
20
  preloader?: any;
18
- crossorigin?: "anonymous" | "use-credentials";
19
- }, {}, "body" | "ref" | "loaded">;
21
+ crossorigin?: 'anonymous' | 'use-credentials';
22
+ codeBlockLanguageLabels?: Record<string, string>;
23
+ resize?: boolean | ResizeOptions;
24
+ };
25
+ declare const TipTap: import("svelte").Component<Props, {}, "body" | "ref" | "loaded">;
26
+ type TipTap = ReturnType<typeof TipTap>;
20
27
  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
- return editor && prop && tiptap.isActive(prop, attrs);
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(() => tiptap.chain().focus()[methodName](attrs)?.run(), 0);
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()} onclick={toggle} {tooltip} tabindex="0" />
52
+ <IconButton size="1.2em" {icon} active={isActive} onclick={toggle} {tooltip} tabindex={0} />
33
53
  {:else}
34
- <Button outlined={!isActive()} onclick={handler || toggle} small {...rest}>
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
- declare const ToolbarButton: import("svelte").Component<{
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?: unknown;
7
- tooltip: unknown;
8
- handler: unknown;
9
- } & Record<string, unknown>, {}, "">;
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;
@@ -1 +1,2 @@
1
- export default function setMath(tiptap: any): void;
1
+ import type { Editor } from '@tiptap/core';
2
+ export default function setMath(tiptap: Editor): void;