@svgrid/grid 1.2.22 → 1.2.23
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/dist/GridFooter.svelte +34 -20
- package/dist/GridFooter.svelte.d.ts +8 -0
- package/dist/SvAutoComplete.svelte +100 -0
- package/dist/SvAutoComplete.svelte.d.ts +16 -0
- package/dist/SvButton.svelte +97 -0
- package/dist/SvButton.svelte.d.ts +23 -0
- package/dist/SvCalendar.svelte +669 -0
- package/dist/SvCalendar.svelte.d.ts +70 -0
- package/dist/SvCheckBox.svelte +81 -0
- package/dist/SvCheckBox.svelte.d.ts +19 -0
- package/dist/SvColorInput.svelte +187 -0
- package/dist/SvColorInput.svelte.d.ts +15 -0
- package/dist/SvComboBox.svelte +130 -0
- package/dist/SvComboBox.svelte.d.ts +15 -0
- package/dist/SvCountryInput.svelte +123 -0
- package/dist/SvCountryInput.svelte.d.ts +14 -0
- package/dist/SvDateTimePicker.svelte +322 -0
- package/dist/SvDateTimePicker.svelte.d.ts +37 -0
- package/dist/SvDropDownList.svelte +143 -0
- package/dist/SvDropDownList.svelte.d.ts +15 -0
- package/dist/SvForm.svelte +147 -0
- package/dist/SvForm.svelte.d.ts +29 -0
- package/dist/SvGauge.svelte +99 -0
- package/dist/SvGauge.svelte.d.ts +31 -0
- package/dist/SvGrid.controller.svelte.d.ts +16 -2
- package/dist/SvGrid.controller.svelte.js +63 -8
- package/dist/SvGrid.css +84 -31
- package/dist/SvGrid.helpers.d.ts +4 -0
- package/dist/SvGrid.helpers.js +40 -12
- package/dist/SvGrid.svelte +84 -5
- package/dist/SvGrid.types.d.ts +36 -0
- package/dist/SvGridDropdown.svelte +16 -30
- package/dist/SvGridDropdown.svelte.d.ts +4 -0
- package/dist/SvListBox.svelte +122 -0
- package/dist/SvListBox.svelte.d.ts +19 -0
- package/dist/SvMaskedInput.svelte +0 -0
- package/dist/SvMaskedInput.svelte.d.ts +15 -0
- package/dist/SvNumberInput.svelte +0 -0
- package/dist/SvNumberInput.svelte.d.ts +22 -0
- package/dist/SvPasswordInput.svelte +110 -0
- package/dist/SvPasswordInput.svelte.d.ts +22 -0
- package/dist/SvPhoneInput.svelte +0 -0
- package/dist/SvPhoneInput.svelte.d.ts +19 -0
- package/dist/SvRadioGroup.svelte +108 -0
- package/dist/SvRadioGroup.svelte.d.ts +23 -0
- package/dist/SvRating.svelte +111 -0
- package/dist/SvRating.svelte.d.ts +20 -0
- package/dist/SvRepeatButton.svelte +89 -0
- package/dist/SvRepeatButton.svelte.d.ts +21 -0
- package/dist/SvSlider.svelte +182 -0
- package/dist/SvSlider.svelte.d.ts +25 -0
- package/dist/SvSwitchButton.svelte +94 -0
- package/dist/SvSwitchButton.svelte.d.ts +19 -0
- package/dist/SvTabs.svelte +122 -0
- package/dist/SvTabs.svelte.d.ts +26 -0
- package/dist/SvTagsInput.svelte +81 -0
- package/dist/SvTagsInput.svelte.d.ts +19 -0
- package/dist/SvTimePicker.svelte +359 -0
- package/dist/SvTimePicker.svelte.d.ts +27 -0
- package/dist/SvToggleButton.svelte +57 -0
- package/dist/SvToggleButton.svelte.d.ts +16 -0
- package/dist/SvTree.svelte +210 -0
- package/dist/SvTree.svelte.d.ts +29 -0
- package/dist/build-api.js +78 -1
- package/dist/cdn/svgrid.js +10022 -5904
- package/dist/cdn/svgrid.svelte-external.js +8531 -4429
- package/dist/cell-render.d.ts +4 -0
- package/dist/cell-render.js +35 -1
- package/dist/clipboard.js +19 -1
- package/dist/collaboration.d.ts +2 -0
- package/dist/collaboration.js +4 -0
- package/dist/core.d.ts +22 -1
- package/dist/countries.d.ts +15 -0
- package/dist/countries.js +61 -0
- package/dist/datetime/date-core.d.ts +88 -0
- package/dist/datetime/date-core.js +165 -0
- package/dist/datetime/date-format.d.ts +39 -0
- package/dist/datetime/date-format.js +371 -0
- package/dist/datetime/date-restrict.d.ts +23 -0
- package/dist/datetime/date-restrict.js +50 -0
- package/dist/datetime/date-selection.d.ts +25 -0
- package/dist/datetime/date-selection.js +94 -0
- package/dist/datetime/mask.d.ts +21 -0
- package/dist/datetime/mask.js +90 -0
- package/dist/editing.d.ts +1 -1
- package/dist/editing.js +7 -0
- package/dist/editors/cell-editors.d.ts +2 -2
- package/dist/editors/cell-editors.js +3 -1
- package/dist/export-format.d.ts +178 -0
- package/dist/export-format.js +419 -0
- package/dist/hyperformula-adapter.d.ts +4 -0
- package/dist/hyperformula-adapter.js +15 -6
- package/dist/index.d.ts +36 -0
- package/dist/index.js +41 -0
- package/dist/list-option.d.ts +10 -0
- package/dist/list-option.js +7 -0
- package/dist/menus.js +18 -2
- package/dist/popover.d.ts +48 -0
- package/dist/popover.js +65 -0
- package/dist/row-resize.d.ts +4 -4
- package/dist/row-resize.js +8 -5
- package/dist/selection.d.ts +6 -0
- package/dist/selection.js +31 -5
- package/dist/server-data-source.d.ts +43 -0
- package/dist/server-data-source.js +100 -0
- package/dist/sparkline.js +8 -4
- package/dist/svgrid-wrapper.types.d.ts +26 -1
- package/package.json +6 -1
- package/src/GridFooter.svelte +34 -20
- package/src/SvAutoComplete.svelte +100 -0
- package/src/SvButton.svelte +97 -0
- package/src/SvCalendar.svelte +669 -0
- package/src/SvCalendar.test.ts +211 -0
- package/src/SvCheckBox.svelte +81 -0
- package/src/SvColorInput.svelte +187 -0
- package/src/SvComboBox.svelte +130 -0
- package/src/SvCountryInput.svelte +123 -0
- package/src/SvDateTimePicker.svelte +322 -0
- package/src/SvDateTimePicker.test.ts +136 -0
- package/src/SvDropDownList.svelte +143 -0
- package/src/SvForm.svelte +147 -0
- package/src/SvGauge.svelte +99 -0
- package/src/SvGrid.controller.svelte.ts +74 -8
- package/src/SvGrid.css +84 -31
- package/src/SvGrid.helpers.ts +41 -12
- package/src/SvGrid.svelte +84 -5
- package/src/SvGrid.types.ts +33 -0
- package/src/SvGridDropdown.svelte +16 -30
- package/src/SvListBox.svelte +122 -0
- package/src/SvMaskedInput.svelte +0 -0
- package/src/SvNumberInput.svelte +0 -0
- package/src/SvPasswordInput.svelte +110 -0
- package/src/SvPhoneInput.svelte +0 -0
- package/src/SvRadioGroup.svelte +108 -0
- package/src/SvRating.svelte +111 -0
- package/src/SvRepeatButton.svelte +89 -0
- package/src/SvSlider.svelte +182 -0
- package/src/SvSwitchButton.svelte +94 -0
- package/src/SvTabs.svelte +122 -0
- package/src/SvTagsInput.svelte +81 -0
- package/src/SvTimePicker.svelte +359 -0
- package/src/SvTimePicker.test.ts +98 -0
- package/src/SvToggleButton.svelte +57 -0
- package/src/SvTree.svelte +210 -0
- package/src/build-api.ts +115 -0
- package/src/cell-render.test.ts +38 -0
- package/src/cell-render.ts +43 -0
- package/src/clipboard.test.ts +18 -0
- package/src/clipboard.ts +24 -1
- package/src/collaboration.test.ts +30 -0
- package/src/collaboration.ts +6 -0
- package/src/core.ts +27 -3
- package/src/countries.ts +71 -0
- package/src/datetime/date-core.test.ts +217 -0
- package/src/datetime/date-core.ts +204 -0
- package/src/datetime/date-format.test.ts +121 -0
- package/src/datetime/date-format.ts +317 -0
- package/src/datetime/date-restrict.ts +60 -0
- package/src/datetime/date-selection.test.ts +129 -0
- package/src/datetime/date-selection.ts +137 -0
- package/src/datetime/mask.test.ts +36 -0
- package/src/datetime/mask.ts +84 -0
- package/src/editing.test.ts +22 -0
- package/src/editing.ts +7 -1
- package/src/editors/cell-editors.ts +7 -1
- package/src/export-data-api.test.ts +126 -0
- package/src/export-format.test.ts +107 -0
- package/src/export-format.ts +598 -0
- package/src/hyperformula-adapter.test.ts +35 -1
- package/src/hyperformula-adapter.ts +18 -6
- package/src/index.ts +71 -0
- package/src/list-option.ts +15 -0
- package/src/menus.ts +21 -2
- package/src/popover.ts +79 -0
- package/src/row-resize.test.ts +25 -0
- package/src/row-resize.ts +8 -5
- package/src/selection.test.ts +36 -4
- package/src/selection.ts +30 -9
- package/src/server-data-source.test.ts +201 -0
- package/src/server-data-source.ts +148 -0
- package/src/sparkline.test.ts +9 -0
- package/src/sparkline.ts +9 -4
- package/src/svgrid-wrapper.types.ts +28 -1
- package/src/svgrid.behavior.test.ts +27 -0
- package/src/svgrid.new-features.wrapper.test.ts +27 -1
- package/src/ui-buttons.test.ts +105 -0
- package/src/ui-composite.test.ts +89 -0
- package/src/ui-inputs.test.ts +92 -0
- package/src/ui-range.test.ts +61 -0
- package/src/ui-selection.test.ts +106 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type FormFieldType = 'text' | 'email' | 'tel' | 'textarea' | 'number' | 'password' | 'select' | 'checkbox' | 'switch' | 'date' | 'color' | 'rating';
|
|
2
|
+
export type FormField = {
|
|
3
|
+
name: string;
|
|
4
|
+
label: string;
|
|
5
|
+
type?: FormFieldType;
|
|
6
|
+
required?: boolean;
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
options?: Array<{
|
|
9
|
+
value: string | number;
|
|
10
|
+
label: string;
|
|
11
|
+
}>;
|
|
12
|
+
/** Return an error message, or null/undefined when valid. */
|
|
13
|
+
validate?: (value: any, values: Record<string, any>) => string | null | undefined;
|
|
14
|
+
/** Span full width in the grid. */
|
|
15
|
+
full?: boolean;
|
|
16
|
+
};
|
|
17
|
+
type Props = {
|
|
18
|
+
fields: ReadonlyArray<FormField>;
|
|
19
|
+
initial?: Record<string, any>;
|
|
20
|
+
onSubmit?: (values: Record<string, any>) => void;
|
|
21
|
+
onChange?: (values: Record<string, any>) => void;
|
|
22
|
+
submitLabel?: string;
|
|
23
|
+
/** Columns in the responsive field grid. */
|
|
24
|
+
columns?: number;
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
};
|
|
27
|
+
declare const SvForm: import("svelte").Component<Props, {}, "">;
|
|
28
|
+
type SvForm = ReturnType<typeof SvForm>;
|
|
29
|
+
export default SvForm;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* SvGauge - a radial arc gauge (SVG) that renders a value within [min, max]
|
|
4
|
+
* with optional colored threshold bands, a needle, and a center label. Parity:
|
|
5
|
+
* Smart `smart-gauge` (radial). Display control; theme-driven.
|
|
6
|
+
*/
|
|
7
|
+
type Band = { from: number; to: number; color: string }
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
value?: number
|
|
11
|
+
min?: number
|
|
12
|
+
max?: number
|
|
13
|
+
/** Sweep angle in degrees (default 270, a classic dashboard gauge). */
|
|
14
|
+
sweep?: number
|
|
15
|
+
/** Colored threshold bands along the arc. */
|
|
16
|
+
bands?: Band[]
|
|
17
|
+
/** Show the needle. */
|
|
18
|
+
needle?: boolean
|
|
19
|
+
/** Center label (defaults to the formatted value). */
|
|
20
|
+
label?: string
|
|
21
|
+
unit?: string
|
|
22
|
+
size?: number
|
|
23
|
+
thickness?: number
|
|
24
|
+
formatValue?: (v: number) => string
|
|
25
|
+
ariaLabel?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
value = 0,
|
|
30
|
+
min = 0,
|
|
31
|
+
max = 100,
|
|
32
|
+
sweep = 270,
|
|
33
|
+
bands = [],
|
|
34
|
+
needle = true,
|
|
35
|
+
label,
|
|
36
|
+
unit = '',
|
|
37
|
+
size = 180,
|
|
38
|
+
thickness = 14,
|
|
39
|
+
formatValue,
|
|
40
|
+
ariaLabel,
|
|
41
|
+
}: Props = $props()
|
|
42
|
+
|
|
43
|
+
const clamped = $derived(Math.min(max, Math.max(min, value)))
|
|
44
|
+
const startAngle = $derived(90 + (360 - sweep) / 2) // symmetric around the bottom
|
|
45
|
+
const cx = $derived(size / 2)
|
|
46
|
+
const cy = $derived(size / 2)
|
|
47
|
+
const r = $derived(size / 2 - thickness / 2 - 2)
|
|
48
|
+
|
|
49
|
+
function frac(v: number) { return (v - min) / (max - min) }
|
|
50
|
+
function angleOf(v: number) { return startAngle + frac(v) * sweep }
|
|
51
|
+
function polar(angleDeg: number, radius: number) {
|
|
52
|
+
const a = (angleDeg * Math.PI) / 180
|
|
53
|
+
return { x: cx + radius * Math.cos(a), y: cy + radius * Math.sin(a) }
|
|
54
|
+
}
|
|
55
|
+
function arcPath(v0: number, v1: number, radius: number): string {
|
|
56
|
+
const a0 = angleOf(v0)
|
|
57
|
+
const a1 = angleOf(v1)
|
|
58
|
+
const p0 = polar(a0, radius)
|
|
59
|
+
const p1 = polar(a1, radius)
|
|
60
|
+
const large = a1 - a0 > 180 ? 1 : 0
|
|
61
|
+
return `M ${p0.x} ${p0.y} A ${radius} ${radius} 0 ${large} 1 ${p1.x} ${p1.y}`
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const needleEnd = $derived(polar(angleOf(clamped), r - thickness / 2))
|
|
65
|
+
const display = $derived(label ?? `${formatValue ? formatValue(clamped) : Math.round(clamped)}${unit}`)
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<div class="sv-gauge" role="meter" aria-valuenow={clamped} aria-valuemin={min} aria-valuemax={max} aria-label={ariaLabel ?? 'Gauge'}>
|
|
69
|
+
<svg viewBox={`0 0 ${size} ${size}`} width={size} height={size} class="sv-gauge__svg">
|
|
70
|
+
<!-- track -->
|
|
71
|
+
<path d={arcPath(min, max, r)} class="sv-gauge__track" fill="none" stroke-width={thickness} stroke-linecap="round" />
|
|
72
|
+
<!-- bands (or accent fill up to value when no bands) -->
|
|
73
|
+
{#if bands.length}
|
|
74
|
+
{#each bands as band (band.from + '-' + band.to)}
|
|
75
|
+
<path d={arcPath(band.from, band.to, r)} fill="none" stroke={band.color} stroke-width={thickness} stroke-linecap="round" />
|
|
76
|
+
{/each}
|
|
77
|
+
{:else}
|
|
78
|
+
<path d={arcPath(min, clamped, r)} class="sv-gauge__value" fill="none" stroke-width={thickness} stroke-linecap="round" />
|
|
79
|
+
{/if}
|
|
80
|
+
{#if needle}
|
|
81
|
+
<line x1={cx} y1={cy} x2={needleEnd.x} y2={needleEnd.y} class="sv-gauge__needle" stroke-linecap="round" />
|
|
82
|
+
<circle cx={cx} cy={cy} r="5" class="sv-gauge__hub" />
|
|
83
|
+
{/if}
|
|
84
|
+
</svg>
|
|
85
|
+
<div class="sv-gauge__label">{display}</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<style>
|
|
89
|
+
.sv-gauge { position: relative; display: inline-flex; flex-direction: column; align-items: center; color: var(--sg-fg, #0f172a); }
|
|
90
|
+
.sv-gauge__svg { display: block; }
|
|
91
|
+
.sv-gauge__track { stroke: var(--sg-border, #e2e8f0); }
|
|
92
|
+
.sv-gauge__value { stroke: var(--sg-accent, #2563eb); }
|
|
93
|
+
.sv-gauge__needle { stroke: var(--sg-fg, #0f172a); stroke-width: 3; }
|
|
94
|
+
.sv-gauge__hub { fill: var(--sg-fg, #0f172a); }
|
|
95
|
+
.sv-gauge__label {
|
|
96
|
+
position: absolute; left: 0; right: 0; bottom: 22%; text-align: center;
|
|
97
|
+
font-size: 22px; font-weight: 750; font-variant-numeric: tabular-nums;
|
|
98
|
+
}
|
|
99
|
+
</style>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SvGauge - a radial arc gauge (SVG) that renders a value within [min, max]
|
|
3
|
+
* with optional colored threshold bands, a needle, and a center label. Parity:
|
|
4
|
+
* Smart `smart-gauge` (radial). Display control; theme-driven.
|
|
5
|
+
*/
|
|
6
|
+
type Band = {
|
|
7
|
+
from: number;
|
|
8
|
+
to: number;
|
|
9
|
+
color: string;
|
|
10
|
+
};
|
|
11
|
+
type Props = {
|
|
12
|
+
value?: number;
|
|
13
|
+
min?: number;
|
|
14
|
+
max?: number;
|
|
15
|
+
/** Sweep angle in degrees (default 270, a classic dashboard gauge). */
|
|
16
|
+
sweep?: number;
|
|
17
|
+
/** Colored threshold bands along the arc. */
|
|
18
|
+
bands?: Band[];
|
|
19
|
+
/** Show the needle. */
|
|
20
|
+
needle?: boolean;
|
|
21
|
+
/** Center label (defaults to the formatted value). */
|
|
22
|
+
label?: string;
|
|
23
|
+
unit?: string;
|
|
24
|
+
size?: number;
|
|
25
|
+
thickness?: number;
|
|
26
|
+
formatValue?: (v: number) => string;
|
|
27
|
+
ariaLabel?: string;
|
|
28
|
+
};
|
|
29
|
+
declare const SvGauge: import("svelte").Component<Props, {}, "">;
|
|
30
|
+
type SvGauge = ReturnType<typeof SvGauge>;
|
|
31
|
+
export default SvGauge;
|
|
@@ -225,6 +225,10 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
225
225
|
readonly sortDirectionByColumn: Record<string, false | "asc" | "desc">;
|
|
226
226
|
readonly groupingColumns: any;
|
|
227
227
|
readonly paginationState: any;
|
|
228
|
+
readonly externalPaginationEnabled: boolean;
|
|
229
|
+
readonly paginationTotalRows: number;
|
|
230
|
+
readonly paginationPageIndex: any;
|
|
231
|
+
readonly paginationPageSize: any;
|
|
228
232
|
readonly getRowColumnValue: (row: Row<TData>, columnId: string) => unknown;
|
|
229
233
|
readonly allRowsBeforePagination: Row<TData>[];
|
|
230
234
|
readonly allRows: Row<TData>[];
|
|
@@ -327,6 +331,10 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
327
331
|
readonly computeRowClass: (row: Row<TData>, rowIndex: number) => string;
|
|
328
332
|
readonly computeCellClass: (row: Row<TData>, column: Column<TData>) => string;
|
|
329
333
|
readonly computeCellTooltip: (row: Row<TData>, column: Column<TData>) => string | null;
|
|
334
|
+
readonly computeCellValidity: (row: Row<TData>, column: Column<TData>) => {
|
|
335
|
+
invalid: boolean;
|
|
336
|
+
message: string | null;
|
|
337
|
+
};
|
|
330
338
|
readonly computeCellNote: (row: Row<TData>, column: Column<TData>) => string | null;
|
|
331
339
|
readonly getCellDisplayValue: (rowId: string, columnId: string, baseValue: unknown) => any;
|
|
332
340
|
readonly getColumnAlign: typeof getColumnAlign;
|
|
@@ -339,7 +347,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
339
347
|
readonly computePinnedCellClass: (rowData: TData, column: Column<TData>) => string;
|
|
340
348
|
readonly isRowSelected: (rowId: string) => boolean;
|
|
341
349
|
readonly toggleRowSelectionById: (rowId: string) => void;
|
|
342
|
-
readonly headerSelectionState: "
|
|
350
|
+
readonly headerSelectionState: "all" | "none" | "some";
|
|
343
351
|
readonly toggleSelectAllRows: () => void;
|
|
344
352
|
userHasActivatedCell: boolean;
|
|
345
353
|
readonly setActiveCell: (rowIndex: number, colIndex: number) => void;
|
|
@@ -372,6 +380,12 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
372
380
|
colIndex: any;
|
|
373
381
|
} | null;
|
|
374
382
|
readonly isInFillPreview: (rowIndex: number, colIndex: number) => boolean;
|
|
383
|
+
readonly fillMarqueeEdges: (rowIndex: number, colIndex: number) => {
|
|
384
|
+
top: boolean;
|
|
385
|
+
bottom: boolean;
|
|
386
|
+
left: boolean;
|
|
387
|
+
right: boolean;
|
|
388
|
+
} | null;
|
|
375
389
|
readonly findColumnById: (columnId: string) => any;
|
|
376
390
|
readonly readCellRaw: (rowIndex: number, columnId: string) => unknown;
|
|
377
391
|
readonly writeCellRaw: (rowIndex: number, columnId: string, value: unknown) => void;
|
|
@@ -408,7 +422,7 @@ export declare function createSvGridController<TFeatures extends TableFeatures =
|
|
|
408
422
|
before: unknown;
|
|
409
423
|
after: unknown;
|
|
410
424
|
}, direction: "undo" | "redo") => void;
|
|
411
|
-
readonly updateEditingCellValue: (value:
|
|
425
|
+
readonly updateEditingCellValue: (value: unknown) => void;
|
|
412
426
|
readonly onEditorKeyDown: (event: KeyboardEvent) => void;
|
|
413
427
|
readonly focusOnMount: (node: HTMLInputElement | HTMLTextAreaElement) => void;
|
|
414
428
|
readonly onHeaderSortClick: (event: MouseEvent, columnId: string) => void;
|
|
@@ -243,6 +243,10 @@ export function createSvGridController(props) {
|
|
|
243
243
|
});
|
|
244
244
|
let columnVirtualizerVersion = $state(0);
|
|
245
245
|
let gridStateVersion = $state(0);
|
|
246
|
+
// Bumps only when a row-model-affecting slice changes (see the store
|
|
247
|
+
// subscription below) - the row-model derivation depends on THIS, not the
|
|
248
|
+
// catch-all gridStateVersion, so navigation doesn't rebuild 1M rows.
|
|
249
|
+
let dataStateVersion = $state(0);
|
|
246
250
|
const selectionColumnWidth = 44;
|
|
247
251
|
const rowNumberColumnWidth = $derived(props.rowNumberWidth ?? 56);
|
|
248
252
|
const showRowNumbersEffective = $derived(props.showRowNumbers ?? false);
|
|
@@ -391,6 +395,10 @@ export function createSvGridController(props) {
|
|
|
391
395
|
// the wrapper records filter state but does not actually filter rows.
|
|
392
396
|
// svelte-ignore state_referenced_locally
|
|
393
397
|
const externalFilterEnabled = props.externalFilter === true;
|
|
398
|
+
// Server-side pagination: the footer reads rowCount/pageIndex from props and
|
|
399
|
+
// emits onPaginationChange instead of slicing locally. Controlled by the
|
|
400
|
+
// consumer. Reactive (unlike sort/filter) so pageIndex/rowCount can change.
|
|
401
|
+
const externalPaginationEnabled = $derived(props.externalPagination === true);
|
|
394
402
|
const passthroughSortedRowModel = ({ rows }) => rows;
|
|
395
403
|
const grid = createSvGrid({
|
|
396
404
|
get _features() {
|
|
@@ -434,9 +442,34 @@ export function createSvGridController(props) {
|
|
|
434
442
|
activeCell: { rowIndex: 0, colIndex: 0, cellId: null },
|
|
435
443
|
},
|
|
436
444
|
});
|
|
445
|
+
// `gridStateVersion` bumps on EVERY store change (incl. moving the active
|
|
446
|
+
// cell). `dataStateVersion` bumps ONLY when a slice that actually changes the
|
|
447
|
+
// row model changes (sort / filter / pagination / grouping / expansion /
|
|
448
|
+
// selection) - so the O(rows) row-model derivation does NOT re-run on plain
|
|
449
|
+
// keyboard navigation. Without this, arrow-keying a 1,000,000-row grid re-ran
|
|
450
|
+
// the entire core->filter->sort->group pipeline on every keystroke.
|
|
451
|
+
let prevDataSlices = null;
|
|
437
452
|
$effect(() => {
|
|
438
453
|
const unsubscribe = grid.store.subscribe(() => {
|
|
439
454
|
gridStateVersion += 1;
|
|
455
|
+
const s = grid.getState();
|
|
456
|
+
if (!prevDataSlices ||
|
|
457
|
+
prevDataSlices.sorting !== s.sorting ||
|
|
458
|
+
prevDataSlices.columnFilters !== s.columnFilters ||
|
|
459
|
+
prevDataSlices.pagination !== s.pagination ||
|
|
460
|
+
prevDataSlices.grouping !== s.grouping ||
|
|
461
|
+
prevDataSlices.expanded !== s.expanded ||
|
|
462
|
+
prevDataSlices.rowSelection !== s.rowSelection) {
|
|
463
|
+
dataStateVersion += 1;
|
|
464
|
+
prevDataSlices = {
|
|
465
|
+
sorting: s.sorting,
|
|
466
|
+
columnFilters: s.columnFilters,
|
|
467
|
+
pagination: s.pagination,
|
|
468
|
+
grouping: s.grouping,
|
|
469
|
+
expanded: s.expanded,
|
|
470
|
+
rowSelection: s.rowSelection,
|
|
471
|
+
};
|
|
472
|
+
}
|
|
440
473
|
});
|
|
441
474
|
return unsubscribe;
|
|
442
475
|
});
|
|
@@ -707,7 +740,11 @@ export function createSvGridController(props) {
|
|
|
707
740
|
* filters reduce the dataset.
|
|
708
741
|
*/
|
|
709
742
|
const allRowsBeforePagination = $derived.by(() => {
|
|
710
|
-
|
|
743
|
+
// Depend on dataStateVersion (row-model-affecting store changes) NOT
|
|
744
|
+
// gridStateVersion - so moving the active cell / selection does not force
|
|
745
|
+
// this O(rows) pipeline to re-run. Filter-input state (globalFilter etc.)
|
|
746
|
+
// and internalData are read below and tracked as their own dependencies.
|
|
747
|
+
dataStateVersion;
|
|
711
748
|
// Touch internalData + internalColumns so the row model re-derives when
|
|
712
749
|
// the consumer replaces the data array (e.g. via a "Reset" button).
|
|
713
750
|
void internalData;
|
|
@@ -794,7 +831,8 @@ export function createSvGridController(props) {
|
|
|
794
831
|
*/
|
|
795
832
|
const allRows = $derived.by(() => {
|
|
796
833
|
const rows = allRowsBeforePagination;
|
|
797
|
-
|
|
834
|
+
// External pagination: `data` already IS the current page - never slice.
|
|
835
|
+
if (!paginationEnabled || externalPaginationEnabled)
|
|
798
836
|
return rows;
|
|
799
837
|
const { pageIndex, pageSize } = paginationState;
|
|
800
838
|
const start = pageIndex * pageSize;
|
|
@@ -802,8 +840,9 @@ export function createSvGridController(props) {
|
|
|
802
840
|
});
|
|
803
841
|
// When a filter reduces the dataset, the stored pageIndex can point beyond
|
|
804
842
|
// the last valid page. Reset to page 0 so the grid never shows a blank body.
|
|
843
|
+
// Skipped for external pagination, where the consumer owns pageIndex.
|
|
805
844
|
$effect(() => {
|
|
806
|
-
if (!paginationEnabled)
|
|
845
|
+
if (!paginationEnabled || externalPaginationEnabled)
|
|
807
846
|
return;
|
|
808
847
|
const { pageIndex, pageSize } = paginationState;
|
|
809
848
|
const pageCount = Math.ceil(allRowsBeforePagination.length / pageSize);
|
|
@@ -811,6 +850,11 @@ export function createSvGridController(props) {
|
|
|
811
850
|
grid.setPagination({ pageIndex: 0, pageSize });
|
|
812
851
|
}
|
|
813
852
|
});
|
|
853
|
+
// Footer-facing pagination values. In external mode they come from the
|
|
854
|
+
// consumer-controlled props; otherwise from the local row model + state.
|
|
855
|
+
const paginationTotalRows = $derived(externalPaginationEnabled ? (props.rowCount ?? 0) : allRowsBeforePagination.length);
|
|
856
|
+
const paginationPageIndex = $derived(externalPaginationEnabled ? (props.pageIndex ?? 0) : paginationState.pageIndex);
|
|
857
|
+
const paginationPageSize = $derived(externalPaginationEnabled ? (props.pageSize ?? 10) : paginationState.pageSize);
|
|
814
858
|
const rowSelectionState = $derived.by(() => {
|
|
815
859
|
gridStateVersion;
|
|
816
860
|
return grid.getState().rowSelection ?? {};
|
|
@@ -1179,9 +1223,14 @@ export function createSvGridController(props) {
|
|
|
1179
1223
|
const SUMMARY_DEFER_CELL_LIMIT = 50000;
|
|
1180
1224
|
let summaryByColumn = $state({});
|
|
1181
1225
|
$effect(() => {
|
|
1182
|
-
// Re-aggregate whenever the
|
|
1183
|
-
//
|
|
1184
|
-
gridStateVersion
|
|
1226
|
+
// Re-aggregate whenever the data / columns / edits change. We depend on
|
|
1227
|
+
// `allRows` / `allColumns` / `editedCellValues` DIRECTLY - not the
|
|
1228
|
+
// catch-all `gridStateVersion` - because that version bumps on EVERY store
|
|
1229
|
+
// change, including moving the active cell or selection. `allRows` stays
|
|
1230
|
+
// referentially stable across those (sort/filter/paginate produce a new
|
|
1231
|
+
// rows array; navigation does not), so this now skips the
|
|
1232
|
+
// O(rows x cols) aggregation on plain keyboard navigation - which was
|
|
1233
|
+
// making arrow-key movement crawl on huge grids (e.g. 1,000,000 rows).
|
|
1185
1234
|
void editedCellValues;
|
|
1186
1235
|
const rows = allRows;
|
|
1187
1236
|
const columns = allColumns;
|
|
@@ -1776,6 +1825,10 @@ export function createSvGridController(props) {
|
|
|
1776
1825
|
get sortDirectionByColumn() { return sortDirectionByColumn; },
|
|
1777
1826
|
get groupingColumns() { return groupingColumns; },
|
|
1778
1827
|
get paginationState() { return paginationState; },
|
|
1828
|
+
get externalPaginationEnabled() { return externalPaginationEnabled; },
|
|
1829
|
+
get paginationTotalRows() { return paginationTotalRows; },
|
|
1830
|
+
get paginationPageIndex() { return paginationPageIndex; },
|
|
1831
|
+
get paginationPageSize() { return paginationPageSize; },
|
|
1779
1832
|
get getRowColumnValue() { return getRowColumnValue; },
|
|
1780
1833
|
get allRowsBeforePagination() { return allRowsBeforePagination; },
|
|
1781
1834
|
get allRows() { return allRows; },
|
|
@@ -1841,6 +1894,7 @@ export function createSvGridController(props) {
|
|
|
1841
1894
|
get computeRowClass() { return computeRowClass; },
|
|
1842
1895
|
get computeCellClass() { return computeCellClass; },
|
|
1843
1896
|
get computeCellTooltip() { return computeCellTooltip; },
|
|
1897
|
+
get computeCellValidity() { return computeCellValidity; },
|
|
1844
1898
|
get computeCellNote() { return computeCellNote; },
|
|
1845
1899
|
get getCellDisplayValue() { return getCellDisplayValue; },
|
|
1846
1900
|
get getColumnAlign() { return getColumnAlign; },
|
|
@@ -1876,6 +1930,7 @@ export function createSvGridController(props) {
|
|
|
1876
1930
|
get getSelectionRects() { return getSelectionRects; },
|
|
1877
1931
|
get fillHandleCell() { return fillHandleCell; },
|
|
1878
1932
|
get isInFillPreview() { return isInFillPreview; },
|
|
1933
|
+
get fillMarqueeEdges() { return fillMarqueeEdges; },
|
|
1879
1934
|
get findColumnById() { return findColumnById; },
|
|
1880
1935
|
get readCellRaw() { return readCellRaw; },
|
|
1881
1936
|
get writeCellRaw() { return writeCellRaw; },
|
|
@@ -1969,9 +2024,9 @@ export function createSvGridController(props) {
|
|
|
1969
2024
|
const { onGridKeyDown, onWindowKeydown, onHeaderSortClick } = createKeyboard(ctx);
|
|
1970
2025
|
const { computeSummaries, hasRenderedColumn } = createSummaries(ctx);
|
|
1971
2026
|
const { updateFilterRow, updateFilterOperator, updateFilterMenuValue, updateFilterMenuValueTo, toggleCheckboxWithKeyboard, isColumnFiltered, closeMenus, openChooseColumns, openColumnMenu, openFilterMenu, openOperatorMenu, sortColumnFromMenu, clearColumnSort, groupByColumnFromMenu, clearGroupingFromMenu, isFacetChecked, toggleFacetValue, isAllFacetsChecked, toggleAllFacets, clearColumnFilter, changePage, goToPage, setPageSize, openContextMenu, closeContextMenu, contextMenuItems, saveComment, removeComment, closeCommentEditor } = createMenus(ctx);
|
|
1972
|
-
const { cellConditionalFormat, computeRowClass, computeCellClass, computeCellTooltip, computeCellNote, getColumnEditorOptions, formatListCellValue, formatCellValue, formatPinnedValue, computePinnedCellClass } = createCellRender(ctx);
|
|
2027
|
+
const { cellConditionalFormat, computeRowClass, computeCellClass, computeCellTooltip, computeCellValidity, computeCellNote, getColumnEditorOptions, formatListCellValue, formatCellValue, formatPinnedValue, computePinnedCellClass } = createCellRender(ctx);
|
|
1973
2028
|
const { isCellEditable, isCellEditableAt, getRowColumnValue, getCellDisplayValue, startEditingWithChar, startEditing, stopEditing, startFullRowEdit, setFullRowDraft, commitFullRowEdit, cancelFullRowEdit, saveEditingCell, applyHistoryStep, updateEditingCellValue, onEditorKeyDown, focusOnMount, onCellDoubleClick, pasteFromClipboard, onGridPaste } = createEditing(ctx);
|
|
1974
|
-
const { isRowSelected, toggleRowSelectionById, toggleSelectAllRows, setActiveCell, scrollActiveCellIntoView, setSelection, extendSelection, isCellInSelectedRange, getCellRangeEdges, getSelectionRects, isInFillPreview, findColumnById, onCellPointerDown, onCellPointerEnter, endDragSelection, onWindowPointerMove, onCellClick, emitCellDoubleClick } = createSelection(ctx);
|
|
2029
|
+
const { isRowSelected, toggleRowSelectionById, toggleSelectAllRows, setActiveCell, scrollActiveCellIntoView, setSelection, extendSelection, isCellInSelectedRange, getCellRangeEdges, getSelectionRects, isInFillPreview, fillMarqueeEdges, findColumnById, onCellPointerDown, onCellPointerEnter, endDragSelection, onWindowPointerMove, onCellClick, emitCellDoubleClick } = createSelection(ctx);
|
|
1975
2030
|
const { cellPinStyle, isColumnPinned, getCurrentColumnOrder, emitColumnOrder, setColumnOrderInternal, applyColumnDrop, onColumnHeaderDragStart, onColumnHeaderDragOver, onColumnHeaderDragLeave, onColumnHeaderDrop, onColumnHeaderDragEnd, pinColumnLeft, pinColumnRight, unpinColumn, toggleColumnVisibleInPanel, moveColumnInPanel, toggleGroupInPanel, getColumnBaseWidth, getColumnWidth, startColumnResize, onColumnResizeMove, endColumnResize, measureText, autosizeColumn, autosizeAllColumns, resetColumns } = createColumns(ctx);
|
|
1976
2031
|
const { onRowDragStart, onRowDragOver, onRowDragLeave, onRowDrop, onRowsContainerDragOver, onRowsContainerDrop, onRowDragEnd } = createRowDrag(ctx);
|
|
1977
2032
|
const { register: registerAlignedGrid, broadcastScroll: broadcastAlignedScroll, broadcastWidths: broadcastAlignedWidths } = createAlignedGrids(ctx);
|
package/dist/SvGrid.css
CHANGED
|
@@ -348,9 +348,13 @@
|
|
|
348
348
|
.sv-grid-table tr > :last-child.sv-grid-cell {
|
|
349
349
|
border-right: 0;
|
|
350
350
|
}
|
|
351
|
-
/* Row hover tint
|
|
352
|
-
|
|
353
|
-
|
|
351
|
+
/* Row hover tint - opt-in via `enableRowHover` (off by default; the class is
|
|
352
|
+
present unless the grid opts in). Uses `background-color` (not the shorthand)
|
|
353
|
+
so it never resets the fill-marquee `background-image`, and skips selected
|
|
354
|
+
cells so the selection stays visible under a hovered row. */
|
|
355
|
+
.sv-grid-table:not(.sv-grid-no-row-hover)
|
|
356
|
+
tbody tr:hover > .sv-grid-cell:not([data-selected-range="true"]) {
|
|
357
|
+
background-color: var(--sg-row-hover-bg, #eef2ff);
|
|
354
358
|
}
|
|
355
359
|
/* Only the active cell + the cell hosting the fill handle become a
|
|
356
360
|
positioning context (so the absolutely-positioned handle anchors
|
|
@@ -441,8 +445,10 @@
|
|
|
441
445
|
),
|
|
442
446
|
var(--sg-pinned-bg, color-mix(in oklab, var(--sg-header-bg, #f1f5f9) 92%, var(--sg-accent, #2563eb) 8%));
|
|
443
447
|
}
|
|
444
|
-
/* Hovered row keeps the pinned tint distinguishable from the hover.
|
|
445
|
-
|
|
448
|
+
/* Hovered row keeps the pinned tint distinguishable from the hover. Selected
|
|
449
|
+
cells are excluded so the selection stays visible under hover. */
|
|
450
|
+
.sv-grid-table:not(.sv-grid-no-row-hover)
|
|
451
|
+
.sv-grid-row:hover > .sv-grid-cell[data-pinned]:not([data-selected-range="true"]) {
|
|
446
452
|
background:
|
|
447
453
|
linear-gradient(
|
|
448
454
|
color-mix(in srgb, var(--sg-row-hover-bg, #eef2ff) 55%, transparent),
|
|
@@ -1062,18 +1068,52 @@ select.sv-grid-fr-editor {
|
|
|
1062
1068
|
transform-origin: bottom right;
|
|
1063
1069
|
}
|
|
1064
1070
|
|
|
1065
|
-
/*
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
`--sg-fill-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1071
|
+
/* Excel-style fill-handle drag marquee: ONE thin dashed rectangle around the
|
|
1072
|
+
whole target range (source + fill-into area), not a thick per-cell border or
|
|
1073
|
+
fill tint. Drawn as four background-image layers on the boundary cells - no
|
|
1074
|
+
`border`, so it paints on the compositor with zero reflow while dragging.
|
|
1075
|
+
`--sg-fill-marquee` overrides the colour (defaults to a theme-aware grey). */
|
|
1076
|
+
/* Step 1 (LOW specificity): default every edge layer to `none`, then light up
|
|
1077
|
+
the edges this cell sits on. These are custom properties, which the
|
|
1078
|
+
`background:` shorthands on selected / zebra cells do NOT reset - so they can
|
|
1079
|
+
safely live at low specificity and just compete with each other by source
|
|
1080
|
+
order (gradient rules come after the defaults, so they win). */
|
|
1081
|
+
.sv-grid-cell[data-fill-top="true"],
|
|
1082
|
+
.sv-grid-cell[data-fill-bottom="true"],
|
|
1083
|
+
.sv-grid-cell[data-fill-left="true"],
|
|
1084
|
+
.sv-grid-cell[data-fill-right="true"] {
|
|
1085
|
+
--sv-fill-top: none;
|
|
1086
|
+
--sv-fill-bottom: none;
|
|
1087
|
+
--sv-fill-left: none;
|
|
1088
|
+
--sv-fill-right: none;
|
|
1089
|
+
/* Match the selection colour (the grid accent) by default. */
|
|
1090
|
+
--sv-fill-color: var(--sg-fill-marquee, var(--sg-accent, #2563eb));
|
|
1091
|
+
}
|
|
1092
|
+
.sv-grid-cell[data-fill-top="true"] {
|
|
1093
|
+
--sv-fill-top: repeating-linear-gradient(to right, var(--sv-fill-color) 0 3px, transparent 3px 6px);
|
|
1094
|
+
}
|
|
1095
|
+
.sv-grid-cell[data-fill-bottom="true"] {
|
|
1096
|
+
--sv-fill-bottom: repeating-linear-gradient(to right, var(--sv-fill-color) 0 3px, transparent 3px 6px);
|
|
1097
|
+
}
|
|
1098
|
+
.sv-grid-cell[data-fill-left="true"] {
|
|
1099
|
+
--sv-fill-left: repeating-linear-gradient(to bottom, var(--sv-fill-color) 0 3px, transparent 3px 6px);
|
|
1100
|
+
}
|
|
1101
|
+
.sv-grid-cell[data-fill-right="true"] {
|
|
1102
|
+
--sv-fill-right: repeating-linear-gradient(to bottom, var(--sv-fill-color) 0 3px, transparent 3px 6px);
|
|
1103
|
+
}
|
|
1104
|
+
/* Step 2 (HIGH specificity, `.sv-grid-table` prefix): compose the resolved edge
|
|
1105
|
+
layers into `background-image` so it beats the `background:` shorthands on
|
|
1106
|
+
selected / zebra / hover cells (which would otherwise reset background-image).
|
|
1107
|
+
The cell keeps its background-COLOR; this only adds the dashed lines on top. */
|
|
1108
|
+
.sv-grid-table .sv-grid-cell[data-fill-top="true"],
|
|
1109
|
+
.sv-grid-table .sv-grid-cell[data-fill-bottom="true"],
|
|
1110
|
+
.sv-grid-table .sv-grid-cell[data-fill-left="true"],
|
|
1111
|
+
.sv-grid-table .sv-grid-cell[data-fill-right="true"] {
|
|
1112
|
+
background-image: var(--sv-fill-top), var(--sv-fill-bottom),
|
|
1113
|
+
var(--sv-fill-left), var(--sv-fill-right);
|
|
1114
|
+
background-repeat: no-repeat;
|
|
1115
|
+
background-size: 100% 2px, 100% 2px, 2px 100%, 2px 100%;
|
|
1116
|
+
background-position: top left, bottom left, top left, top right;
|
|
1077
1117
|
}
|
|
1078
1118
|
|
|
1079
1119
|
.sv-grid-cell-editing {
|
|
@@ -1338,6 +1378,17 @@ select.sv-grid-fr-editor {
|
|
|
1338
1378
|
.sv-grid-cell-cf {
|
|
1339
1379
|
position: relative;
|
|
1340
1380
|
}
|
|
1381
|
+
/* Declarative cell validation (column `validate` hook). Invalid cells get a
|
|
1382
|
+
* soft red wash and a red inset ring - Handsontable's `htInvalid` look. The
|
|
1383
|
+
* value keeps rendering as-is; the message (if any) shows as the tooltip. */
|
|
1384
|
+
.sv-grid-cell-invalid {
|
|
1385
|
+
background-color: var(--sg-invalid-bg, rgba(239, 68, 68, 0.14)) !important;
|
|
1386
|
+
box-shadow: inset 0 0 0 1px var(--sg-invalid-border, rgba(239, 68, 68, 0.45));
|
|
1387
|
+
color: var(--sg-invalid-fg, #b91c1c);
|
|
1388
|
+
}
|
|
1389
|
+
.sv-grid-cell-invalid.sv-grid-cell-active {
|
|
1390
|
+
box-shadow: inset 0 0 0 2px var(--sg-invalid-border, rgba(239, 68, 68, 0.7));
|
|
1391
|
+
}
|
|
1341
1392
|
.sv-grid-status-bar {
|
|
1342
1393
|
display: flex;
|
|
1343
1394
|
flex-wrap: wrap;
|
|
@@ -2103,32 +2154,34 @@ select.sv-grid-fr-editor {
|
|
|
2103
2154
|
color: var(--sg-fg, #0f172a);
|
|
2104
2155
|
font-size: 13px;
|
|
2105
2156
|
}
|
|
2157
|
+
/* Top-positioned pager: rounded + bordered on the top edge instead. */
|
|
2158
|
+
.sv-grid-pagination-top {
|
|
2159
|
+
border-top: 1px solid var(--sg-border, #e2e8f0);
|
|
2160
|
+
border-bottom: 0;
|
|
2161
|
+
border-radius: var(--sg-radius, 6px) var(--sg-radius, 6px) 0 0;
|
|
2162
|
+
}
|
|
2106
2163
|
.sv-grid-pagination-pagesize {
|
|
2107
2164
|
display: inline-flex;
|
|
2108
2165
|
align-items: center;
|
|
2109
2166
|
gap: 8px;
|
|
2110
2167
|
color: var(--sg-muted, #64748b);
|
|
2111
2168
|
}
|
|
2112
|
-
.sv-grid-pagination-pagesize
|
|
2169
|
+
.sv-grid-pagination-pagesize-dd {
|
|
2170
|
+
position: relative;
|
|
2113
2171
|
height: 28px;
|
|
2114
|
-
|
|
2172
|
+
min-width: 62px;
|
|
2115
2173
|
border: 1px solid var(--sg-input-border, #cbd5e1);
|
|
2116
2174
|
border-radius: 5px;
|
|
2117
2175
|
background: var(--sg-input-bg, #fff);
|
|
2118
|
-
|
|
2119
|
-
|
|
2176
|
+
}
|
|
2177
|
+
.sv-grid-pagination-pagesize-dd .sv-grid-dropdown,
|
|
2178
|
+
.sv-grid-pagination-pagesize-dd .sv-grid-dropdown-trigger {
|
|
2179
|
+
background: transparent;
|
|
2180
|
+
border-radius: 5px;
|
|
2120
2181
|
font-size: 13px;
|
|
2121
|
-
|
|
2122
|
-
appearance: none;
|
|
2123
|
-
-webkit-appearance: none;
|
|
2124
|
-
background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
|
|
2125
|
-
linear-gradient(135deg, currentColor 50%, transparent 50%);
|
|
2126
|
-
background-position: calc(100% - 12px) 12px, calc(100% - 8px) 12px;
|
|
2127
|
-
background-size: 4px 4px, 4px 4px;
|
|
2128
|
-
background-repeat: no-repeat;
|
|
2182
|
+
color: var(--sg-fg, #0f172a);
|
|
2129
2183
|
}
|
|
2130
|
-
.sv-grid-pagination-pagesize
|
|
2131
|
-
outline: none;
|
|
2184
|
+
.sv-grid-pagination-pagesize-dd:focus-within {
|
|
2132
2185
|
border-color: var(--sg-accent, #2563eb);
|
|
2133
2186
|
box-shadow: 0 0 0 2px color-mix(in srgb, var(--sg-accent, #2563eb) 30%, transparent);
|
|
2134
2187
|
}
|
package/dist/SvGrid.helpers.d.ts
CHANGED
|
@@ -12,6 +12,10 @@ export declare function getCellKey(rowId: string, columnId: string): string;
|
|
|
12
12
|
export declare function resolveClassList(value: string | ReadonlyArray<string> | Record<string, boolean> | undefined | null): string;
|
|
13
13
|
export declare function toDateInputValue(value: unknown): string;
|
|
14
14
|
export declare function toDateTimeLocalInputValue(value: unknown): string;
|
|
15
|
+
/** 'HH:MM[:SS]' string (or Date) to a Date carrying that time (today's date). */
|
|
16
|
+
export declare function timeStringToDate(value: unknown): Date | null;
|
|
17
|
+
/** A Date to an 'HH:MM' string (the `time` editor's stored form). */
|
|
18
|
+
export declare function dateToTimeString(d: Date | null): string | null;
|
|
15
19
|
export declare function getEditableInputValue(editorType: CellEditorType, value: unknown): string;
|
|
16
20
|
export declare function getEditorInputType(editorType: CellEditorType): "number" | "date" | "text" | "time" | "password" | "color" | "datetime-local";
|
|
17
21
|
/** Resolve a stored value to an array form (for list/chips multi-select). */
|
package/dist/SvGrid.helpers.js
CHANGED
|
@@ -57,25 +57,52 @@ export function toDateTimeLocalInputValue(value) {
|
|
|
57
57
|
return "";
|
|
58
58
|
return parsed.toISOString().slice(0, 16);
|
|
59
59
|
}
|
|
60
|
+
/** Strip the `-native` opt-out suffix so the native-input helpers treat
|
|
61
|
+
* `date-native` exactly like `date` (the rich pickers own the bare types). */
|
|
62
|
+
function baseEditor(editorType) {
|
|
63
|
+
return String(editorType).replace(/-native$/, "");
|
|
64
|
+
}
|
|
65
|
+
/** 'HH:MM[:SS]' string (or Date) to a Date carrying that time (today's date). */
|
|
66
|
+
export function timeStringToDate(value) {
|
|
67
|
+
if (value instanceof Date)
|
|
68
|
+
return value;
|
|
69
|
+
if (typeof value === "string") {
|
|
70
|
+
const m = value.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?/);
|
|
71
|
+
if (m) {
|
|
72
|
+
const d = new Date();
|
|
73
|
+
d.setHours(Math.min(23, +m[1]), Math.min(59, +m[2]), m[3] ? +m[3] : 0, 0);
|
|
74
|
+
return d;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
/** A Date to an 'HH:MM' string (the `time` editor's stored form). */
|
|
80
|
+
export function dateToTimeString(d) {
|
|
81
|
+
if (!d)
|
|
82
|
+
return null;
|
|
83
|
+
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
|
|
84
|
+
}
|
|
60
85
|
export function getEditableInputValue(editorType, value) {
|
|
61
|
-
|
|
86
|
+
const base = baseEditor(editorType);
|
|
87
|
+
if (base === "date")
|
|
62
88
|
return toDateInputValue(value);
|
|
63
|
-
if (
|
|
89
|
+
if (base === "datetime")
|
|
64
90
|
return toDateTimeLocalInputValue(value);
|
|
65
91
|
return String(value ?? "");
|
|
66
92
|
}
|
|
67
93
|
export function getEditorInputType(editorType) {
|
|
68
|
-
|
|
94
|
+
const base = baseEditor(editorType);
|
|
95
|
+
if (base === "number")
|
|
69
96
|
return "number";
|
|
70
|
-
if (
|
|
97
|
+
if (base === "date")
|
|
71
98
|
return "date";
|
|
72
|
-
if (
|
|
99
|
+
if (base === "datetime")
|
|
73
100
|
return "datetime-local";
|
|
74
|
-
if (
|
|
101
|
+
if (base === "time")
|
|
75
102
|
return "time";
|
|
76
|
-
if (
|
|
103
|
+
if (base === "password")
|
|
77
104
|
return "password";
|
|
78
|
-
if (
|
|
105
|
+
if (base === "color")
|
|
79
106
|
return "color";
|
|
80
107
|
return "text";
|
|
81
108
|
}
|
|
@@ -111,13 +138,14 @@ export function colorfulChipStyle(color) {
|
|
|
111
138
|
`color: color-mix(in srgb, ${color} 80%, var(--sg-fg, #0f172a));`);
|
|
112
139
|
}
|
|
113
140
|
export function getEditorClass(editorType) {
|
|
114
|
-
|
|
141
|
+
const base = baseEditor(editorType);
|
|
142
|
+
if (base === "number")
|
|
115
143
|
return "sv-grid-cell-editor sv-grid-cell-editor-number";
|
|
116
|
-
if (
|
|
144
|
+
if (base === "date")
|
|
117
145
|
return "sv-grid-cell-editor sv-grid-cell-editor-date";
|
|
118
|
-
if (
|
|
146
|
+
if (base === "datetime")
|
|
119
147
|
return "sv-grid-cell-editor sv-grid-cell-editor-datetime";
|
|
120
|
-
if (
|
|
148
|
+
if (base === "color")
|
|
121
149
|
return "sv-grid-cell-editor sv-grid-cell-editor-color";
|
|
122
150
|
return "sv-grid-cell-editor";
|
|
123
151
|
}
|