@pixerate/editor-svelte 0.23.0 → 0.25.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 (147) hide show
  1. package/README.md +4 -2
  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/StylesGallery.svelte +169 -0
  6. package/dist/StylesGallery.svelte.d.ts +27 -0
  7. package/dist/StylesGallery.svelte.d.ts.map +1 -0
  8. package/dist/canvas/AnimationTimeline.svelte +217 -0
  9. package/dist/canvas/AnimationTimeline.svelte.d.ts +20 -0
  10. package/dist/canvas/AnimationTimeline.svelte.d.ts.map +1 -0
  11. package/dist/canvas/CanvasComponentsPanel.svelte +73 -0
  12. package/dist/canvas/CanvasComponentsPanel.svelte.d.ts +13 -0
  13. package/dist/canvas/CanvasComponentsPanel.svelte.d.ts.map +1 -0
  14. package/dist/canvas/CanvasInstanceInspector.svelte +136 -0
  15. package/dist/canvas/CanvasInstanceInspector.svelte.d.ts +16 -0
  16. package/dist/canvas/CanvasInstanceInspector.svelte.d.ts.map +1 -0
  17. package/dist/canvas/TokenBindingPicker.svelte +93 -0
  18. package/dist/canvas/TokenBindingPicker.svelte.d.ts +20 -0
  19. package/dist/canvas/TokenBindingPicker.svelte.d.ts.map +1 -0
  20. package/dist/canvas/TokenModeSwitcher.svelte +55 -0
  21. package/dist/canvas/TokenModeSwitcher.svelte.d.ts +18 -0
  22. package/dist/canvas/TokenModeSwitcher.svelte.d.ts.map +1 -0
  23. package/dist/canvas/createCanvasAnimation.svelte.d.ts +98 -0
  24. package/dist/canvas/createCanvasAnimation.svelte.d.ts.map +1 -0
  25. package/dist/canvas/createCanvasAnimation.svelte.js +79 -0
  26. package/dist/canvas/createCanvasArrange.svelte.d.ts +4 -0
  27. package/dist/canvas/createCanvasArrange.svelte.d.ts.map +1 -1
  28. package/dist/canvas/createCanvasAutoLayout.svelte.d.ts.map +1 -1
  29. package/dist/canvas/createCanvasComponents.svelte.d.ts +68 -0
  30. package/dist/canvas/createCanvasComponents.svelte.d.ts.map +1 -0
  31. package/dist/canvas/createCanvasComponents.svelte.js +31 -0
  32. package/dist/canvas/createCanvasTokens.svelte.d.ts +31 -0
  33. package/dist/canvas/createCanvasTokens.svelte.d.ts.map +1 -0
  34. package/dist/canvas/createCanvasTokens.svelte.js +50 -0
  35. package/dist/canvas/index.d.ts +12 -0
  36. package/dist/canvas/index.d.ts.map +1 -1
  37. package/dist/canvas/index.js +16 -0
  38. package/dist/docxExport.svelte.d.ts +26 -0
  39. package/dist/docxExport.svelte.d.ts.map +1 -0
  40. package/dist/docxExport.svelte.js +36 -0
  41. package/dist/fileImport.svelte.d.ts +8 -1
  42. package/dist/fileImport.svelte.d.ts.map +1 -1
  43. package/dist/fileImport.svelte.js +11 -1
  44. package/dist/generation/AssetMentionChips.svelte +130 -0
  45. package/dist/generation/AssetMentionChips.svelte.d.ts +21 -0
  46. package/dist/generation/AssetMentionChips.svelte.d.ts.map +1 -0
  47. package/dist/generation/GenerationHistory.svelte +161 -0
  48. package/dist/generation/GenerationHistory.svelte.d.ts +28 -0
  49. package/dist/generation/GenerationHistory.svelte.d.ts.map +1 -0
  50. package/dist/generation/index.d.ts +7 -0
  51. package/dist/generation/index.d.ts.map +1 -1
  52. package/dist/generation/index.js +3 -0
  53. package/dist/generation/records.svelte.d.ts +18 -0
  54. package/dist/generation/records.svelte.d.ts.map +1 -0
  55. package/dist/generation/records.svelte.js +66 -0
  56. package/dist/image-editor/ImageEditor.svelte +72 -1
  57. package/dist/image-editor/ImageEditor.svelte.d.ts.map +1 -1
  58. package/dist/image-editor/RefineEdgePanel.svelte +180 -0
  59. package/dist/image-editor/RefineEdgePanel.svelte.d.ts +13 -0
  60. package/dist/image-editor/RefineEdgePanel.svelte.d.ts.map +1 -0
  61. package/dist/image-editor/SelectionOptionsBar.svelte +135 -0
  62. package/dist/image-editor/SelectionOptionsBar.svelte.d.ts +15 -0
  63. package/dist/image-editor/SelectionOptionsBar.svelte.d.ts.map +1 -0
  64. package/dist/image-editor/index.d.ts +6 -0
  65. package/dist/image-editor/index.d.ts.map +1 -1
  66. package/dist/image-editor/index.js +2 -0
  67. package/dist/index.d.ts +9 -0
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +5 -0
  70. package/dist/paragraphStyles.svelte.d.ts +30 -0
  71. package/dist/paragraphStyles.svelte.d.ts.map +1 -0
  72. package/dist/paragraphStyles.svelte.js +44 -0
  73. package/dist/review/TrackedChangesList.svelte +67 -0
  74. package/dist/review/TrackedChangesList.svelte.d.ts +13 -0
  75. package/dist/review/TrackedChangesList.svelte.d.ts.map +1 -0
  76. package/dist/review/index.d.ts +3 -0
  77. package/dist/review/index.d.ts.map +1 -1
  78. package/dist/review/index.js +1 -0
  79. package/dist/review/review.svelte.d.ts +6 -0
  80. package/dist/review/review.svelte.d.ts.map +1 -1
  81. package/dist/review/review.svelte.js +3 -0
  82. package/dist/spreadsheet/FormulaBar.svelte +13 -4
  83. package/dist/spreadsheet/FormulaBar.svelte.d.ts.map +1 -1
  84. package/dist/spreadsheet/FormulaHints.svelte +7 -5
  85. package/dist/spreadsheet/FormulaHints.svelte.d.ts +3 -0
  86. package/dist/spreadsheet/FormulaHints.svelte.d.ts.map +1 -1
  87. package/dist/spreadsheet/NameManager.svelte +165 -0
  88. package/dist/spreadsheet/NameManager.svelte.d.ts +11 -0
  89. package/dist/spreadsheet/NameManager.svelte.d.ts.map +1 -0
  90. package/dist/spreadsheet/SheetTabs.svelte +161 -0
  91. package/dist/spreadsheet/SheetTabs.svelte.d.ts +10 -0
  92. package/dist/spreadsheet/SheetTabs.svelte.d.ts.map +1 -0
  93. package/dist/spreadsheet/SpreadsheetConditionalFormatPanel.svelte +184 -0
  94. package/dist/spreadsheet/SpreadsheetConditionalFormatPanel.svelte.d.ts +9 -0
  95. package/dist/spreadsheet/SpreadsheetConditionalFormatPanel.svelte.d.ts.map +1 -0
  96. package/dist/spreadsheet/SpreadsheetContextMenu.svelte +9 -4
  97. package/dist/spreadsheet/SpreadsheetContextMenu.svelte.d.ts +5 -1
  98. package/dist/spreadsheet/SpreadsheetContextMenu.svelte.d.ts.map +1 -1
  99. package/dist/spreadsheet/SpreadsheetDataValidationPanel.svelte +141 -0
  100. package/dist/spreadsheet/SpreadsheetDataValidationPanel.svelte.d.ts +9 -0
  101. package/dist/spreadsheet/SpreadsheetDataValidationPanel.svelte.d.ts.map +1 -0
  102. package/dist/spreadsheet/SpreadsheetEditor.svelte +273 -19
  103. package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts +8 -0
  104. package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts.map +1 -1
  105. package/dist/spreadsheet/SpreadsheetGroupRow.svelte +31 -3
  106. package/dist/spreadsheet/SpreadsheetGroupRow.svelte.d.ts +6 -0
  107. package/dist/spreadsheet/SpreadsheetGroupRow.svelte.d.ts.map +1 -1
  108. package/dist/spreadsheet/SpreadsheetValidationDialog.svelte +92 -0
  109. package/dist/spreadsheet/SpreadsheetValidationDialog.svelte.d.ts +9 -0
  110. package/dist/spreadsheet/SpreadsheetValidationDialog.svelte.d.ts.map +1 -0
  111. package/dist/spreadsheet/index.d.ts +10 -0
  112. package/dist/spreadsheet/index.d.ts.map +1 -1
  113. package/dist/spreadsheet/index.js +6 -0
  114. package/dist/spreadsheet/spreadsheet.svelte.d.ts +56 -3
  115. package/dist/spreadsheet/spreadsheet.svelte.d.ts.map +1 -1
  116. package/dist/spreadsheet/spreadsheet.svelte.js +105 -6
  117. package/dist/spreadsheet/viewport.svelte.d.ts +26 -0
  118. package/dist/spreadsheet/viewport.svelte.d.ts.map +1 -0
  119. package/dist/spreadsheet/viewport.svelte.js +93 -0
  120. package/dist/tables/InsertTableGrid.svelte +85 -0
  121. package/dist/tables/InsertTableGrid.svelte.d.ts +21 -0
  122. package/dist/tables/InsertTableGrid.svelte.d.ts.map +1 -0
  123. package/dist/tables/TableBubbleMenu.svelte +43 -0
  124. package/dist/tables/TableBubbleMenu.svelte.d.ts +18 -0
  125. package/dist/tables/TableBubbleMenu.svelte.d.ts.map +1 -0
  126. package/dist/tables/TableToolbar.svelte +81 -0
  127. package/dist/tables/TableToolbar.svelte.d.ts +19 -0
  128. package/dist/tables/TableToolbar.svelte.d.ts.map +1 -0
  129. package/dist/tables/index.d.ts +12 -0
  130. package/dist/tables/index.d.ts.map +1 -0
  131. package/dist/tables/index.js +4 -0
  132. package/dist/tables/tableMenu.svelte.d.ts +21 -0
  133. package/dist/tables/tableMenu.svelte.d.ts.map +1 -0
  134. package/dist/tables/tableMenu.svelte.js +27 -0
  135. package/dist/tokens/TokenSwatch.svelte +14 -0
  136. package/dist/tokens/TokenSwatch.svelte.d.ts +8 -0
  137. package/dist/tokens/TokenSwatch.svelte.d.ts.map +1 -0
  138. package/dist/tokens/TokensPanel.svelte +323 -0
  139. package/dist/tokens/TokensPanel.svelte.d.ts +14 -0
  140. package/dist/tokens/TokensPanel.svelte.d.ts.map +1 -0
  141. package/dist/tokens/createTokens.svelte.d.ts +31 -0
  142. package/dist/tokens/createTokens.svelte.d.ts.map +1 -0
  143. package/dist/tokens/createTokens.svelte.js +43 -0
  144. package/dist/tokens/index.d.ts +7 -0
  145. package/dist/tokens/index.d.ts.map +1 -0
  146. package/dist/tokens/index.js +3 -0
  147. package/package.json +10 -2
@@ -0,0 +1,141 @@
1
+ <script lang="ts">
2
+ import {
3
+ DATA_VALIDATION_ERROR_STYLE_OPTIONS,
4
+ DATA_VALIDATION_OPERATOR_OPTIONS,
5
+ DATA_VALIDATION_TYPE_OPTIONS,
6
+ describeDataValidation,
7
+ formatRuleRanges,
8
+ parseRuleRanges,
9
+ type DataValidationErrorStyle,
10
+ type DataValidationOperator,
11
+ type DataValidationType
12
+ } from '@pixerate/editor/spreadsheet';
13
+ import type { createReactiveSpreadsheet } from './spreadsheet.svelte.js';
14
+
15
+ interface Props {
16
+ sheetState: ReturnType<typeof createReactiveSpreadsheet>;
17
+ onClose: () => void;
18
+ }
19
+
20
+ let { sheetState, onClose }: Props = $props();
21
+
22
+ const inputClass = 'w-full rounded border border-border bg-background px-1.5 py-0.5 text-xs text-foreground';
23
+ const buttonClass = 'rounded border border-border px-2 py-0.5 hover:bg-muted disabled:opacity-40';
24
+ const selectionText = () => (sheetState.selectedRange ? formatRuleRanges([sheetState.selectedRange]) : 'A1');
25
+
26
+ let rules = $derived(sheetState.document.table?.dataValidations ?? []);
27
+ let type = $state<DataValidationType>('list');
28
+ let operator = $state<DataValidationOperator>('between');
29
+ let formula1 = $state('');
30
+ let formula2 = $state('');
31
+ let errorStyle = $state<DataValidationErrorStyle>('stop');
32
+ let errorMessage = $state('');
33
+ let inputMessage = $state('');
34
+ let ignoreBlank = $state(true);
35
+ let appliesTo = $state(selectionText());
36
+ let ranges = $derived(parseRuleRanges(appliesTo));
37
+ let usesOperator = $derived(type === 'whole' || type === 'decimal' || type === 'date' || type === 'time' || type === 'textLength');
38
+ let needsTwo = $derived(usesOperator && (operator === 'between' || operator === 'notBetween'));
39
+ let circles = $derived.by(() => {
40
+ void sheetState.document;
41
+ return sheetState.getInvalidDataCircles();
42
+ });
43
+
44
+ function apply() {
45
+ if (!ranges) return;
46
+ sheetState.addDataValidation({
47
+ ranges,
48
+ type,
49
+ ...(usesOperator ? { operator } : {}),
50
+ ...(formula1 ? { formula1 } : {}),
51
+ ...(needsTwo && formula2 ? { formula2 } : {}),
52
+ errorStyle,
53
+ ...(errorMessage ? { errorMessage } : {}),
54
+ ...(inputMessage ? { inputMessage } : {}),
55
+ ignoreBlank
56
+ });
57
+ }
58
+ </script>
59
+
60
+ <!-- Data validation manager: rules, a form to validate a range, and "circle invalid data". -->
61
+ <div
62
+ role="dialog"
63
+ aria-label="Data validation rules"
64
+ tabindex="-1"
65
+ class="fixed right-4 top-16 z-40 flex max-h-[calc(100vh-5rem)] w-80 flex-col overflow-y-auto rounded-md border border-border bg-popover p-3 text-xs text-popover-foreground shadow-lg"
66
+ style="background: var(--popover, var(--background, #fff));"
67
+ onkeydown={(e) => {
68
+ e.stopPropagation();
69
+ if (e.key === 'Escape') onClose();
70
+ }}
71
+ oncontextmenu={(e) => e.stopPropagation()}
72
+ >
73
+ <div class="mb-2 flex items-center justify-between">
74
+ <h2 class="text-sm font-semibold">Data validation</h2>
75
+ <button type="button" aria-label="Close" class="px-1 text-muted-foreground hover:text-foreground" onclick={onClose}>✕</button>
76
+ </div>
77
+ <ul class="mb-3 flex flex-col gap-1" aria-label="Validation rules">
78
+ {#if rules.length === 0}
79
+ <li class="text-muted-foreground">No validation on this sheet.</li>
80
+ {/if}
81
+ {#each rules as rule (rule.id)}
82
+ <li data-rule-id={rule.id} class="flex items-center gap-1.5 rounded border border-border px-1.5 py-1">
83
+ <div class="min-w-0 flex-1">
84
+ <div class="truncate" title={describeDataValidation(rule)}>{describeDataValidation(rule)}</div>
85
+ <div class="truncate font-mono text-[10px] text-muted-foreground">{formatRuleRanges(rule.ranges)} · {rule.errorStyle ?? 'stop'}</div>
86
+ </div>
87
+ <button type="button" aria-label="Delete rule" class={buttonClass} onclick={() => sheetState.deleteDataValidation(rule.id)}>✕</button>
88
+ </li>
89
+ {/each}
90
+ </ul>
91
+
92
+ <fieldset class="flex flex-col gap-1.5 border-t border-border pt-2">
93
+ <legend class="sr-only">New validation</legend>
94
+ <label class="flex flex-col gap-0.5">
95
+ Allow
96
+ <select aria-label="Allow" class={inputClass} bind:value={type}>
97
+ {#each DATA_VALIDATION_TYPE_OPTIONS as o (o.type)}
98
+ <option value={o.type}>{o.label}</option>
99
+ {/each}
100
+ </select>
101
+ </label>
102
+ {#if usesOperator}
103
+ <select aria-label="Operator" class={inputClass} bind:value={operator}>
104
+ {#each DATA_VALIDATION_OPERATOR_OPTIONS as o (o.operator)}
105
+ <option value={o.operator}>{o.label}</option>
106
+ {/each}
107
+ </select>
108
+ {/if}
109
+ {#if type !== 'any'}
110
+ <input
111
+ aria-label={type === 'list' ? 'Source' : type === 'custom' ? 'Formula' : needsTwo ? 'Minimum' : 'Value'}
112
+ placeholder={type === 'list' ? 'Low,Medium,High or =$H$1:$H$5' : type === 'custom' ? '=A1>0' : 'Value or =formula'}
113
+ class={inputClass}
114
+ bind:value={formula1}
115
+ />
116
+ {/if}
117
+ {#if needsTwo}
118
+ <input aria-label="Maximum" placeholder="Maximum" class={inputClass} bind:value={formula2} />
119
+ {/if}
120
+ <label class="flex items-center gap-1"><input type="checkbox" bind:checked={ignoreBlank} /> Ignore blank</label>
121
+ <input aria-label="Input message" placeholder="Input message (optional)" class={inputClass} bind:value={inputMessage} />
122
+ <select aria-label="Error style" class={inputClass} bind:value={errorStyle}>
123
+ {#each DATA_VALIDATION_ERROR_STYLE_OPTIONS as o (o.style)}
124
+ <option value={o.style}>{o.label}</option>
125
+ {/each}
126
+ </select>
127
+ <input aria-label="Error message" placeholder="Error message (optional)" class={inputClass} bind:value={errorMessage} />
128
+ <label class="flex flex-col gap-0.5">
129
+ Applies to
130
+ <input aria-label="Applies to" class="{inputClass} font-mono" bind:value={appliesTo} />
131
+ </label>
132
+ <div class="flex flex-wrap justify-between gap-2">
133
+ <button type="button" class={buttonClass} onclick={() => (appliesTo = selectionText())}>Use selection</button>
134
+ <button type="button" class={buttonClass} disabled={!ranges} onclick={() => ranges?.forEach((r) => sheetState.clearDataValidation(r))}>Clear</button>
135
+ <button type="button" class={buttonClass} disabled={!ranges} onclick={apply}>Apply</button>
136
+ </div>
137
+ <button type="button" class={buttonClass} aria-pressed={circles} onclick={() => sheetState.setInvalidDataCircles(!circles)}>
138
+ {circles ? 'Clear validation circles' : 'Circle invalid data'}
139
+ </button>
140
+ </fieldset>
141
+ </div>
@@ -0,0 +1,9 @@
1
+ import type { createReactiveSpreadsheet } from './spreadsheet.svelte.js';
2
+ interface Props {
3
+ sheetState: ReturnType<typeof createReactiveSpreadsheet>;
4
+ onClose: () => void;
5
+ }
6
+ declare const SpreadsheetDataValidationPanel: import("svelte").Component<Props, {}, "">;
7
+ type SpreadsheetDataValidationPanel = ReturnType<typeof SpreadsheetDataValidationPanel>;
8
+ export default SpreadsheetDataValidationPanel;
9
+ //# sourceMappingURL=SpreadsheetDataValidationPanel.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpreadsheetDataValidationPanel.svelte.d.ts","sourceRoot":"","sources":["../../src/spreadsheet/SpreadsheetDataValidationPanel.svelte.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAGvE,UAAU,KAAK;IACb,UAAU,EAAE,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC;IACzD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAsHH,QAAA,MAAM,8BAA8B,2CAAwC,CAAC;AAC7E,KAAK,8BAA8B,GAAG,UAAU,CAAC,OAAO,8BAA8B,CAAC,CAAC;AACxF,eAAe,8BAA8B,CAAC"}
@@ -14,14 +14,25 @@
14
14
  type SpreadsheetViewColumn,
15
15
  type SpreadsheetViewRow,
16
16
  formatCellForDisplay,
17
- numberFormatColorToCss
17
+ numberFormatColorToCss,
18
+ getConditionalFormatCss,
19
+ getConditionalFormatIcon,
20
+ getDataBarCss,
21
+ conditionalFormatStyleString,
22
+ type SpreadsheetPanelId,
23
+ type SpreadsheetValidationAction
18
24
  } from '@pixerate/editor/spreadsheet';
19
25
  import FormulaBar from './FormulaBar.svelte';
26
+ import SpreadsheetValidationDialog from './SpreadsheetValidationDialog.svelte';
27
+ import SpreadsheetConditionalFormatPanel from './SpreadsheetConditionalFormatPanel.svelte';
28
+ import SpreadsheetDataValidationPanel from './SpreadsheetDataValidationPanel.svelte';
20
29
  import SpreadsheetContextMenu from './SpreadsheetContextMenu.svelte';
21
30
  import SpreadsheetGroupRow from './SpreadsheetGroupRow.svelte';
22
31
  import FormulaHints from './FormulaHints.svelte';
32
+ import SheetTabs from './SheetTabs.svelte';
23
33
  import { createReactiveSpreadsheet } from './spreadsheet.svelte.js';
24
34
  import { attachSpreadsheetClipboard } from './tableInteractions.js';
35
+ import { createSpreadsheetViewport } from './viewport.svelte.js';
25
36
 
26
37
  interface Props {
27
38
  sheetState?: ReturnType<typeof createReactiveSpreadsheet>;
@@ -35,6 +46,14 @@
35
46
  onAddColumnClick?: () => void;
36
47
  onColumnHeaderClick?: (col: SpreadsheetColumn) => void;
37
48
  onColumnResize?: (colId: string, width: number) => void;
49
+ /** Show the sheet tabs bar under the grid (add, switch, rename, reorder, delete sheets). */
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';
38
57
  }
39
58
 
40
59
  let {
@@ -46,9 +65,58 @@
46
65
  customCellRenderer,
47
66
  onAddColumnClick,
48
67
  onColumnHeaderClick,
49
- onColumnResize
68
+ onColumnResize,
69
+ showSheetTabs = false,
70
+ virtualize = 'auto'
50
71
  }: Props = $props();
51
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
+
114
+ // Defined names for formula autocomplete in the cell editor.
115
+ let nameSuggestions = $derived.by(() => {
116
+ void sheetState.document;
117
+ return sheetState.controller.getNameSuggestions();
118
+ });
119
+
52
120
  let isReadOnly = $derived(readOnly || readonly);
53
121
 
54
122
  let gridContainer = $state<HTMLDivElement | null>(null);
@@ -85,6 +153,73 @@
85
153
 
86
154
  let fillDrag = $state.raw<{ source: CellRange; target: CellRange | null } | null>(null);
87
155
  let menu = $state.raw<{ x: number; y: number; items: SpreadsheetMenuItem[] } | null>(null);
156
+ // Conditional formatting / data validation manager, opened from the context menu.
157
+ let panel = $state<SpreadsheetPanelId | null>(null);
158
+ // List dropdown of a list-validated cell.
159
+ let listMenu = $state.raw<{ x: number; y: number; row: number; col: number; values: string[] } | null>(null);
160
+
161
+ // --- Data validation: list dropdown, input message, alerts ---
162
+ let activeListValues = $derived.by(() => {
163
+ void sheetState.document;
164
+ const cell = sheetState.activeCell;
165
+ return cell && !isReadOnly ? sheetState.controller.getValidationListValues(cell.row, cell.col) : null;
166
+ });
167
+ let inputMessage = $derived.by(() => {
168
+ void sheetState.document;
169
+ const cell = sheetState.activeCell;
170
+ const rule = cell ? sheetState.controller.getDataValidationAt(cell.row, cell.col) : undefined;
171
+ return rule && rule.showInputMessage !== false && (rule.inputTitle || rule.inputMessage) ? rule : null;
172
+ });
173
+
174
+ // "Circle invalid data": recomputed when the document (or the toggle) changes.
175
+ let invalidCircles = $derived.by(() => {
176
+ void sheetState.document;
177
+ if (!sheetState.controller.getInvalidDataCircles()) return null;
178
+ return new Set(sheetState.controller.findInvalidCells().map((c) => `${c.row}:${c.col}`));
179
+ });
180
+
181
+ function openListMenu(row: number, col: number, anchor?: HTMLElement | null) {
182
+ if (isReadOnly || sheetState.document.columns[col]?.readOnly) return;
183
+ const values = sheetState.controller.getValidationListValues(row, col);
184
+ if (!values) return;
185
+ const td = anchor ?? gridContainer?.querySelector<HTMLElement>(`td[data-row="${row}"][data-col="${col}"]`);
186
+ const rect = td?.getBoundingClientRect();
187
+ listMenu = { x: rect ? rect.left : 0, y: rect ? rect.bottom : 0, row, col, values };
188
+ }
189
+
190
+ // Close the list on a press outside it (or on its arrow, which toggles it).
191
+ $effect(() => {
192
+ if (!listMenu) return;
193
+ const close = (e: MouseEvent) => {
194
+ if (!(e.target as Element | null)?.closest?.('[aria-label="Allowed values"], .spreadsheet-list-arrow')) listMenu = null;
195
+ };
196
+ window.addEventListener('mousedown', close);
197
+ return () => window.removeEventListener('mousedown', close);
198
+ });
199
+
200
+ function chooseListValue(value: string) {
201
+ if (!listMenu) return;
202
+ const row = sheetState.document.rows[listMenu.row];
203
+ const col = sheetState.document.columns[listMenu.col];
204
+ listMenu = null;
205
+ if (row && col) sheetState.commitCellInput(row.id, col.id, value);
206
+ gridContainer?.focus();
207
+ }
208
+
209
+ function resolveValidation(action: SpreadsheetValidationAction) {
210
+ sheetState.resolveValidationPrompt(action);
211
+ // Back to the cell editor when it is still open (retry), else the grid.
212
+ requestAnimationFrame(() => {
213
+ if (cellInput) cellInput.focus();
214
+ else gridContainer?.focus();
215
+ });
216
+ }
217
+
218
+ /** Inline style of a cell's conditional look (fill, font), keeping the selection tint visible. */
219
+ function conditionalStyle(fill: string | undefined, css: string, selected: boolean): string {
220
+ if (!css) return '';
221
+ return ` ${css}${fill && selected ? ' box-shadow: inset 0 0 0 9999px color-mix(in srgb, var(--primary, #6366f1) 12%, transparent);' : ''}`;
222
+ }
88
223
  let filteredColIds = $derived.by(() => {
89
224
  const filter = sheetState.document.table?.filter;
90
225
  return new Set(filter?.enabled ? filter.rules.map((r) => r.colId) : []);
@@ -174,11 +309,12 @@
174
309
  sheetState.cancelEditing();
175
310
  } else if (e.key === 'Enter') {
176
311
  e.preventDefault();
177
- sheetState.commitEditing();
312
+ // Invalid input keeps the editor open behind the validation alert.
313
+ if (sheetState.commitEditing().status !== 'committed') return;
178
314
  sheetState.moveCursor('down');
179
315
  } else if (e.key === 'Tab') {
180
316
  e.preventDefault();
181
- sheetState.commitEditing();
317
+ if (sheetState.commitEditing().status !== 'committed') return;
182
318
  sheetState.moveCursor(e.shiftKey ? 'left' : 'right');
183
319
  } else {
184
320
  return;
@@ -190,7 +326,11 @@
190
326
 
191
327
  if (!sheetState.activeCell) return;
192
328
 
193
- if (e.key === 'ArrowUp') {
329
+ if (e.altKey && e.key === 'ArrowDown') {
330
+ // Alt+Down opens the list of a list-validated cell.
331
+ e.preventDefault();
332
+ openListMenu(sheetState.activeCell.row, sheetState.activeCell.col);
333
+ } else if (e.key === 'ArrowUp') {
194
334
  e.preventDefault();
195
335
  sheetState.moveCursor('up', e.shiftKey);
196
336
  } else if (e.key === 'ArrowDown') {
@@ -443,6 +583,7 @@
443
583
  }
444
584
  </script>
445
585
 
586
+ {#snippet grid()}
446
587
  <div
447
588
  bind:this={gridContainer}
448
589
  class="flex flex-col h-full w-full overflow-hidden border border-border bg-background select-none outline-none text-foreground font-sans text-xs {className}"
@@ -455,10 +596,15 @@
455
596
  {#if showFormulaBar}
456
597
  <FormulaBar {sheetState} />
457
598
  {/if}
599
+ {#if inputMessage}
600
+ <div role="note" data-validation-input-message="" class="border-b border-border bg-muted/30 px-3 py-1 text-[11px] text-muted-foreground">
601
+ {#if inputMessage.inputTitle}<span class="mr-1 font-semibold text-foreground">{inputMessage.inputTitle}</span>{/if}{inputMessage.inputMessage ?? ''}
602
+ </div>
603
+ {/if}
458
604
 
459
605
  <!-- Main Scrollable Grid -->
460
- <div class="relative flex-1 overflow-auto">
461
- <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">
462
608
  <!-- Table Header Columns -->
463
609
  <thead>
464
610
  <tr class="h-7 bg-muted sticky top-0 z-20 border-b border-border shadow-xs">
@@ -466,7 +612,11 @@
466
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">
467
613
  #
468
614
  </th>
469
- {#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}
470
620
  {@const col = viewCol.column}
471
621
  {@const cIdx = viewCol.index}
472
622
  <th
@@ -502,6 +652,7 @@
502
652
  ></div>
503
653
  </div>
504
654
  </th>
655
+ {/if}
505
656
  {/each}
506
657
  {#if onAddColumnClick}
507
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">
@@ -515,11 +666,21 @@
515
666
 
516
667
  <!-- Table Body Rows -->
517
668
  <tbody>
518
- {#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}
519
676
  {#if viewRow.kind === 'group'}
520
677
  <SpreadsheetGroupRow
521
678
  group={viewRow.group}
522
- 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}
523
684
  groupColId={sheetState.document.table?.grouping?.colId ?? ''}
524
685
  onToggle={sheetState.toggleGroupCollapsed}
525
686
  trailingCell={!!onAddColumnClick}
@@ -540,7 +701,11 @@
540
701
  </td>
541
702
 
542
703
  <!-- Cells (a merged cell renders its top-left cell, spanning the merge) -->
543
- {#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}
544
709
  {@const span = sheetState.viewModel.getCellSpan(displayRowIndex, viewCol.index)}
545
710
  {#if span}
546
711
  {@const rIdx = span.source.row}
@@ -552,6 +717,10 @@
552
717
  {@const active = isActiveCell(rIdx, cIdx)}
553
718
  {@const isEditing = sheetState.editingCell?.row === rIdx && sheetState.editingCell?.col === cIdx}
554
719
 
720
+ {@const conditional = sheetState.conditionalFormatOverlay.get(rIdx, cIdx)}
721
+ {@const conditionalCss = conditionalFormatStyleString(getConditionalFormatCss(conditional))}
722
+ {@const circled = !!invalidCircles?.has(`${rIdx}:${cIdx}`)}
723
+ {@const showListArrow = active && !isEditing && !!activeListValues}
555
724
  {@const inFillPreview = !!fillDrag?.target && !selected && rangeContains(fillDrag.target, displayRowIndex, viewCol.index)}
556
725
  {@const showFillHandle =
557
726
  !isReadOnly &&
@@ -568,7 +737,8 @@
568
737
  rowspan={span.rowSpan}
569
738
  colspan={span.colSpan}
570
739
  class="relative border-r border-border px-2 py-1 truncate max-w-0 text-foreground transition-colors {selected ? 'bg-primary/10' : ''} {active ? 'ring-2 ring-primary ring-inset z-10' : ''} {viewCol.header ? 'font-semibold' : ''}"
571
- style="max-width: 0; overflow: hidden; {frozenCellStyle(viewRow, viewCol)}{inFillPreview ? ' outline: 1px dashed var(--primary, #6366f1); outline-offset: -1px;' : ''}"
740
+ style="max-width: 0; overflow: hidden; {frozenCellStyle(viewRow, viewCol)}{conditionalStyle(conditional?.fill, conditionalCss, selected)}{inFillPreview ? ' outline: 1px dashed var(--primary, #6366f1); outline-offset: -1px;' : ''}"
741
+ data-cf-fill={conditional?.fill}
572
742
  onclick={(e) => handleCellClick(rIdx, cIdx, e)}
573
743
  ondblclick={() => handleCellDblClick(rIdx, cIdx)}
574
744
  oncontextmenu={(e) => handleCellContextMenu(rIdx, cIdx, e)}
@@ -582,20 +752,58 @@
582
752
  bind:value={sheetState.draftValue}
583
753
  onblur={() => sheetState.commitEditing()}
584
754
  />
585
- <FormulaHints input={cellInput} value={sheetState.draftValue} onAccept={(next) => (sheetState.draftValue = next)} />
755
+ <FormulaHints input={cellInput} value={sheetState.draftValue} onAccept={(next) => (sheetState.draftValue = next)} names={nameSuggestions} />
586
756
  {:else if customCellRenderer}
587
757
  {@const cellArg = Object.assign({ row, col, cell }, cell)}
588
758
  {@render (customCellRenderer as any)(cellArg, col, row)}
589
759
  {:else}
590
760
  {@const shown = formatCellForDisplay(cell)}
761
+ {@const textColor = conditional?.color ?? (shown.color ? numberFormatColorToCss(shown.color) : undefined)}
762
+ {@const icon = conditional?.icon ? getConditionalFormatIcon(conditional.icon.set, conditional.icon.index) : null}
763
+ {#if conditional?.dataBar}
764
+ <span aria-hidden="true" class="spreadsheet-data-bar" style={conditionalFormatStyleString(getDataBarCss(conditional.dataBar))}></span>
765
+ {/if}
766
+ {#snippet cellText()}
767
+ <span
768
+ class="truncate block max-w-full {cell.error ? 'text-destructive font-semibold' : ''}"
769
+ title={cell.error ? (cell.errorMessage ?? cell.error) : undefined}
770
+ data-numeric={shown.numeric ? '' : undefined}
771
+ style="{conditional?.dataBar || icon ? 'position: relative;' : ''}{icon ? ' flex: 1; min-width: 0;' : ''}{shown.numeric ? ' text-align: right;' : ''}{textColor ? ` color: ${textColor};` : ''}"
772
+ >
773
+ {conditional?.hideValue ? '' : shown.text}
774
+ </span>
775
+ {/snippet}
776
+ {#if icon && conditional?.icon}
777
+ <span class="flex items-center gap-1 min-w-0" style="position: relative;">
778
+ <span class="spreadsheet-cf-icon shrink-0" role="img" aria-label={icon.label} data-icon={conditional.icon.id} style="color: {icon.color};">{icon.glyph}</span>
779
+ {@render cellText()}
780
+ </span>
781
+ {:else}
782
+ {@render cellText()}
783
+ {/if}
784
+ {/if}
785
+ {#if circled}
591
786
  <span
592
- class="truncate block max-w-full {cell.error ? 'text-destructive font-semibold' : ''}"
593
- title={cell.error ? (cell.errorMessage ?? cell.error) : undefined}
594
- data-numeric={shown.numeric ? '' : undefined}
595
- style="{shown.numeric ? 'text-align: right;' : ''}{shown.color ? ` color: ${numberFormatColorToCss(shown.color)};` : ''}"
787
+ class="spreadsheet-invalid-circle"
788
+ aria-label="Invalid value"
789
+ style="position: absolute; inset: 1px 2px; border: 2px solid #e11d48; border-radius: 50%; pointer-events: none; z-index: 6;"
790
+ ></span>
791
+ {/if}
792
+ {#if showListArrow}
793
+ <button
794
+ type="button"
795
+ class="spreadsheet-list-arrow"
796
+ aria-label="Show allowed values"
797
+ aria-haspopup="listbox"
798
+ tabindex="-1"
799
+ style="position: absolute; right: 1px; top: 1px; bottom: 1px; width: 16px; z-index: 24; font-size: 10px; line-height: 1; background: var(--muted, #f4f4f5); border: 1px solid var(--border, #e4e4e7); border-radius: 2px; cursor: pointer; padding: 0;"
800
+ onmousedown={(e) => e.stopPropagation()}
801
+ onclick={(e) => {
802
+ e.stopPropagation();
803
+ if (listMenu) listMenu = null;
804
+ else openListMenu(rIdx, cIdx, (e.currentTarget as HTMLElement).parentElement);
805
+ }}>▾</button
596
806
  >
597
- {shown.text}
598
- </span>
599
807
  {/if}
600
808
  {#if activeSpill && rangeContains(activeSpill.range, rIdx, cIdx)}
601
809
  <span aria-hidden="true" class="spreadsheet-spill-border" style={spillBorderStyle(activeSpill.range, rIdx, cIdx)}></span>
@@ -612,6 +820,7 @@
612
820
  {/if}
613
821
  </td>
614
822
  {/if}
823
+ {/if}
615
824
  {/each}
616
825
  {#if onAddColumnClick}
617
826
  <td class="w-10 min-w-[2.5rem] bg-muted/10 border-r border-border"></td>
@@ -620,6 +829,7 @@
620
829
  <td class="border-b border-border bg-transparent p-0"></td>
621
830
  </tr>
622
831
  {/if}
832
+ {/if}
623
833
  {/each}
624
834
  </tbody>
625
835
  </table>
@@ -633,9 +843,53 @@
633
843
  menu = null;
634
844
  gridContainer?.focus();
635
845
  }}
846
+ onOpenPanel={(next) => (panel = next)}
636
847
  />
637
848
  {/if}
849
+ {#if panel === 'conditionalFormatting'}
850
+ <SpreadsheetConditionalFormatPanel {sheetState} onClose={() => (panel = null)} />
851
+ {:else if panel === 'dataValidation'}
852
+ <SpreadsheetDataValidationPanel {sheetState} onClose={() => (panel = null)} />
853
+ {/if}
854
+ {#if listMenu}
855
+ <div
856
+ role="listbox"
857
+ aria-label="Allowed values"
858
+ tabindex="-1"
859
+ class="fixed z-50 max-h-60 min-w-[8rem] overflow-y-auto rounded-md border border-border bg-popover py-1 text-xs text-popover-foreground shadow-lg"
860
+ style="left: {listMenu.x}px; top: {listMenu.y}px; background: var(--popover, var(--background, #fff));"
861
+ onkeydown={(e) => {
862
+ e.stopPropagation();
863
+ if (e.key === 'Escape') {
864
+ listMenu = null;
865
+ gridContainer?.focus();
866
+ }
867
+ }}
868
+ >
869
+ {#if listMenu.values.length === 0}
870
+ <div class="px-3 py-1 text-muted-foreground">No values</div>
871
+ {/if}
872
+ {#each listMenu.values as value, i (`${i}:${value}`)}
873
+ <button type="button" role="option" aria-selected="false" class="block w-full px-3 py-1 text-left hover:bg-muted" onclick={() => chooseListValue(value)}
874
+ >{value}</button
875
+ >
876
+ {/each}
877
+ </div>
878
+ {/if}
879
+ {#if sheetState.validationPrompt}
880
+ <SpreadsheetValidationDialog prompt={sheetState.validationPrompt} onResolve={resolveValidation} />
881
+ {/if}
638
882
  </div>
883
+ {/snippet}
884
+
885
+ {#if showSheetTabs}
886
+ <div class="flex h-full w-full flex-col">
887
+ <div class="min-h-0 flex-1">{@render grid()}</div>
888
+ <SheetTabs {sheetState} readOnly={isReadOnly} />
889
+ </div>
890
+ {:else}
891
+ {@render grid()}
892
+ {/if}
639
893
 
640
894
  <style>
641
895
  .col-resize-handle {
@@ -15,6 +15,14 @@ interface Props {
15
15
  onAddColumnClick?: () => void;
16
16
  onColumnHeaderClick?: (col: SpreadsheetColumn) => void;
17
17
  onColumnResize?: (colId: string, width: number) => void;
18
+ /** Show the sheet tabs bar under the grid (add, switch, rename, reorder, delete sheets). */
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';
18
26
  }
19
27
  declare const SpreadsheetEditor: import("svelte").Component<Props, {}, "">;
20
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,EAOd,MAAM,8BAA8B,CAAC;AAKxC,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;CACzD;AAgiBH,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"}