@svgrid/grid 2.2.27 → 2.2.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +199 -199
- package/dist/FlexRender.svelte +96 -96
- package/dist/GridFooter.svelte +179 -179
- package/dist/GridMenus.svelte +782 -734
- package/dist/SvAutoComplete.svelte +165 -165
- package/dist/SvCalendar.svelte +503 -503
- package/dist/SvCarousel.svelte +141 -141
- package/dist/SvCheckBox.svelte +101 -101
- package/dist/SvColorInput.svelte +181 -181
- package/dist/SvComboBox.svelte +275 -275
- package/dist/SvContextMenu.svelte +116 -116
- package/dist/SvCountryInput.svelte +160 -160
- package/dist/SvDrawer.svelte +254 -250
- package/dist/SvDropDownList.svelte +374 -374
- package/dist/SvDurationInput.svelte +126 -126
- package/dist/SvField.svelte +293 -293
- package/dist/SvForm.svelte +437 -437
- package/dist/SvGrid.controller.svelte.d.ts +14 -1
- package/dist/SvGrid.controller.svelte.js +112 -28
- package/dist/SvGrid.css +2704 -2723
- package/dist/SvGrid.svelte +3361 -3333
- package/dist/SvGrid.types.d.ts +20 -5
- package/dist/SvGridChart.svelte +1724 -1724
- package/dist/SvGridChartPanel.svelte +485 -485
- package/dist/SvGridChartView.svelte +70 -70
- package/dist/SvGridDropdown.svelte +696 -696
- package/dist/SvGridSelect.svelte +259 -259
- package/dist/SvGroupCell.svelte +109 -109
- package/dist/SvListBox.svelte +326 -287
- package/dist/SvListBox.svelte.d.ts +10 -1
- package/dist/SvMaskedInput.svelte +122 -122
- package/dist/SvMenu.svelte +124 -124
- package/dist/SvMenuList.svelte +146 -146
- package/dist/SvMultiSelect.svelte +281 -281
- package/dist/SvNumberInput.svelte +168 -168
- package/dist/SvOtpInput.svelte +158 -158
- package/dist/SvPasswordInput.svelte +151 -151
- package/dist/SvPhoneInput.svelte +133 -131
- package/dist/SvPopover.svelte +197 -197
- package/dist/SvRadioGroup.svelte +107 -107
- package/dist/SvRowGroupPanel.svelte +170 -170
- package/dist/SvScrollArea.svelte +61 -61
- package/dist/SvSlider.svelte +200 -200
- package/dist/SvSwitchButton.svelte +107 -107
- package/dist/SvTagsInput.svelte +112 -112
- package/dist/SvTextInput.svelte +147 -147
- package/dist/SvTimePicker.svelte +245 -245
- package/dist/SvToaster.svelte +159 -159
- package/dist/SvToggleButton.svelte +85 -85
- package/dist/SvTooltip.svelte +161 -161
- package/dist/SvTour.svelte +204 -204
- package/dist/SvTree.svelte +437 -437
- package/dist/SvTreeSelect.svelte +233 -233
- package/dist/cdn/GridMenus-Da_p7SKS.js +486 -0
- package/dist/cdn/GridMenus-Ds3OpzZT.js +490 -0
- package/dist/cdn/{SvDateTimePicker-B1sDc9Pz.js → SvDateTimePicker-B8GopjhC.js} +2 -2
- package/dist/cdn/{SvDateTimePicker-Bh0FTs1E.js → SvDateTimePicker-DB0aIEk8.js} +2 -2
- package/dist/cdn/{SvGridChart-Bgk4hDs8.js → SvGridChart-BcLCYvw9.js} +1 -1
- package/dist/cdn/{SvGridChart-BKQsO5fZ.js → SvGridChart-BhnFdLd0.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-DkCc2Z7b.js → SvGridChartPanel-Cmx9DHAS.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-DSkHIQDd.js → SvGridChartPanel-DvjwxLvK.js} +1 -1
- package/dist/cdn/{SvGridChartView-BrbVaELm.js → SvGridChartView-B40DTJSX.js} +1 -1
- package/dist/cdn/{SvGridChartView-i9rWvSKg.js → SvGridChartView-PwqI4s02.js} +1 -1
- package/dist/cdn/{SvGridDropdown-D08sVmAy.js → SvGridDropdown-DjALc_4f.js} +1 -1
- package/dist/cdn/{SvGridDropdown-CGxxopQ4.js → SvGridDropdown-hpnHvOGt.js} +1 -1
- package/dist/cdn/{src-cgs1wcUS.js → src-BLJfdyL0.js} +4034 -3889
- package/dist/cdn/{src-Cw8d9U8j.js → src-DYDCiC0D.js} +7053 -6908
- package/dist/cdn/svgrid.js +9 -9
- package/dist/cdn/svgrid.svelte-external.js +9 -9
- package/dist/chart-export.js +8 -8
- package/dist/column-groups.js +8 -10
- package/dist/column-id.d.ts +9 -0
- package/dist/column-id.js +25 -0
- package/dist/core.js +2 -1
- package/dist/createListbox.svelte.d.ts +15 -2
- package/dist/createListbox.svelte.js +27 -6
- package/dist/editing.d.ts +1 -0
- package/dist/editing.js +45 -10
- package/dist/filtering/excel-filters.d.ts +30 -10
- package/dist/filtering/excel-filters.js +96 -15
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/menus.d.ts +2 -0
- package/dist/menus.js +25 -0
- package/dist/row-resize.js +40 -0
- package/dist/selection.js +1 -1
- package/package.json +10 -10
- package/src/FlexRender.svelte +96 -96
- package/src/GridFooter.svelte +179 -179
- package/src/GridMenus.svelte +782 -734
- package/src/SvAutoComplete.svelte +165 -165
- package/src/SvCalendar.svelte +503 -503
- package/src/SvCalendar.test.ts +226 -226
- package/src/SvCarousel.svelte +141 -141
- package/src/SvCheckBox.svelte +101 -101
- package/src/SvColorInput.svelte +181 -181
- package/src/SvComboBox.svelte +275 -275
- package/src/SvContextMenu.svelte +116 -116
- package/src/SvCountryInput.svelte +160 -160
- package/src/SvDrawer.svelte +254 -250
- package/src/SvDrawer.test.ts +18 -1
- package/src/SvDropDownList.svelte +374 -374
- package/src/SvDurationInput.svelte +126 -126
- package/src/SvField.svelte +293 -293
- package/src/SvForm.svelte +437 -437
- package/src/SvForm.test.ts +411 -411
- package/src/SvGrid.controller.svelte.ts +3335 -3239
- package/src/SvGrid.css +2704 -2723
- package/src/SvGrid.svelte +3361 -3333
- package/src/SvGrid.types.ts +1628 -1613
- package/src/SvGridChart.svelte +1724 -1724
- package/src/SvGridChartPanel.svelte +485 -485
- package/src/SvGridChartView.svelte +70 -70
- package/src/SvGridDropdown.svelte +696 -696
- package/src/SvGridSelect.svelte +259 -259
- package/src/SvGroupCell.svelte +109 -109
- package/src/SvListBox.svelte +326 -287
- package/src/SvMaskedInput.svelte +122 -122
- package/src/SvMenu.svelte +124 -124
- package/src/SvMenu.test.ts +97 -97
- package/src/SvMenuList.svelte +146 -146
- package/src/SvMultiSelect.svelte +281 -281
- package/src/SvNavPane.test.ts +15 -4
- package/src/SvNumberInput.svelte +168 -168
- package/src/SvOtpInput.svelte +158 -158
- package/src/SvPasswordInput.svelte +151 -151
- package/src/SvPhoneInput.svelte +133 -131
- package/src/SvPopover.svelte +197 -197
- package/src/SvRadioGroup.svelte +107 -107
- package/src/SvRowGroupPanel.svelte +170 -170
- package/src/SvScrollArea.svelte +61 -61
- package/src/SvSlider.svelte +200 -200
- package/src/SvSwitchButton.svelte +107 -107
- package/src/SvTagsInput.svelte +112 -112
- package/src/SvTextInput.svelte +147 -147
- package/src/SvTimePicker.svelte +245 -245
- package/src/SvToaster.svelte +159 -159
- package/src/SvToaster.test.ts +95 -95
- package/src/SvToggleButton.svelte +85 -85
- package/src/SvTooltip.svelte +161 -161
- package/src/SvTour.svelte +204 -204
- package/src/SvTree.svelte +437 -437
- package/src/SvTreeSelect.svelte +233 -233
- package/src/a11y/dismissable.test.ts +119 -119
- package/src/a11y/dismissable.ts +114 -114
- package/src/a11y.contract.test.ts +49 -49
- package/src/a11y.test.ts +59 -59
- package/src/a11y.ts +61 -61
- package/src/ai.test.ts +502 -502
- package/src/ai.ts +1391 -1391
- package/src/build-api.coverage.test.ts +633 -633
- package/src/build-api.ts +895 -895
- package/src/builtin-editors.grid.test.ts +85 -85
- package/src/cell-formatting.ts +169 -169
- package/src/cell-render.ts +469 -469
- package/src/chart-export.ts +201 -201
- package/src/chart-view.svelte.ts +36 -36
- package/src/chart.ts +2296 -2296
- package/src/collaboration.test.ts +104 -104
- package/src/collaboration.ts +167 -167
- package/src/column-groups.test.ts +48 -0
- package/src/column-groups.ts +8 -10
- package/src/column-id.ts +30 -0
- package/src/core.performance.test.ts +30 -30
- package/src/core.ts +1112 -1111
- package/src/createAutocomplete.svelte.ts +132 -132
- package/src/createCombobox.svelte.ts +191 -191
- package/src/createCountryInput.svelte.ts +157 -157
- package/src/createDropdownList.svelte.ts +168 -168
- package/src/createForm.svelte.ts +386 -386
- package/src/createGrid.svelte.ts +42 -42
- package/src/createGrid.test.ts +10 -10
- package/src/createGridState.svelte.ts +17 -17
- package/src/createListbox.svelte.ts +39 -7
- package/src/createMenu.svelte.ts +225 -225
- package/src/createPopoverSelect.svelte.ts +213 -213
- package/src/createSlider.svelte.ts +191 -191
- package/src/createTooltip.svelte.ts +144 -144
- package/src/createTree.svelte.ts +319 -319
- package/src/editing.test.ts +949 -859
- package/src/editing.ts +709 -675
- package/src/editor-contract.ts +157 -157
- package/src/export-data-api.test.ts +126 -126
- package/src/export-format.test.ts +107 -107
- package/src/export-format.ts +598 -598
- package/src/filter-operators.ts +157 -157
- package/src/filtering/excel-filters.test.ts +44 -1
- package/src/filtering/excel-filters.ts +91 -14
- package/src/flex-render.ts +3 -3
- package/src/form-field.ts +127 -127
- package/src/index.ts +715 -713
- package/src/js-scroller.svelte.ts +173 -173
- package/src/keyboard.test.ts +59 -59
- package/src/keyboard.ts +97 -97
- package/src/list-nav.test.ts +49 -49
- package/src/list-nav.ts +29 -29
- package/src/list-option.test.ts +56 -56
- package/src/list-option.ts +172 -172
- package/src/menus.test.ts +736 -700
- package/src/menus.ts +679 -652
- package/src/merge-objects.ts +48 -48
- package/src/overlays.test.ts +90 -90
- package/src/render-component.ts +28 -28
- package/src/row-resize.test.ts +100 -0
- package/src/row-resize.ts +36 -0
- package/src/scheduler-model.test.ts +562 -562
- package/src/scheduler-model.ts +872 -872
- package/src/selection.test.ts +754 -754
- package/src/selection.ts +600 -600
- package/src/server-data-source.test.ts +289 -289
- package/src/server-data-source.ts +413 -413
- package/src/sparkline.test.ts +68 -68
- package/src/sparkline.ts +169 -169
- package/src/spreadsheet.test.ts +489 -489
- package/src/spreadsheet.ts +312 -312
- package/src/static-functions.ts +11 -11
- package/src/subscribe.ts +38 -38
- package/src/svgrid-wrapper.types.ts +524 -524
- package/src/svgrid.api-extensions.test.ts +8 -3
- package/src/svgrid.behavior.test.ts +706 -706
- package/src/svgrid.cell-dom-ids.test.ts +96 -0
- package/src/svgrid.charting.test.ts +534 -534
- package/src/svgrid.conditional-stat-scope.test.ts +111 -0
- package/src/svgrid.features.test.ts +157 -157
- package/src/svgrid.filter-menu-listbox.svelte.test.ts +308 -0
- package/src/svgrid.filter-menu-scroll.test.ts +3 -1
- package/src/svgrid.group-header-ids.test.ts +121 -0
- package/src/svgrid.new-features.wrapper.test.ts +251 -251
- package/src/svgrid.wrapper.test.ts +40 -40
- package/src/test-setup.ts +62 -62
- package/src/themes/index.ts +215 -215
- package/src/toast-store.svelte.ts +246 -246
- package/src/toast-store.test.ts +147 -147
- package/src/ui-buttons.test.ts +144 -144
- package/src/ui-inputs.test.ts +118 -118
- package/src/ui-range.test.ts +70 -70
- package/src/ui-selection.test.ts +155 -155
- package/src/ui-tier1.test.ts +142 -142
- package/src/virtual.test.ts +88 -88
- package/src/virtualization/column-virtualizer.test.ts +27 -27
- package/src/virtualization/column-virtualizer.ts +30 -30
- package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
- package/src/virtualization/types.ts +30 -30
- package/src/virtualization/virtualizer.test.ts +47 -47
- package/src/virtualization/virtualizer.ts +296 -296
- package/themes/ember.css +71 -71
- package/dist/cdn/GridMenus-BzxEYNtW.js +0 -421
- package/dist/cdn/GridMenus-Dr-nGEXn.js +0 -417
|
@@ -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>
|
package/src/SvScrollArea.svelte
CHANGED
|
@@ -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>
|