@pixerate/editor-svelte 0.24.0 → 0.25.1

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 (60) hide show
  1. package/README.md +3 -1
  2. package/dist/ExportDocxButton.svelte +48 -0
  3. package/dist/ExportDocxButton.svelte.d.ts +13 -0
  4. package/dist/ExportDocxButton.svelte.d.ts.map +1 -0
  5. package/dist/canvas/AnimationTimeline.svelte +217 -0
  6. package/dist/canvas/AnimationTimeline.svelte.d.ts +20 -0
  7. package/dist/canvas/AnimationTimeline.svelte.d.ts.map +1 -0
  8. package/dist/canvas/CanvasComponentsPanel.svelte +73 -0
  9. package/dist/canvas/CanvasComponentsPanel.svelte.d.ts +13 -0
  10. package/dist/canvas/CanvasComponentsPanel.svelte.d.ts.map +1 -0
  11. package/dist/canvas/CanvasInstanceInspector.svelte +136 -0
  12. package/dist/canvas/CanvasInstanceInspector.svelte.d.ts +16 -0
  13. package/dist/canvas/CanvasInstanceInspector.svelte.d.ts.map +1 -0
  14. package/dist/canvas/createCanvasAnimation.svelte.d.ts +98 -0
  15. package/dist/canvas/createCanvasAnimation.svelte.d.ts.map +1 -0
  16. package/dist/canvas/createCanvasAnimation.svelte.js +79 -0
  17. package/dist/canvas/createCanvasArrange.svelte.d.ts.map +1 -1
  18. package/dist/canvas/createCanvasAutoLayout.svelte.d.ts.map +1 -1
  19. package/dist/canvas/createCanvasComponents.svelte.d.ts +68 -0
  20. package/dist/canvas/createCanvasComponents.svelte.d.ts.map +1 -0
  21. package/dist/canvas/createCanvasComponents.svelte.js +31 -0
  22. package/dist/canvas/index.d.ts +7 -0
  23. package/dist/canvas/index.d.ts.map +1 -1
  24. package/dist/canvas/index.js +9 -0
  25. package/dist/docxExport.svelte.d.ts +26 -0
  26. package/dist/docxExport.svelte.d.ts.map +1 -0
  27. package/dist/docxExport.svelte.js +36 -0
  28. package/dist/index.d.ts +5 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +3 -0
  31. package/dist/spreadsheet/SpreadsheetEditor.svelte +77 -7
  32. package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts +6 -0
  33. package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts.map +1 -1
  34. package/dist/spreadsheet/SpreadsheetGroupRow.svelte +31 -3
  35. package/dist/spreadsheet/SpreadsheetGroupRow.svelte.d.ts +6 -0
  36. package/dist/spreadsheet/SpreadsheetGroupRow.svelte.d.ts.map +1 -1
  37. package/dist/spreadsheet/index.d.ts +1 -0
  38. package/dist/spreadsheet/index.d.ts.map +1 -1
  39. package/dist/spreadsheet/index.js +1 -0
  40. package/dist/spreadsheet/spreadsheet.svelte.d.ts +1 -1
  41. package/dist/spreadsheet/spreadsheet.svelte.d.ts.map +1 -1
  42. package/dist/spreadsheet/viewport.svelte.d.ts +26 -0
  43. package/dist/spreadsheet/viewport.svelte.d.ts.map +1 -0
  44. package/dist/spreadsheet/viewport.svelte.js +93 -0
  45. package/dist/tables/InsertTableGrid.svelte +85 -0
  46. package/dist/tables/InsertTableGrid.svelte.d.ts +21 -0
  47. package/dist/tables/InsertTableGrid.svelte.d.ts.map +1 -0
  48. package/dist/tables/TableBubbleMenu.svelte +43 -0
  49. package/dist/tables/TableBubbleMenu.svelte.d.ts +18 -0
  50. package/dist/tables/TableBubbleMenu.svelte.d.ts.map +1 -0
  51. package/dist/tables/TableToolbar.svelte +81 -0
  52. package/dist/tables/TableToolbar.svelte.d.ts +19 -0
  53. package/dist/tables/TableToolbar.svelte.d.ts.map +1 -0
  54. package/dist/tables/index.d.ts +12 -0
  55. package/dist/tables/index.d.ts.map +1 -0
  56. package/dist/tables/index.js +4 -0
  57. package/dist/tables/tableMenu.svelte.d.ts +21 -0
  58. package/dist/tables/tableMenu.svelte.d.ts.map +1 -0
  59. package/dist/tables/tableMenu.svelte.js +27 -0
  60. package/package.json +2 -2
@@ -32,6 +32,7 @@
32
32
  import SheetTabs from './SheetTabs.svelte';
33
33
  import { createReactiveSpreadsheet } from './spreadsheet.svelte.js';
34
34
  import { attachSpreadsheetClipboard } from './tableInteractions.js';
35
+ import { createSpreadsheetViewport } from './viewport.svelte.js';
35
36
 
36
37
  interface Props {
37
38
  sheetState?: ReturnType<typeof createReactiveSpreadsheet>;
@@ -47,6 +48,12 @@
47
48
  onColumnResize?: (colId: string, width: number) => void;
48
49
  /** Show the sheet tabs bar under the grid (add, switch, rename, reorder, delete sheets). */
49
50
  showSheetTabs?: boolean;
51
+ /**
52
+ * Render only the rows and columns in (or near) view. `'auto'` (default)
53
+ * virtualises sheets with more than 200 displayed rows or 60 columns;
54
+ * `true` / `false` force it.
55
+ */
56
+ virtualize?: boolean | 'auto';
50
57
  }
51
58
 
52
59
  let {
@@ -59,9 +66,51 @@
59
66
  onAddColumnClick,
60
67
  onColumnHeaderClick,
61
68
  onColumnResize,
62
- showSheetTabs = false
69
+ showSheetTabs = false,
70
+ virtualize = 'auto'
63
71
  }: Props = $props();
64
72
 
73
+ // Virtualised rendering: frozen rows/columns, a spacer, the rows and
74
+ // columns in view, another spacer (everything for small sheets).
75
+ type ColumnSlot = { kind: 'col'; key: string; viewCol: SpreadsheetViewColumn } | { kind: 'space'; key: string; width: number };
76
+ type RowSlot = { kind: 'row'; key: string; viewRow: SpreadsheetViewRow } | { kind: 'space'; key: string; height: number };
77
+ let scrollContainer = $state<HTMLDivElement | null>(null);
78
+ const viewportState = createSpreadsheetViewport({
79
+ view: () => sheetState.viewModel,
80
+ virtualize: () => virtualize,
81
+ merges: () => sheetState.document.table?.merges,
82
+ activeCell: () => sheetState.activeCell
83
+ });
84
+ $effect(() => viewportState.attach(scrollContainer));
85
+ let viewWindow = $derived(viewportState.window);
86
+ let columnSlots = $derived.by<ColumnSlot[]>(() => {
87
+ const view = sheetState.viewModel;
88
+ const win = viewWindow;
89
+ const col = (viewCol: SpreadsheetViewColumn): ColumnSlot => ({ kind: 'col', key: viewCol.column.id, viewCol });
90
+ if (!win.virtual) return view.columns.map(col);
91
+ const slots = view.columns.slice(0, win.frozenColumns).map(col);
92
+ if (win.leftSpace > 0) slots.push({ kind: 'space', key: 'space:left', width: win.leftSpace });
93
+ for (let i = win.colStart; i < win.colEnd; i++) slots.push(col(view.columns[i]));
94
+ if (win.rightSpace > 0) slots.push({ kind: 'space', key: 'space:right', width: win.rightSpace });
95
+ return slots;
96
+ });
97
+ let renderedColumns = $derived(columnSlots.flatMap((slot) => (slot.kind === 'col' ? [slot.viewCol] : [])));
98
+ let rowSlots = $derived.by<RowSlot[]>(() => {
99
+ const view = sheetState.viewModel;
100
+ const win = viewWindow;
101
+ const row = (viewRow: SpreadsheetViewRow): RowSlot => ({
102
+ kind: 'row',
103
+ key: viewRow.kind === 'group' ? `group:${viewRow.group.key}` : viewRow.row.id,
104
+ viewRow
105
+ });
106
+ if (!win.virtual) return view.rows.map(row);
107
+ const slots = view.rows.slice(0, win.frozenRows).map(row);
108
+ if (win.topSpace > 0) slots.push({ kind: 'space', key: 'space:top', height: win.topSpace });
109
+ for (let i = win.rowStart; i < win.rowEnd; i++) slots.push(row(view.rows[i]));
110
+ if (win.bottomSpace > 0) slots.push({ kind: 'space', key: 'space:bottom', height: win.bottomSpace });
111
+ return slots;
112
+ });
113
+
65
114
  // Defined names for formula autocomplete in the cell editor.
66
115
  let nameSuggestions = $derived.by(() => {
67
116
  void sheetState.document;
@@ -554,8 +603,8 @@
554
603
  {/if}
555
604
 
556
605
  <!-- Main Scrollable Grid -->
557
- <div class="relative flex-1 overflow-auto">
558
- <table class="border-collapse table-fixed w-max min-w-full">
606
+ <div bind:this={scrollContainer} class="relative flex-1 overflow-auto" onscroll={viewportState.onScroll}>
607
+ <table class="border-separate border-spacing-0 table-fixed w-max min-w-full [&>*>tr:not([data-spacer])>*]:border-b [&>*>tr>*]:border-border">
559
608
  <!-- Table Header Columns -->
560
609
  <thead>
561
610
  <tr class="h-7 bg-muted sticky top-0 z-20 border-b border-border shadow-xs">
@@ -563,7 +612,11 @@
563
612
  <th class="w-12 min-w-[3rem] sticky left-0 z-30 bg-muted border-r border-border text-center text-[10px] text-muted-foreground font-medium">
564
613
  #
565
614
  </th>
566
- {#each sheetState.viewModel.columns as viewCol (viewCol.column.id)}
615
+ {#each columnSlots as slot (slot.key)}
616
+ {#if slot.kind === 'space'}
617
+ <th aria-hidden="true" data-spacer="" class="p-0" style="width: {slot.width}px; min-width: {slot.width}px; max-width: {slot.width}px; padding: 0;"></th>
618
+ {:else}
619
+ {@const viewCol = slot.viewCol}
567
620
  {@const col = viewCol.column}
568
621
  {@const cIdx = viewCol.index}
569
622
  <th
@@ -599,6 +652,7 @@
599
652
  ></div>
600
653
  </div>
601
654
  </th>
655
+ {/if}
602
656
  {/each}
603
657
  {#if onAddColumnClick}
604
658
  <th class="w-10 min-w-[2.5rem] px-1 text-center bg-muted/40 hover:bg-muted cursor-pointer transition-colors" onclick={onAddColumnClick} title="Add Column">
@@ -612,11 +666,21 @@
612
666
 
613
667
  <!-- Table Body Rows -->
614
668
  <tbody>
615
- {#each sheetState.viewModel.rows as viewRow (viewRow.kind === 'group' ? `group:${viewRow.group.key}` : viewRow.row.id)}
669
+ {#each rowSlots as rowSlot (rowSlot.key)}
670
+ {#if rowSlot.kind === 'space'}
671
+ <tr aria-hidden="true" data-spacer="" style="height: {rowSlot.height}px;">
672
+ <td colspan={columnSlots.length + 3} style="padding: 0; border: 0;"></td>
673
+ </tr>
674
+ {:else}
675
+ {@const viewRow = rowSlot.viewRow}
616
676
  {#if viewRow.kind === 'group'}
617
677
  <SpreadsheetGroupRow
618
678
  group={viewRow.group}
619
- columns={sheetState.viewModel.columns}
679
+ columns={renderedColumns}
680
+ labelColumns={sheetState.viewModel.columns}
681
+ leadingColumns={viewWindow.virtual ? viewWindow.frozenColumns : undefined}
682
+ leftSpace={viewWindow.leftSpace}
683
+ rightSpace={viewWindow.rightSpace}
620
684
  groupColId={sheetState.document.table?.grouping?.colId ?? ''}
621
685
  onToggle={sheetState.toggleGroupCollapsed}
622
686
  trailingCell={!!onAddColumnClick}
@@ -637,7 +701,11 @@
637
701
  </td>
638
702
 
639
703
  <!-- Cells (a merged cell renders its top-left cell, spanning the merge) -->
640
- {#each sheetState.viewModel.columns as viewCol (viewCol.column.id)}
704
+ {#each columnSlots as slot (slot.key)}
705
+ {#if slot.kind === 'space'}
706
+ <td aria-hidden="true" data-spacer="" style="padding: 0;"></td>
707
+ {:else}
708
+ {@const viewCol = slot.viewCol}
641
709
  {@const span = sheetState.viewModel.getCellSpan(displayRowIndex, viewCol.index)}
642
710
  {#if span}
643
711
  {@const rIdx = span.source.row}
@@ -752,6 +820,7 @@
752
820
  {/if}
753
821
  </td>
754
822
  {/if}
823
+ {/if}
755
824
  {/each}
756
825
  {#if onAddColumnClick}
757
826
  <td class="w-10 min-w-[2.5rem] bg-muted/10 border-r border-border"></td>
@@ -760,6 +829,7 @@
760
829
  <td class="border-b border-border bg-transparent p-0"></td>
761
830
  </tr>
762
831
  {/if}
832
+ {/if}
763
833
  {/each}
764
834
  </tbody>
765
835
  </table>
@@ -17,6 +17,12 @@ interface Props {
17
17
  onColumnResize?: (colId: string, width: number) => void;
18
18
  /** Show the sheet tabs bar under the grid (add, switch, rename, reorder, delete sheets). */
19
19
  showSheetTabs?: boolean;
20
+ /**
21
+ * Render only the rows and columns in (or near) view. `'auto'` (default)
22
+ * virtualises sheets with more than 200 displayed rows or 60 columns;
23
+ * `true` / `false` force it.
24
+ */
25
+ virtualize?: boolean | 'auto';
20
26
  }
21
27
  declare const SpreadsheetEditor: import("svelte").Component<Props, {}, "">;
22
28
  type SpreadsheetEditor = ReturnType<typeof SpreadsheetEditor>;
@@ -1 +1 @@
1
- {"version":3,"file":"SpreadsheetEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/spreadsheet/SpreadsheetEditor.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAMH,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,QAAQ,EAad,MAAM,8BAA8B,CAAC;AASxC,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAIlE,UAAU,KAAK;IACb,UAAU,CAAC,EAAE,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC;IAC1D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EACf,OAAO,CAAC,CAAC,QAAQ,EAAE,iBAAiB,EAAE,cAAc,CAAC,CAAC,GACtD,OAAO,CAAC,CAAC;QAAE,GAAG,EAAE,cAAc,CAAC;QAAC,GAAG,EAAE,iBAAiB,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC,CAAC;IAC/E,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACvD,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,4FAA4F;IAC5F,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAwrBH,QAAA,MAAM,iBAAiB,2CAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"SpreadsheetEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/spreadsheet/SpreadsheetEditor.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAMH,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,QAAQ,EAad,MAAM,8BAA8B,CAAC;AASxC,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAKlE,UAAU,KAAK;IACb,UAAU,CAAC,EAAE,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC;IAC1D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EACf,OAAO,CAAC,CAAC,QAAQ,EAAE,iBAAiB,EAAE,cAAc,CAAC,CAAC,GACtD,OAAO,CAAC,CAAC;QAAE,GAAG,EAAE,cAAc,CAAC;QAAC,GAAG,EAAE,iBAAiB,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC,CAAC;IAC/E,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACvD,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,4FAA4F;IAC5F,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAC/B;AAsvBH,QAAA,MAAM,iBAAiB,2CAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
@@ -9,12 +9,31 @@
9
9
  onToggle: (key: string) => void;
10
10
  /** Render a trailing cell for the "add column" column. */
11
11
  trailingCell?: boolean;
12
+ /** Every displayed column, when `columns` is a virtualised window of them (picks the label column). */
13
+ labelColumns?: SpreadsheetViewColumn[];
14
+ /** Virtualised: columns before the left spacer (the frozen ones), and the spacers' widths. */
15
+ leadingColumns?: number;
16
+ leftSpace?: number;
17
+ rightSpace?: number;
12
18
  }
13
19
 
14
- let { group, columns, groupColId, onToggle, trailingCell = false }: Props = $props();
20
+ let {
21
+ group,
22
+ columns,
23
+ groupColId,
24
+ onToggle,
25
+ trailingCell = false,
26
+ labelColumns,
27
+ leadingColumns,
28
+ leftSpace = 0,
29
+ rightSpace = 0
30
+ }: Props = $props();
15
31
 
16
32
  // The label goes in the grouping column, or the first column when that one is hidden.
17
- let labelColId = $derived(columns.some((c) => c.column.id === groupColId) ? groupColId : columns[0]?.column.id);
33
+ let labelColId = $derived.by(() => {
34
+ const candidates = labelColumns ?? columns;
35
+ return candidates.some((c) => c.column.id === groupColId) ? groupColId : candidates[0]?.column.id;
36
+ });
18
37
 
19
38
  const formatSummary = (value: number | null) => (value === null ? '–' : String(value));
20
39
  </script>
@@ -35,8 +54,11 @@
35
54
  {group.collapsed ? '▸' : '▾'}
36
55
  </button>
37
56
  </td>
38
- {#each columns as { column } (column.id)}
57
+ {#each columns as { column }, i (column.id)}
39
58
  {@const summary = group.summaries[column.id]}
59
+ {#if leadingColumns !== undefined && i === leadingColumns && leftSpace > 0}
60
+ <td aria-hidden="true" data-spacer="" style="padding: 0;"></td>
61
+ {/if}
40
62
  <td class="border-r border-border px-2 py-1 truncate max-w-0 text-foreground" style="max-width: 0; overflow: hidden;">
41
63
  {#if column.id === labelColId}
42
64
  <span class="truncate">
@@ -47,6 +69,12 @@
47
69
  {/if}
48
70
  </td>
49
71
  {/each}
72
+ {#if leadingColumns !== undefined && leadingColumns >= columns.length && leftSpace > 0}
73
+ <td aria-hidden="true" data-spacer="" style="padding: 0;"></td>
74
+ {/if}
75
+ {#if rightSpace > 0}
76
+ <td aria-hidden="true" data-spacer="" style="padding: 0;"></td>
77
+ {/if}
50
78
  {#if trailingCell}
51
79
  <td class="w-10 min-w-[2.5rem] bg-muted/10 border-r border-border"></td>
52
80
  {/if}
@@ -7,6 +7,12 @@ interface Props {
7
7
  onToggle: (key: string) => void;
8
8
  /** Render a trailing cell for the "add column" column. */
9
9
  trailingCell?: boolean;
10
+ /** Every displayed column, when `columns` is a virtualised window of them (picks the label column). */
11
+ labelColumns?: SpreadsheetViewColumn[];
12
+ /** Virtualised: columns before the left spacer (the frozen ones), and the spacers' widths. */
13
+ leadingColumns?: number;
14
+ leftSpace?: number;
15
+ rightSpace?: number;
10
16
  }
11
17
  declare const SpreadsheetGroupRow: import("svelte").Component<Props, {}, "">;
12
18
  type SpreadsheetGroupRow = ReturnType<typeof SpreadsheetGroupRow>;
@@ -1 +1 @@
1
- {"version":3,"file":"SpreadsheetGroupRow.svelte.d.ts","sourceRoot":"","sources":["../../src/spreadsheet/SpreadsheetGroupRow.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAG7F,UAAU,KAAK;IACb,KAAK,EAAE,mBAAmB,CAAC;IAC3B,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,0DAA0D;IAC1D,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AA4CH,QAAA,MAAM,mBAAmB,2CAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"SpreadsheetGroupRow.svelte.d.ts","sourceRoot":"","sources":["../../src/spreadsheet/SpreadsheetGroupRow.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAG7F,UAAU,KAAK;IACb,KAAK,EAAE,mBAAmB,CAAC;IAC3B,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,0DAA0D;IAC1D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,uGAAuG;IACvG,YAAY,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACvC,8FAA8F;IAC9F,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAkEH,QAAA,MAAM,mBAAmB,2CAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
@@ -21,4 +21,5 @@ export type FormulaBarProps = ComponentProps<typeof FormulaBarComponent>;
21
21
  export type FormulaHintsProps = ComponentProps<typeof FormulaHintsComponent>;
22
22
  export type SheetTabsProps = ComponentProps<typeof SheetTabsComponent>;
23
23
  export type NameManagerProps = ComponentProps<typeof NameManagerComponent>;
24
+ export * from './viewport.svelte.js';
24
25
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/spreadsheet/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,0BAA0B,MAAM,4BAA4B,CAAC;AACzE,OAAO,KAAK,mBAAmB,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,kBAAkB,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;AAE7D,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAC9E,cAAc,wBAAwB,CAAC;AACvC,OAAO,EAAE,OAAO,IAAI,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AAC9F,OAAO,EAAE,OAAO,IAAI,iCAAiC,EAAE,MAAM,4CAA4C,CAAC;AAC1G,OAAO,EAAE,OAAO,IAAI,8BAA8B,EAAE,MAAM,yCAAyC,CAAC;AAEpG,MAAM,MAAM,sBAAsB,GAAG,cAAc,CAAC,OAAO,0BAA0B,CAAC,CAAC;AACvF,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACzE,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,MAAM,MAAM,cAAc,GAAG,cAAc,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACvE,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/spreadsheet/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,0BAA0B,MAAM,4BAA4B,CAAC;AACzE,OAAO,KAAK,mBAAmB,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,kBAAkB,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;AAE7D,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAC9E,cAAc,wBAAwB,CAAC;AACvC,OAAO,EAAE,OAAO,IAAI,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AAC9F,OAAO,EAAE,OAAO,IAAI,iCAAiC,EAAE,MAAM,4CAA4C,CAAC;AAC1G,OAAO,EAAE,OAAO,IAAI,8BAA8B,EAAE,MAAM,yCAAyC,CAAC;AAEpG,MAAM,MAAM,sBAAsB,GAAG,cAAc,CAAC,OAAO,0BAA0B,CAAC,CAAC;AACvF,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACzE,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,MAAM,MAAM,cAAc,GAAG,cAAc,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACvE,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAC3E,cAAc,sBAAsB,CAAC"}
@@ -10,3 +10,4 @@ export * from './tableInteractions.js';
10
10
  export { default as SpreadsheetValidationDialog } from './SpreadsheetValidationDialog.svelte';
11
11
  export { default as SpreadsheetConditionalFormatPanel } from './SpreadsheetConditionalFormatPanel.svelte';
12
12
  export { default as SpreadsheetDataValidationPanel } from './SpreadsheetDataValidationPanel.svelte';
13
+ export * from './viewport.svelte.js';
@@ -1,5 +1,6 @@
1
1
  import { SpreadsheetController, type SpreadsheetControllerOptions, type SpreadsheetDocument, type CellCoordinate, type CellRange, type CellData, type SpreadsheetColumn, type SpreadsheetRow, type SpreadsheetDataSource, type SpreadsheetNameScope, type SpreadsheetCommitResult, type SpreadsheetValidationAction, type SpreadsheetValidationPrompt } from '@pixerate/editor/spreadsheet';
2
2
  export declare function createReactiveSpreadsheet(options?: SpreadsheetControllerOptions): {
3
+ mergeCells: (range?: CellRange | null) => boolean;
3
4
  sortRows: (keys: import("@pixerate/editor").SortKey[], options?: {
4
5
  range?: CellRange | null;
5
6
  }) => boolean;
@@ -23,7 +24,6 @@ export declare function createReactiveSpreadsheet(options?: SpreadsheetControlle
23
24
  unhideAll: () => void;
24
25
  getMerges: () => CellRange[];
25
26
  getMergeAt: (row: number, col: number) => CellRange | null;
26
- mergeCells: (range?: CellRange | null) => boolean;
27
27
  unmergeCells: (range?: CellRange | null) => boolean;
28
28
  autofill: (source: CellRange, target: CellRange) => boolean;
29
29
  groupRowsBy: (colId: string, summaries?: import("@pixerate/editor").GroupSummary[]) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"spreadsheet.svelte.d.ts","sourceRoot":"","sources":["../../src/spreadsheet/spreadsheet.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EAErB,KAAK,4BAA4B,EACjC,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EACjC,MAAM,8BAA8B,CAAC;AAEtC,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,4BAAiC;;aAyRuokD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAAgyW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAnKr+6D,MAAM;;;;;mBAMX,MAAM,SAAS,MAAM,GAAG,QAAQ;wBAG3B,MAAM,SAAS,MAAM,OAAO,MAAM;oBAItC,MAAM,OAAO,MAAM;uBAKhB,SAAS;uBAIT,MAAM,QAAQ,MAAM,eAAe,MAAM;;;wBAYxC,MAAM,GAAG,uBAAuB;IAKpD,+FAA+F;2BACxE,MAAM,SAAS,MAAM,OAAO,MAAM,GAAG,uBAAuB;IAKnF,qFAAqF;oCACrD,2BAA2B;;oBAS3C,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO;wBAK5B,MAAM,WAAW,OAAO,CAAC,iBAAiB,CAAC;wBAI3C,MAAM;qBAIT,MAAM,WAAW,OAAO,CAAC,cAAc,CAAC;qBAIxC,MAAM;0BAID,MAAM,SAAS,MAAM;8BAIjB,MAAM,gBAAgB,MAAM;2BAI/B,mBAAmB;;2BAanB,qBAAqB;6BAInB,MAAM,GAAG,SAAS;;wBAQvB,SAAS,GAAG,IAAI;wBAGhB,MAAM;;;6BAaD,MAAM;uBAKZ,UAAU;YAiCyowD,CAAC;aAAuB,CAAC;gBAA0B,CAAC;gBAAgD,CAAC;iBAjCltwD,CAAC,CAAC,CAAC;0BAKtC,MAAM,WAAW,MAAM;wBAGzB,MAAM,WAAW,MAAM;0BAGrB,MAAM;6BAKH,MAAM,YAAY,UAAU;YAiBsvyD,CAAC;aAAuB,CAAC;gBAA0B,CAAC;wBAjBxwyD,CAAC,CAAC,CAAC;qBAKzE,MAAM,YAAY,MAAM,YAAY,oBAAoB,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;wBAG5E,MAAM,WAAW,MAAM,YAAY,oBAAoB;qBAG1D,MAAM,YAAY,oBAAoB;EAK1D"}
1
+ {"version":3,"file":"spreadsheet.svelte.d.ts","sourceRoot":"","sources":["../../src/spreadsheet/spreadsheet.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EAErB,KAAK,4BAA4B,EACjC,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EACjC,MAAM,8BAA8B,CAAC;AAEtC,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,4BAAiC;;;aAyRmx0D,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAA06W,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAnK3vrE,MAAM;;;;;mBAMX,MAAM,SAAS,MAAM,GAAG,QAAQ;wBAG3B,MAAM,SAAS,MAAM,OAAO,MAAM;oBAItC,MAAM,OAAO,MAAM;uBAKhB,SAAS;uBAIT,MAAM,QAAQ,MAAM,eAAe,MAAM;;;wBAYxC,MAAM,GAAG,uBAAuB;IAKpD,+FAA+F;2BACxE,MAAM,SAAS,MAAM,OAAO,MAAM,GAAG,uBAAuB;IAKnF,qFAAqF;oCACrD,2BAA2B;;oBAS3C,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO;wBAK5B,MAAM,WAAW,OAAO,CAAC,iBAAiB,CAAC;wBAI3C,MAAM;qBAIT,MAAM,WAAW,OAAO,CAAC,cAAc,CAAC;qBAIxC,MAAM;0BAID,MAAM,SAAS,MAAM;8BAIjB,MAAM,gBAAgB,MAAM;2BAI/B,mBAAmB;;2BAanB,qBAAqB;6BAInB,MAAM,GAAG,SAAS;;wBAQvB,SAAS,GAAG,IAAI;wBAGhB,MAAM;;;6BAaD,MAAM;uBAKZ,UAAU;YAiCqxgE,CAAC;aAAuB,CAAC;gBAA0B,CAAC;gBAAgD,CAAC;iBAjC91gE,CAAC,CAAC,CAAC;0BAKtC,MAAM,WAAW,MAAM;wBAGzB,MAAM,WAAW,MAAM;0BAGrB,MAAM;6BAKH,MAAM,YAAY,UAAU;YAiBk4iE,CAAC;aAAuB,CAAC;gBAA0B,CAAC;wBAjBp5iE,CAAC,CAAC,CAAC;qBAKzE,MAAM,YAAY,MAAM,YAAY,oBAAoB,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;wBAG5E,MAAM,WAAW,MAAM,YAAY,oBAAoB;qBAG1D,MAAM,YAAY,oBAAoB;EAK1D"}
@@ -0,0 +1,26 @@
1
+ import { type CellCoordinate, type CellRange, type SpreadsheetViewModel } from '@pixerate/editor/spreadsheet';
2
+ export interface SpreadsheetViewportSource {
3
+ view: () => SpreadsheetViewModel;
4
+ /** `'auto'` (default): virtualise large views only; `true` / `false` force it. */
5
+ virtualize?: () => boolean | 'auto' | undefined;
6
+ /** Merged ranges, rendered whole when they cross the window's edge. */
7
+ merges?: () => readonly CellRange[] | undefined;
8
+ /** Scrolled into view when it changes (virtualised views only). */
9
+ activeCell?: () => CellCoordinate | null | undefined;
10
+ overscanRows?: number;
11
+ overscanColumns?: number;
12
+ }
13
+ /**
14
+ * Virtualised rendering for `SpreadsheetEditor` (the Svelte counterpart of
15
+ * React's `useSpreadsheetViewport`): tracks the scroll container passed to
16
+ * `attach`, works out the window of rows and columns to render, and scrolls
17
+ * the active cell into view. Call it while a component initialises.
18
+ */
19
+ export declare function createSpreadsheetViewport(source: SpreadsheetViewportSource): {
20
+ readonly metrics: import("@pixerate/editor").SpreadsheetViewMetrics;
21
+ /** What to render; `virtual: false` renders every row and column. */
22
+ readonly window: import("@pixerate/editor").SpreadsheetViewWindow;
23
+ onScroll: () => void;
24
+ attach: (el: HTMLElement | null) => () => void;
25
+ };
26
+ //# sourceMappingURL=viewport.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewport.svelte.d.ts","sourceRoot":"","sources":["../../src/spreadsheet/viewport.svelte.ts"],"names":[],"mappings":"AACA,OAAO,EAOL,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,oBAAoB,EAG1B,MAAM,8BAA8B,CAAC;AAEtC,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,oBAAoB,CAAC;IACjC,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IAChD,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,SAAS,SAAS,EAAE,GAAG,SAAS,CAAC;IAChD,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,cAAc,GAAG,IAAI,GAAG,SAAS,CAAC;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,yBAAyB;;IA4EvE,qEAAqE;;;iBAxCnD,WAAW,GAAG,IAAI,KAAG,MAAM,IAAI;EA+CpD"}
@@ -0,0 +1,93 @@
1
+ import { untrack } from 'svelte';
2
+ import { fullSpreadsheetViewWindow, getSpreadsheetScrollToCell, getSpreadsheetViewWindow, measureSpreadsheetView, sameSpreadsheetViewWindow, shouldVirtualizeSpreadsheet } from '@pixerate/editor/spreadsheet';
3
+ /**
4
+ * Virtualised rendering for `SpreadsheetEditor` (the Svelte counterpart of
5
+ * React's `useSpreadsheetViewport`): tracks the scroll container passed to
6
+ * `attach`, works out the window of rows and columns to render, and scrolls
7
+ * the active cell into view. Call it while a component initialises.
8
+ */
9
+ export function createSpreadsheetViewport(source) {
10
+ let viewport = $state.raw({ scrollTop: 0, scrollLeft: 0, width: 0, height: 0 });
11
+ let element = null;
12
+ const metrics = $derived(measureSpreadsheetView(source.view()));
13
+ const virtual = $derived(shouldVirtualizeSpreadsheet(source.view(), source.virtualize?.() ?? 'auto'));
14
+ const windowOptions = $derived({
15
+ merges: source.merges?.(),
16
+ overscanRows: source.overscanRows,
17
+ overscanColumns: source.overscanColumns
18
+ });
19
+ const win = $derived(virtual ? getSpreadsheetViewWindow(metrics, viewport, windowOptions) : fullSpreadsheetViewWindow(metrics));
20
+ const read = (el) => ({
21
+ scrollTop: el.scrollTop,
22
+ scrollLeft: el.scrollLeft,
23
+ width: el.clientWidth,
24
+ height: el.clientHeight
25
+ });
26
+ /** Stores a new viewport, re-rendering only when the window it gives changes. */
27
+ function update(next) {
28
+ if (!virtual)
29
+ return;
30
+ const prev = viewport;
31
+ const same = prev.width === next.width &&
32
+ prev.height === next.height &&
33
+ sameSpreadsheetViewWindow(getSpreadsheetViewWindow(metrics, prev, windowOptions), getSpreadsheetViewWindow(metrics, next, windowOptions));
34
+ if (!same)
35
+ viewport = next;
36
+ }
37
+ function onScroll() {
38
+ if (element)
39
+ update(read(element));
40
+ }
41
+ /** Tracks `el` (the scroll container); returns a cleanup function. */
42
+ function attach(el) {
43
+ element = el;
44
+ if (!el)
45
+ return () => { };
46
+ onScroll();
47
+ if (typeof ResizeObserver === 'undefined')
48
+ return () => { };
49
+ const observer = new ResizeObserver(() => onScroll());
50
+ observer.observe(el);
51
+ return () => observer.disconnect();
52
+ }
53
+ // Measure once virtualisation turns on (the element may already be attached).
54
+ $effect(() => {
55
+ if (virtual)
56
+ untrack(onScroll);
57
+ });
58
+ // Keyboard navigation and selection: bring the active cell into view.
59
+ // Only the active cell and the mode re-run it: scrolling away from the
60
+ // active cell (or an edit elsewhere) must not snap back.
61
+ $effect(() => {
62
+ const cell = source.activeCell?.();
63
+ const row = cell?.row;
64
+ const col = cell?.col;
65
+ if (!virtual || row === undefined || col === undefined)
66
+ return;
67
+ untrack(() => {
68
+ const base = element ? read(element) : viewport;
69
+ const target = getSpreadsheetScrollToCell(metrics, base, { row, col });
70
+ if (!target)
71
+ return;
72
+ if (element) {
73
+ if (target.scrollTop !== undefined)
74
+ element.scrollTop = target.scrollTop;
75
+ if (target.scrollLeft !== undefined)
76
+ element.scrollLeft = target.scrollLeft;
77
+ }
78
+ // Some environments (jsdom) never fire the scroll event.
79
+ update({ ...base, ...target });
80
+ });
81
+ });
82
+ return {
83
+ get metrics() {
84
+ return metrics;
85
+ },
86
+ /** What to render; `virtual: false` renders every row and column. */
87
+ get window() {
88
+ return win;
89
+ },
90
+ onScroll,
91
+ attach
92
+ };
93
+ }
@@ -0,0 +1,85 @@
1
+ <script lang="ts">
2
+ import type { Editor } from "@tiptap/core";
3
+ import { createTableMenu, type SvelteTableMenu } from "./tableMenu.svelte.js";
4
+
5
+ interface Props {
6
+ editor?: Editor | null;
7
+ /** Grid size offered (default 8 × 8). */
8
+ rows?: number;
9
+ cols?: number;
10
+ /** Insert with a header row (default `true`). */
11
+ withHeaderRow?: boolean;
12
+ class?: string;
13
+ cellClass?: string;
14
+ activeCellClass?: string;
15
+ /** Called after a table was inserted. */
16
+ oninsert?: (size: { rows: number; cols: number }) => void;
17
+ }
18
+
19
+ let {
20
+ editor = null,
21
+ rows = 8,
22
+ cols = 8,
23
+ withHeaderRow = true,
24
+ class: className = "inline-flex flex-col gap-1 rounded-md border border-border bg-background p-2 text-xs shadow-md",
25
+ cellClass = "h-4 w-4 rounded-sm border border-border bg-background disabled:opacity-40",
26
+ activeCellClass = "border-primary bg-primary/20",
27
+ oninsert,
28
+ }: Props = $props();
29
+
30
+ let table = $state.raw<SvelteTableMenu>(createTableMenu(null));
31
+ $effect(() => {
32
+ const menu = createTableMenu(editor);
33
+ table = menu;
34
+ return () => menu.destroy();
35
+ });
36
+
37
+ let hover = $state<{ rows: number; cols: number } | null>(null);
38
+ let status = $derived(table.current.actions.insertTable);
39
+ let disabled = $derived(!editor || !status.enabled);
40
+ let sizes = $derived(
41
+ Array.from({ length: rows * cols }, (_, i) => ({ rows: Math.floor(i / cols) + 1, cols: (i % cols) + 1 })),
42
+ );
43
+
44
+ const keepSelection = (event: MouseEvent) => event.preventDefault();
45
+
46
+ function insert(size: { rows: number; cols: number }) {
47
+ if (table.run("insertTable", { ...size, withHeaderRow })) oninsert?.(size);
48
+ hover = null;
49
+ }
50
+ </script>
51
+
52
+ <div
53
+ class={className}
54
+ role="group"
55
+ aria-label="Insert table"
56
+ title={disabled ? status.reason : undefined}
57
+ data-insert-table-grid=""
58
+ onmouseleave={() => (hover = null)}
59
+ >
60
+ <div
61
+ role="grid"
62
+ aria-label="Table size"
63
+ style="display: grid; grid-template-columns: repeat({cols}, max-content); gap: 2px"
64
+ >
65
+ {#each sizes as size, i (i)}
66
+ {@const active = !!hover && size.rows <= hover.rows && size.cols <= hover.cols}
67
+ <button
68
+ type="button"
69
+ data-rows={size.rows}
70
+ data-cols={size.cols}
71
+ data-active={active}
72
+ aria-label="{size.rows} × {size.cols} table"
73
+ {disabled}
74
+ class={active ? `${cellClass} ${activeCellClass}` : cellClass}
75
+ onmousedown={keepSelection}
76
+ onmouseenter={() => (hover = size)}
77
+ onfocus={() => (hover = size)}
78
+ onclick={() => insert(size)}
79
+ ></button>
80
+ {/each}
81
+ </div>
82
+ <div aria-live="polite" data-testid="insert-table-size">
83
+ {disabled ? status.reason : hover ? `${hover.rows} × ${hover.cols}` : "Insert table"}
84
+ </div>
85
+ </div>
@@ -0,0 +1,21 @@
1
+ import type { Editor } from "@tiptap/core";
2
+ interface Props {
3
+ editor?: Editor | null;
4
+ /** Grid size offered (default 8 × 8). */
5
+ rows?: number;
6
+ cols?: number;
7
+ /** Insert with a header row (default `true`). */
8
+ withHeaderRow?: boolean;
9
+ class?: string;
10
+ cellClass?: string;
11
+ activeCellClass?: string;
12
+ /** Called after a table was inserted. */
13
+ oninsert?: (size: {
14
+ rows: number;
15
+ cols: number;
16
+ }) => void;
17
+ }
18
+ declare const InsertTableGrid: import("svelte").Component<Props, {}, "">;
19
+ type InsertTableGrid = ReturnType<typeof InsertTableGrid>;
20
+ export default InsertTableGrid;
21
+ //# sourceMappingURL=InsertTableGrid.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InsertTableGrid.svelte.d.ts","sourceRoot":"","sources":["../../src/tables/InsertTableGrid.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAIzC,UAAU,KAAK;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC3D;AAqDH,QAAA,MAAM,eAAe,2CAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
@@ -0,0 +1,43 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import type { Editor } from "@tiptap/core";
4
+ import { shouldShowTableBubbleMenu, type TableToolbarItem } from "@pixerate/editor";
5
+ import BubbleMenu from "../BubbleMenu.svelte";
6
+ import TableToolbar from "./TableToolbar.svelte";
7
+
8
+ interface Props {
9
+ editor?: Editor;
10
+ class?: string;
11
+ pluginKey?: any;
12
+ updateDelay?: number;
13
+ tippyOptions?: Record<string, any>;
14
+ /** Buttons of the default toolbar. */
15
+ items?: readonly TableToolbarItem[];
16
+ /** Custom content (default: a `TableToolbar`). */
17
+ children?: Snippet;
18
+ }
19
+
20
+ let {
21
+ editor,
22
+ class: className = "flex h-fit w-fit items-center gap-1 rounded-md border border-gray-200 bg-white p-1 shadow-md",
23
+ pluginKey = "tableBubbleMenu",
24
+ updateDelay = 250,
25
+ tippyOptions = {},
26
+ items,
27
+ children,
28
+ }: Props = $props();
29
+ </script>
30
+
31
+ <!--
32
+ A bubble menu that follows the selection while it is in a table (even a
33
+ collapsed cursor), with the table toolbar or your own children. It uses its
34
+ own plugin key (`tableBubbleMenu`), so it can be combined with the
35
+ formatting `BubbleMenu`.
36
+ -->
37
+ <BubbleMenu {editor} class={className} {pluginKey} {updateDelay} {tippyOptions} shouldShow={shouldShowTableBubbleMenu}>
38
+ {#if children}
39
+ {@render children()}
40
+ {:else}
41
+ <TableToolbar {editor} {...items ? { items } : {}} />
42
+ {/if}
43
+ </BubbleMenu>
@@ -0,0 +1,18 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { Editor } from "@tiptap/core";
3
+ import { type TableToolbarItem } from "@pixerate/editor";
4
+ interface Props {
5
+ editor?: Editor;
6
+ class?: string;
7
+ pluginKey?: any;
8
+ updateDelay?: number;
9
+ tippyOptions?: Record<string, any>;
10
+ /** Buttons of the default toolbar. */
11
+ items?: readonly TableToolbarItem[];
12
+ /** Custom content (default: a `TableToolbar`). */
13
+ children?: Snippet;
14
+ }
15
+ declare const TableBubbleMenu: import("svelte").Component<Props, {}, "">;
16
+ type TableBubbleMenu = ReturnType<typeof TableBubbleMenu>;
17
+ export default TableBubbleMenu;
18
+ //# sourceMappingURL=TableBubbleMenu.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TableBubbleMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/tables/TableBubbleMenu.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAA6B,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAKlF,UAAU,KAAK;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC,sCAAsC;IACtC,KAAK,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACpC,kDAAkD;IAClD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA+BH,QAAA,MAAM,eAAe,2CAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}