@svgrid/grid 2.2.0 → 2.2.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 (114) hide show
  1. package/dist/FlexRender.svelte +96 -96
  2. package/dist/GridFooter.svelte +178 -178
  3. package/dist/SvCarousel.svelte +141 -141
  4. package/dist/SvColorInput.svelte +187 -187
  5. package/dist/SvContextMenu.svelte +116 -116
  6. package/dist/SvDrawer.svelte +250 -250
  7. package/dist/SvGrid.svelte +3131 -3131
  8. package/dist/SvGridBoard.svelte +2339 -2339
  9. package/dist/SvGridChart.svelte +1724 -1724
  10. package/dist/SvGridChartPanel.svelte +485 -485
  11. package/dist/SvGridDropdown.svelte +689 -689
  12. package/dist/SvGroupCell.svelte +109 -109
  13. package/dist/SvMenu.svelte +124 -124
  14. package/dist/SvMenuList.svelte +117 -117
  15. package/dist/SvNumberInput.svelte +131 -131
  16. package/dist/SvOtpInput.svelte +158 -158
  17. package/dist/SvPopover.svelte +139 -139
  18. package/dist/SvRowGroupPanel.svelte +170 -170
  19. package/dist/SvScrollArea.svelte +61 -61
  20. package/dist/SvTooltip.svelte +127 -127
  21. package/dist/SvTour.svelte +204 -204
  22. package/dist/SvTree.svelte +377 -377
  23. package/dist/SvTreeSelect.svelte +230 -230
  24. package/dist/cdn/svgrid.js +8 -8
  25. package/dist/cdn/svgrid.svelte-external.js +8 -8
  26. package/dist/chart-export.js +8 -8
  27. package/package.json +10 -10
  28. package/src/FlexRender.svelte +96 -96
  29. package/src/GridFooter.svelte +178 -178
  30. package/src/SvCarousel.svelte +141 -141
  31. package/src/SvColorInput.svelte +187 -187
  32. package/src/SvContextMenu.svelte +116 -116
  33. package/src/SvDrawer.svelte +250 -250
  34. package/src/SvGrid.controller.svelte.ts +3158 -3158
  35. package/src/SvGrid.svelte +3131 -3131
  36. package/src/SvGrid.types.ts +1489 -1489
  37. package/src/SvGridBoard.svelte +2339 -2339
  38. package/src/SvGridChart.svelte +1724 -1724
  39. package/src/SvGridChartPanel.svelte +485 -485
  40. package/src/SvGridDropdown.svelte +689 -689
  41. package/src/SvGroupCell.svelte +109 -109
  42. package/src/SvMenu.svelte +124 -124
  43. package/src/SvMenu.test.ts +97 -97
  44. package/src/SvMenuList.svelte +117 -117
  45. package/src/SvNumberInput.svelte +131 -131
  46. package/src/SvOtpInput.svelte +158 -158
  47. package/src/SvPopover.svelte +139 -139
  48. package/src/SvRowGroupPanel.svelte +170 -170
  49. package/src/SvScrollArea.svelte +61 -61
  50. package/src/SvTooltip.svelte +127 -127
  51. package/src/SvTour.svelte +204 -204
  52. package/src/SvTree.svelte +377 -377
  53. package/src/SvTreeSelect.svelte +230 -230
  54. package/src/a11y.contract.test.ts +49 -49
  55. package/src/a11y.test.ts +59 -59
  56. package/src/a11y.ts +61 -61
  57. package/src/build-api.ts +870 -870
  58. package/src/cell-formatting.ts +169 -169
  59. package/src/cell-render.ts +469 -469
  60. package/src/chart-export.ts +201 -201
  61. package/src/chart.ts +2296 -2296
  62. package/src/collaboration.test.ts +104 -104
  63. package/src/collaboration.ts +167 -167
  64. package/src/core.performance.test.ts +30 -30
  65. package/src/core.ts +1111 -1111
  66. package/src/createAutocomplete.svelte.ts +132 -132
  67. package/src/createCombobox.svelte.ts +179 -179
  68. package/src/createCountryInput.svelte.ts +157 -157
  69. package/src/createDropdownList.svelte.ts +162 -162
  70. package/src/createGrid.svelte.ts +42 -42
  71. package/src/createGrid.test.ts +10 -10
  72. package/src/createGridState.svelte.ts +17 -17
  73. package/src/createMenu.svelte.ts +202 -202
  74. package/src/createPopoverSelect.svelte.ts +206 -206
  75. package/src/createTree.svelte.ts +319 -319
  76. package/src/editing.test.ts +859 -859
  77. package/src/editing.ts +675 -675
  78. package/src/export-data-api.test.ts +126 -126
  79. package/src/export-format.test.ts +107 -107
  80. package/src/export-format.ts +598 -598
  81. package/src/flex-render.ts +3 -3
  82. package/src/index.ts +623 -623
  83. package/src/keyboard.test.ts +59 -59
  84. package/src/keyboard.ts +97 -97
  85. package/src/list-nav.test.ts +49 -49
  86. package/src/list-nav.ts +29 -29
  87. package/src/menus.ts +639 -639
  88. package/src/merge-objects.ts +48 -48
  89. package/src/overlays.test.ts +90 -90
  90. package/src/render-component.ts +28 -28
  91. package/src/selection.test.ts +754 -754
  92. package/src/selection.ts +600 -600
  93. package/src/server-data-source.test.ts +289 -289
  94. package/src/server-data-source.ts +413 -413
  95. package/src/sparkline.test.ts +68 -68
  96. package/src/sparkline.ts +169 -169
  97. package/src/spreadsheet.test.ts +489 -489
  98. package/src/spreadsheet.ts +312 -312
  99. package/src/static-functions.ts +11 -11
  100. package/src/subscribe.ts +38 -38
  101. package/src/svgrid-wrapper.types.ts +501 -501
  102. package/src/svgrid.behavior.test.ts +706 -706
  103. package/src/svgrid.charting.test.ts +527 -527
  104. package/src/svgrid.features.test.ts +157 -157
  105. package/src/svgrid.new-features.wrapper.test.ts +251 -251
  106. package/src/svgrid.wrapper.test.ts +40 -40
  107. package/src/test-setup.ts +62 -62
  108. package/src/themes/index.ts +159 -159
  109. package/src/virtualization/column-virtualizer.test.ts +27 -27
  110. package/src/virtualization/column-virtualizer.ts +30 -30
  111. package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
  112. package/src/virtualization/types.ts +30 -30
  113. package/src/virtualization/virtualizer.test.ts +47 -47
  114. package/src/virtualization/virtualizer.ts +296 -296
@@ -1,170 +1,170 @@
1
- <!--
2
- A row-group panel (like the "drag columns to group" bar in enterprise grids).
3
- Shows the current group-by columns as chips you can remove or drag to reorder,
4
- plus a "+ Group by" menu to add one. It is also a drop target: a column drag
5
- that carries `text/sv-column` (its column id) adds that column. Wire `onChange`
6
- to a server group model's `setGroupBy`, or to client grouping.
7
-
8
- <SvRowGroupPanel
9
- columns={[{ id: 'region', label: 'Region' }, { id: 'country', label: 'Country' }]}
10
- groupBy={view.groupBy}
11
- onChange={(g) => ctl.setGroupBy(g)}
12
- />
13
- -->
14
- <script lang="ts">
15
- type Col = { id: string; label: string }
16
- type Props = {
17
- /** Columns that can be grouped on. */
18
- columns: Col[]
19
- /** Current group-by column ids, outer to inner. */
20
- groupBy: string[]
21
- /** Called with the new group-by order. */
22
- onChange: (groupBy: string[]) => void
23
- /** Text shown when nothing is grouped. */
24
- placeholder?: string
25
- }
26
- let { columns, groupBy, onChange, placeholder = 'Drag a column here to group by it' }: Props = $props()
27
-
28
- const labelOf = (id: string) => columns.find((c) => c.id === id)?.label ?? id
29
- const available = $derived(columns.filter((c) => !groupBy.includes(c.id)))
30
-
31
- let dragIndex = $state<number | null>(null)
32
-
33
- function remove(id: string) {
34
- onChange(groupBy.filter((g) => g !== id))
35
- }
36
- function add(id: string) {
37
- if (id && !groupBy.includes(id)) onChange([...groupBy, id])
38
- }
39
- function reorder(to: number) {
40
- if (dragIndex === null || dragIndex === to) return
41
- const next = [...groupBy]
42
- const [moved] = next.splice(dragIndex, 1)
43
- next.splice(to, 0, moved!)
44
- dragIndex = null
45
- onChange(next)
46
- }
47
- function onPanelDrop(e: DragEvent) {
48
- const id = e.dataTransfer?.getData('text/sv-column')
49
- if (id) add(id)
50
- }
51
- /** Keyboard equivalent of dragging a chip: moves the chip at `i` one slot earlier/later,
52
- * reusing `reorder()` (via `dragIndex`) so there is a single source of truth for the move. */
53
- function moveByKeyboard(i: number, delta: number) {
54
- const to = i + delta
55
- if (to < 0 || to >= groupBy.length) return
56
- dragIndex = i
57
- reorder(to)
58
- }
59
- function onChipKeydown(e: KeyboardEvent, i: number) {
60
- if (!e.altKey) return
61
- if (e.key === 'ArrowLeft') {
62
- e.preventDefault()
63
- moveByKeyboard(i, -1)
64
- } else if (e.key === 'ArrowRight') {
65
- e.preventDefault()
66
- moveByKeyboard(i, 1)
67
- }
68
- }
69
- </script>
70
-
71
- <div
72
- class="sv-rowgroup-panel"
73
- role="group"
74
- aria-label="Row groups"
75
- ondragover={(e) => e.preventDefault()}
76
- ondrop={onPanelDrop}
77
- >
78
- <span class="sv-rgp-label" aria-hidden="true">Group by:</span>
79
- {#if groupBy.length === 0}
80
- <span class="sv-rgp-empty">{placeholder}</span>
81
- {:else}
82
- {#each groupBy as id, i (id)}
83
- {#if i > 0}<span class="sv-rgp-sep" aria-hidden="true">›</span>{/if}
84
- <!-- svelte-ignore a11y_no_noninteractive_tabindex, a11y_no_noninteractive_element_interactions -->
85
- <span
86
- class="sv-rgp-chip"
87
- class:sv-rgp-chip-drag={dragIndex === i}
88
- role="group"
89
- tabindex="0"
90
- aria-label={`Grouped by ${labelOf(id)}, position ${i + 1} of ${groupBy.length}. Press Alt+Arrow keys to reorder.`}
91
- draggable="true"
92
- ondragstart={() => (dragIndex = i)}
93
- ondragend={() => (dragIndex = null)}
94
- ondragover={(e) => e.preventDefault()}
95
- ondrop={() => reorder(i)}
96
- onkeydown={(e) => onChipKeydown(e, i)}
97
- >
98
- <span class="sv-rgp-grip" aria-hidden="true">⠿</span>
99
- {labelOf(id)}
100
- <button type="button" class="sv-rgp-x" onclick={() => remove(id)} aria-label={`Stop grouping by ${labelOf(id)}`}>×</button>
101
- </span>
102
- {/each}
103
- {/if}
104
- {#if available.length}
105
- <select
106
- class="sv-rgp-add"
107
- aria-label="Add a group column"
108
- onchange={(e) => { add(e.currentTarget.value); e.currentTarget.value = '' }}
109
- >
110
- <option value="">+ Group by</option>
111
- {#each available as c (c.id)}<option value={c.id}>{c.label}</option>{/each}
112
- </select>
113
- {/if}
114
- </div>
115
-
116
- <style>
117
- .sv-rowgroup-panel {
118
- display: flex;
119
- align-items: center;
120
- flex-wrap: wrap;
121
- gap: 6px;
122
- padding: 8px 10px;
123
- border: 1px dashed var(--sg-border, #cbd5e1);
124
- border-radius: 10px;
125
- background: var(--sg-header-bg, #f8fafc);
126
- font-size: 13px;
127
- }
128
- .sv-rgp-label { color: var(--sg-muted, #64748b); font-weight: 600; }
129
- .sv-rgp-empty { color: var(--sg-muted, #94a3b8); font-style: italic; }
130
- .sv-rgp-sep { color: var(--sg-muted, #94a3b8); }
131
- .sv-rgp-chip {
132
- display: inline-flex;
133
- align-items: center;
134
- gap: 6px;
135
- padding: 3px 6px 3px 8px;
136
- border-radius: 999px;
137
- background: var(--sg-bg, #fff);
138
- border: 1px solid var(--sg-border, #e2e8f0);
139
- cursor: grab;
140
- user-select: none;
141
- font-weight: 600;
142
- }
143
- .sv-rgp-chip-drag { opacity: 0.5; }
144
- .sv-rgp-grip { color: var(--sg-muted, #94a3b8); cursor: grab; }
145
- .sv-rgp-x {
146
- display: inline-flex;
147
- align-items: center;
148
- justify-content: center;
149
- width: 16px;
150
- height: 16px;
151
- border: 0;
152
- border-radius: 50%;
153
- background: none;
154
- color: var(--sg-muted, #64748b);
155
- cursor: pointer;
156
- font-size: 14px;
157
- line-height: 1;
158
- }
159
- .sv-rgp-x:hover { background: var(--sg-header-bg, #f1f5f9); color: var(--sg-fg, #0f172a); }
160
- .sv-rgp-add {
161
- font: inherit;
162
- font-size: 13px;
163
- padding: 4px 8px;
164
- border-radius: 8px;
165
- border: 1px solid var(--sg-border, #e2e8f0);
166
- background: var(--sg-bg, #fff);
167
- color: var(--sg-accent, #2563eb);
168
- cursor: pointer;
169
- }
170
- </style>
1
+ <!--
2
+ A row-group panel (like the "drag columns to group" bar in enterprise grids).
3
+ Shows the current group-by columns as chips you can remove or drag to reorder,
4
+ plus a "+ Group by" menu to add one. It is also a drop target: a column drag
5
+ that carries `text/sv-column` (its column id) adds that column. Wire `onChange`
6
+ to a server group model's `setGroupBy`, or to client grouping.
7
+
8
+ <SvRowGroupPanel
9
+ columns={[{ id: 'region', label: 'Region' }, { id: 'country', label: 'Country' }]}
10
+ groupBy={view.groupBy}
11
+ onChange={(g) => ctl.setGroupBy(g)}
12
+ />
13
+ -->
14
+ <script lang="ts">
15
+ type Col = { id: string; label: string }
16
+ type Props = {
17
+ /** Columns that can be grouped on. */
18
+ columns: Col[]
19
+ /** Current group-by column ids, outer to inner. */
20
+ groupBy: string[]
21
+ /** Called with the new group-by order. */
22
+ onChange: (groupBy: string[]) => void
23
+ /** Text shown when nothing is grouped. */
24
+ placeholder?: string
25
+ }
26
+ let { columns, groupBy, onChange, placeholder = 'Drag a column here to group by it' }: Props = $props()
27
+
28
+ const labelOf = (id: string) => columns.find((c) => c.id === id)?.label ?? id
29
+ const available = $derived(columns.filter((c) => !groupBy.includes(c.id)))
30
+
31
+ let dragIndex = $state<number | null>(null)
32
+
33
+ function remove(id: string) {
34
+ onChange(groupBy.filter((g) => g !== id))
35
+ }
36
+ function add(id: string) {
37
+ if (id && !groupBy.includes(id)) onChange([...groupBy, id])
38
+ }
39
+ function reorder(to: number) {
40
+ if (dragIndex === null || dragIndex === to) return
41
+ const next = [...groupBy]
42
+ const [moved] = next.splice(dragIndex, 1)
43
+ next.splice(to, 0, moved!)
44
+ dragIndex = null
45
+ onChange(next)
46
+ }
47
+ function onPanelDrop(e: DragEvent) {
48
+ const id = e.dataTransfer?.getData('text/sv-column')
49
+ if (id) add(id)
50
+ }
51
+ /** Keyboard equivalent of dragging a chip: moves the chip at `i` one slot earlier/later,
52
+ * reusing `reorder()` (via `dragIndex`) so there is a single source of truth for the move. */
53
+ function moveByKeyboard(i: number, delta: number) {
54
+ const to = i + delta
55
+ if (to < 0 || to >= groupBy.length) return
56
+ dragIndex = i
57
+ reorder(to)
58
+ }
59
+ function onChipKeydown(e: KeyboardEvent, i: number) {
60
+ if (!e.altKey) return
61
+ if (e.key === 'ArrowLeft') {
62
+ e.preventDefault()
63
+ moveByKeyboard(i, -1)
64
+ } else if (e.key === 'ArrowRight') {
65
+ e.preventDefault()
66
+ moveByKeyboard(i, 1)
67
+ }
68
+ }
69
+ </script>
70
+
71
+ <div
72
+ class="sv-rowgroup-panel"
73
+ role="group"
74
+ aria-label="Row groups"
75
+ ondragover={(e) => e.preventDefault()}
76
+ ondrop={onPanelDrop}
77
+ >
78
+ <span class="sv-rgp-label" aria-hidden="true">Group by:</span>
79
+ {#if groupBy.length === 0}
80
+ <span class="sv-rgp-empty">{placeholder}</span>
81
+ {:else}
82
+ {#each groupBy as id, i (id)}
83
+ {#if i > 0}<span class="sv-rgp-sep" aria-hidden="true">›</span>{/if}
84
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex, a11y_no_noninteractive_element_interactions -->
85
+ <span
86
+ class="sv-rgp-chip"
87
+ class:sv-rgp-chip-drag={dragIndex === i}
88
+ role="group"
89
+ tabindex="0"
90
+ aria-label={`Grouped by ${labelOf(id)}, position ${i + 1} of ${groupBy.length}. Press Alt+Arrow keys to reorder.`}
91
+ draggable="true"
92
+ ondragstart={() => (dragIndex = i)}
93
+ ondragend={() => (dragIndex = null)}
94
+ ondragover={(e) => e.preventDefault()}
95
+ ondrop={() => reorder(i)}
96
+ onkeydown={(e) => onChipKeydown(e, i)}
97
+ >
98
+ <span class="sv-rgp-grip" aria-hidden="true">⠿</span>
99
+ {labelOf(id)}
100
+ <button type="button" class="sv-rgp-x" onclick={() => remove(id)} aria-label={`Stop grouping by ${labelOf(id)}`}>×</button>
101
+ </span>
102
+ {/each}
103
+ {/if}
104
+ {#if available.length}
105
+ <select
106
+ class="sv-rgp-add"
107
+ aria-label="Add a group column"
108
+ onchange={(e) => { add(e.currentTarget.value); e.currentTarget.value = '' }}
109
+ >
110
+ <option value="">+ Group by</option>
111
+ {#each available as c (c.id)}<option value={c.id}>{c.label}</option>{/each}
112
+ </select>
113
+ {/if}
114
+ </div>
115
+
116
+ <style>
117
+ .sv-rowgroup-panel {
118
+ display: flex;
119
+ align-items: center;
120
+ flex-wrap: wrap;
121
+ gap: 6px;
122
+ padding: 8px 10px;
123
+ border: 1px dashed var(--sg-border, #cbd5e1);
124
+ border-radius: 10px;
125
+ background: var(--sg-header-bg, #f8fafc);
126
+ font-size: 13px;
127
+ }
128
+ .sv-rgp-label { color: var(--sg-muted, #64748b); font-weight: 600; }
129
+ .sv-rgp-empty { color: var(--sg-muted, #94a3b8); font-style: italic; }
130
+ .sv-rgp-sep { color: var(--sg-muted, #94a3b8); }
131
+ .sv-rgp-chip {
132
+ display: inline-flex;
133
+ align-items: center;
134
+ gap: 6px;
135
+ padding: 3px 6px 3px 8px;
136
+ border-radius: 999px;
137
+ background: var(--sg-bg, #fff);
138
+ border: 1px solid var(--sg-border, #e2e8f0);
139
+ cursor: grab;
140
+ user-select: none;
141
+ font-weight: 600;
142
+ }
143
+ .sv-rgp-chip-drag { opacity: 0.5; }
144
+ .sv-rgp-grip { color: var(--sg-muted, #94a3b8); cursor: grab; }
145
+ .sv-rgp-x {
146
+ display: inline-flex;
147
+ align-items: center;
148
+ justify-content: center;
149
+ width: 16px;
150
+ height: 16px;
151
+ border: 0;
152
+ border-radius: 50%;
153
+ background: none;
154
+ color: var(--sg-muted, #64748b);
155
+ cursor: pointer;
156
+ font-size: 14px;
157
+ line-height: 1;
158
+ }
159
+ .sv-rgp-x:hover { background: var(--sg-header-bg, #f1f5f9); color: var(--sg-fg, #0f172a); }
160
+ .sv-rgp-add {
161
+ font: inherit;
162
+ font-size: 13px;
163
+ padding: 4px 8px;
164
+ border-radius: 8px;
165
+ border: 1px solid var(--sg-border, #e2e8f0);
166
+ background: var(--sg-bg, #fff);
167
+ color: var(--sg-accent, #2563eb);
168
+ cursor: pointer;
169
+ }
170
+ </style>
@@ -1,61 +1,61 @@
1
- <script lang="ts">
2
- /**
3
- * SvScrollArea - a scroll container with the kit's themed custom scrollbars
4
- * (Firefox `scrollbar-color` + Chromium `::-webkit-scrollbar`) consuming the
5
- * `--sg-scrollbar-*` tokens, so any panel matches the grid/demos without
6
- * hand-rolling scrollbar CSS. The fallbacks derive from `--sg-fg` so it also
7
- * adapts to light/dark when no preset defines the tokens.
8
- *
9
- * ```svelte
10
- * <SvScrollArea maxHeight="240px">…long content…</SvScrollArea>
11
- * ```
12
- */
13
- import type { Snippet } from 'svelte'
14
-
15
- type Props = {
16
- maxHeight?: string
17
- height?: string
18
- /** Scroll horizontally instead of vertically. */
19
- horizontal?: boolean
20
- /** Accessible name for the scrollable region, announced by screen readers. */
21
- ariaLabel?: string
22
- children?: Snippet
23
- }
24
-
25
- let { maxHeight, height, horizontal = false, ariaLabel = 'Scrollable content', children }: Props = $props()
26
- </script>
27
-
28
- <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
29
- <div
30
- class="sv-scroll"
31
- class:is-x={horizontal}
32
- style:max-height={maxHeight}
33
- style:height={height}
34
- tabindex="0"
35
- role="region"
36
- aria-label={ariaLabel}
37
- >
38
- {@render children?.()}
39
- </div>
40
-
41
- <style>
42
- .sv-scroll {
43
- overflow: auto; overscroll-behavior: contain; min-height: 0;
44
- scrollbar-width: thin;
45
- scrollbar-color: var(--sg-scrollbar-thumb, color-mix(in srgb, var(--sg-fg, #0f172a) 26%, transparent)) var(--sg-scrollbar-bg, transparent);
46
- }
47
- .sv-scroll.is-x { overflow: auto hidden; }
48
- .sv-scroll::-webkit-scrollbar { width: 12px; height: 12px; }
49
- .sv-scroll::-webkit-scrollbar-track {
50
- background: var(--sg-scrollbar-bg, transparent);
51
- box-shadow: inset 1px 0 0 0 var(--sg-scrollbar-border, transparent);
52
- }
53
- .sv-scroll::-webkit-scrollbar-thumb {
54
- background: var(--sg-scrollbar-thumb, color-mix(in srgb, var(--sg-fg, #0f172a) 26%, transparent));
55
- border-radius: var(--sg-scrollbar-thumb-radius, 6px);
56
- border: 3px solid transparent; background-clip: padding-box;
57
- }
58
- .sv-scroll::-webkit-scrollbar-thumb:hover { background: var(--sg-scrollbar-thumb-hover, color-mix(in srgb, var(--sg-fg, #0f172a) 38%, transparent)); }
59
- .sv-scroll::-webkit-scrollbar-thumb:active { background: var(--sg-scrollbar-thumb-active, color-mix(in srgb, var(--sg-fg, #0f172a) 50%, transparent)); }
60
- .sv-scroll::-webkit-scrollbar-corner { background: var(--sg-scrollbar-bg, transparent); }
61
- </style>
1
+ <script lang="ts">
2
+ /**
3
+ * SvScrollArea - a scroll container with the kit's themed custom scrollbars
4
+ * (Firefox `scrollbar-color` + Chromium `::-webkit-scrollbar`) consuming the
5
+ * `--sg-scrollbar-*` tokens, so any panel matches the grid/demos without
6
+ * hand-rolling scrollbar CSS. The fallbacks derive from `--sg-fg` so it also
7
+ * adapts to light/dark when no preset defines the tokens.
8
+ *
9
+ * ```svelte
10
+ * <SvScrollArea maxHeight="240px">…long content…</SvScrollArea>
11
+ * ```
12
+ */
13
+ import type { Snippet } from 'svelte'
14
+
15
+ type Props = {
16
+ maxHeight?: string
17
+ height?: string
18
+ /** Scroll horizontally instead of vertically. */
19
+ horizontal?: boolean
20
+ /** Accessible name for the scrollable region, announced by screen readers. */
21
+ ariaLabel?: string
22
+ children?: Snippet
23
+ }
24
+
25
+ let { maxHeight, height, horizontal = false, ariaLabel = 'Scrollable content', children }: Props = $props()
26
+ </script>
27
+
28
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
29
+ <div
30
+ class="sv-scroll"
31
+ class:is-x={horizontal}
32
+ style:max-height={maxHeight}
33
+ style:height={height}
34
+ tabindex="0"
35
+ role="region"
36
+ aria-label={ariaLabel}
37
+ >
38
+ {@render children?.()}
39
+ </div>
40
+
41
+ <style>
42
+ .sv-scroll {
43
+ overflow: auto; overscroll-behavior: contain; min-height: 0;
44
+ scrollbar-width: thin;
45
+ scrollbar-color: var(--sg-scrollbar-thumb, color-mix(in srgb, var(--sg-fg, #0f172a) 26%, transparent)) var(--sg-scrollbar-bg, transparent);
46
+ }
47
+ .sv-scroll.is-x { overflow: auto hidden; }
48
+ .sv-scroll::-webkit-scrollbar { width: 12px; height: 12px; }
49
+ .sv-scroll::-webkit-scrollbar-track {
50
+ background: var(--sg-scrollbar-bg, transparent);
51
+ box-shadow: inset 1px 0 0 0 var(--sg-scrollbar-border, transparent);
52
+ }
53
+ .sv-scroll::-webkit-scrollbar-thumb {
54
+ background: var(--sg-scrollbar-thumb, color-mix(in srgb, var(--sg-fg, #0f172a) 26%, transparent));
55
+ border-radius: var(--sg-scrollbar-thumb-radius, 6px);
56
+ border: 3px solid transparent; background-clip: padding-box;
57
+ }
58
+ .sv-scroll::-webkit-scrollbar-thumb:hover { background: var(--sg-scrollbar-thumb-hover, color-mix(in srgb, var(--sg-fg, #0f172a) 38%, transparent)); }
59
+ .sv-scroll::-webkit-scrollbar-thumb:active { background: var(--sg-scrollbar-thumb-active, color-mix(in srgb, var(--sg-fg, #0f172a) 50%, transparent)); }
60
+ .sv-scroll::-webkit-scrollbar-corner { background: var(--sg-scrollbar-bg, transparent); }
61
+ </style>