@svgrid/grid 1.2.21 → 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,143 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* SvDropDownList - a single-select dropdown (trigger button + portalled list,
|
|
4
|
+
* no typing). Parity: Smart `smart-drop-down-list`. Controlled via `value` +
|
|
5
|
+
* `onChange`. Popover escapes scroll clipping via popover.ts.
|
|
6
|
+
*/
|
|
7
|
+
import { anchoredRect, portalToBody, type AnchoredRect } from './popover'
|
|
8
|
+
import type { ListOption } from './list-option'
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
options: ReadonlyArray<ListOption>
|
|
12
|
+
value?: string | number | null
|
|
13
|
+
onChange?: (value: string | number) => void
|
|
14
|
+
placeholder?: string
|
|
15
|
+
disabled?: boolean
|
|
16
|
+
name?: string
|
|
17
|
+
size?: 'sm' | 'md' | 'lg'
|
|
18
|
+
ariaLabel?: string
|
|
19
|
+
autoOpen?: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let { options, value = null, onChange, placeholder = 'Select…', disabled = false, name, size = 'md', ariaLabel, autoOpen = false }: Props = $props()
|
|
23
|
+
|
|
24
|
+
let open = $state(false)
|
|
25
|
+
let active = $state(-1)
|
|
26
|
+
let triggerEl = $state<HTMLButtonElement | null>(null)
|
|
27
|
+
let panelEl = $state<HTMLDivElement | null>(null)
|
|
28
|
+
let rect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false })
|
|
29
|
+
|
|
30
|
+
const selected = $derived(options.find((o) => o.value === value) ?? null)
|
|
31
|
+
const enabledIdx = $derived(options.map((o, i) => (o.disabled ? -1 : i)).filter((i) => i >= 0))
|
|
32
|
+
|
|
33
|
+
function updatePos() {
|
|
34
|
+
if (!triggerEl) return
|
|
35
|
+
rect = anchoredRect(triggerEl.getBoundingClientRect(), { estimatedHeight: Math.min(options.length, 8) * 34 + 8 })
|
|
36
|
+
}
|
|
37
|
+
function openPanel() {
|
|
38
|
+
if (disabled || open) return
|
|
39
|
+
open = true
|
|
40
|
+
active = Math.max(0, options.findIndex((o) => o.value === value))
|
|
41
|
+
updatePos()
|
|
42
|
+
}
|
|
43
|
+
function close() { open = false }
|
|
44
|
+
function toggle() { open ? close() : openPanel() }
|
|
45
|
+
function pick(i: number) {
|
|
46
|
+
const o = options[i]
|
|
47
|
+
if (!o || o.disabled) return
|
|
48
|
+
onChange?.(o.value); close(); triggerEl?.focus()
|
|
49
|
+
}
|
|
50
|
+
function move(d: number) {
|
|
51
|
+
const pos = enabledIdx.indexOf(active)
|
|
52
|
+
active = enabledIdx[(pos + d + enabledIdx.length) % enabledIdx.length] ?? active
|
|
53
|
+
queueMicrotask(() => panelEl?.querySelector<HTMLElement>(`[data-idx="${active}"]`)?.scrollIntoView({ block: 'nearest' }))
|
|
54
|
+
}
|
|
55
|
+
function onKeydown(e: KeyboardEvent) {
|
|
56
|
+
if (disabled) return
|
|
57
|
+
if (!open && (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); openPanel(); return }
|
|
58
|
+
if (!open) return
|
|
59
|
+
if (e.key === 'ArrowDown') { e.preventDefault(); move(1) }
|
|
60
|
+
else if (e.key === 'ArrowUp') { e.preventDefault(); move(-1) }
|
|
61
|
+
else if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); pick(active) }
|
|
62
|
+
else if (e.key === 'Escape') { e.preventDefault(); close(); triggerEl?.focus() }
|
|
63
|
+
else if (e.key === 'Home') { active = enabledIdx[0] ?? 0 }
|
|
64
|
+
else if (e.key === 'End') { active = enabledIdx.at(-1) ?? 0 }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
$effect(() => {
|
|
68
|
+
if (!open) return
|
|
69
|
+
const rp = () => updatePos()
|
|
70
|
+
window.addEventListener('scroll', rp, true); window.addEventListener('resize', rp)
|
|
71
|
+
const od = (e: PointerEvent) => { const t = e.target as Node | null; if (t && (triggerEl?.contains(t) || panelEl?.contains(t))) return; close() }
|
|
72
|
+
document.addEventListener('pointerdown', od, true)
|
|
73
|
+
return () => { window.removeEventListener('scroll', rp, true); window.removeEventListener('resize', rp); document.removeEventListener('pointerdown', od, true) }
|
|
74
|
+
})
|
|
75
|
+
function focusOpen(node: HTMLButtonElement) { if (autoOpen) { node.focus(); openPanel() } }
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<button
|
|
79
|
+
bind:this={triggerEl}
|
|
80
|
+
type="button"
|
|
81
|
+
class="sv-ddl sv-ddl--{size}"
|
|
82
|
+
class:is-open={open}
|
|
83
|
+
class:is-disabled={disabled}
|
|
84
|
+
aria-haspopup="listbox"
|
|
85
|
+
aria-expanded={open}
|
|
86
|
+
aria-label={ariaLabel}
|
|
87
|
+
{disabled}
|
|
88
|
+
onclick={toggle}
|
|
89
|
+
onkeydown={onKeydown}
|
|
90
|
+
use:focusOpen
|
|
91
|
+
>
|
|
92
|
+
<span class="sv-ddl__value" class:is-placeholder={!selected}>{selected?.label ?? placeholder}</span>
|
|
93
|
+
<svg class="sv-ddl__chev" viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
|
|
94
|
+
</button>
|
|
95
|
+
|
|
96
|
+
{#if open}
|
|
97
|
+
<div bind:this={panelEl} class="sv-ddl__panel" use:portalToBody style:position="fixed" style:top={`${rect.top}px`} style:left={`${rect.left}px`} style:min-width={`${rect.width}px`} role="listbox" tabindex="-1">
|
|
98
|
+
{#each options as opt, i (opt.value)}
|
|
99
|
+
<!-- svelte-ignore a11y_click_events_have_key_events a11y_interactive_supports_focus -->
|
|
100
|
+
<div
|
|
101
|
+
class="sv-ddl__opt"
|
|
102
|
+
class:is-active={i === active}
|
|
103
|
+
class:is-selected={opt.value === value}
|
|
104
|
+
class:is-disabled={opt.disabled}
|
|
105
|
+
role="option"
|
|
106
|
+
tabindex="-1"
|
|
107
|
+
aria-selected={opt.value === value}
|
|
108
|
+
data-idx={i}
|
|
109
|
+
onclick={() => pick(i)}
|
|
110
|
+
onpointermove={() => { if (!opt.disabled) active = i }}
|
|
111
|
+
>{opt.label}</div>
|
|
112
|
+
{/each}
|
|
113
|
+
</div>
|
|
114
|
+
{/if}
|
|
115
|
+
{#if name}<input type="hidden" {name} value={value ?? ''} />{/if}
|
|
116
|
+
|
|
117
|
+
<style>
|
|
118
|
+
.sv-ddl {
|
|
119
|
+
--_accent: var(--sg-accent, #2563eb);
|
|
120
|
+
display: inline-flex; align-items: center; justify-content: space-between; gap: 8px; width: 200px;
|
|
121
|
+
background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a); font: inherit; text-align: left;
|
|
122
|
+
border: 1px solid var(--sg-input-border, var(--sg-border, #cbd5e1)); border-radius: var(--sg-radius, 8px);
|
|
123
|
+
padding: 0 10px; cursor: pointer;
|
|
124
|
+
}
|
|
125
|
+
.sv-ddl--sm { height: 28px; font-size: 12px; }
|
|
126
|
+
.sv-ddl--md { height: 34px; font-size: 13px; }
|
|
127
|
+
.sv-ddl--lg { height: 40px; font-size: 15px; }
|
|
128
|
+
.sv-ddl.is-open, .sv-ddl:focus-visible { border-color: var(--_accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--_accent) 22%, transparent); outline: none; }
|
|
129
|
+
.sv-ddl.is-disabled { opacity: 0.6; cursor: not-allowed; }
|
|
130
|
+
.sv-ddl__value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
131
|
+
.sv-ddl__value.is-placeholder { color: var(--sg-muted, #94a3b8); }
|
|
132
|
+
.sv-ddl__chev { color: var(--sg-muted, #64748b); flex: none; }
|
|
133
|
+
|
|
134
|
+
:global(.sv-ddl__panel) {
|
|
135
|
+
z-index: 2147483647; max-height: 288px; overflow-y: auto; padding: 4px;
|
|
136
|
+
background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a);
|
|
137
|
+
border: 1px solid var(--sg-border, #e2e8f0); border-radius: 10px; box-shadow: 0 16px 48px -12px rgba(15,23,42,0.35);
|
|
138
|
+
}
|
|
139
|
+
:global(.sv-ddl__opt) { padding: 7px 10px; border-radius: 6px; cursor: pointer; font-size: 13px; }
|
|
140
|
+
:global(.sv-ddl__opt.is-active) { background: var(--sg-row-hover-bg, #f1f5f9); }
|
|
141
|
+
:global(.sv-ddl__opt.is-selected) { color: var(--sg-accent, #2563eb); font-weight: 600; }
|
|
142
|
+
:global(.sv-ddl__opt.is-disabled) { opacity: 0.4; cursor: not-allowed; }
|
|
143
|
+
</style>
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export type FormFieldType =
|
|
3
|
+
| 'text' | 'email' | 'tel' | 'textarea' | 'number' | 'password'
|
|
4
|
+
| 'select' | 'checkbox' | 'switch' | 'date' | 'color' | 'rating'
|
|
5
|
+
|
|
6
|
+
export type FormField = {
|
|
7
|
+
name: string
|
|
8
|
+
label: string
|
|
9
|
+
type?: FormFieldType
|
|
10
|
+
required?: boolean
|
|
11
|
+
placeholder?: string
|
|
12
|
+
options?: Array<{ value: string | number; label: string }>
|
|
13
|
+
/** Return an error message, or null/undefined when valid. */
|
|
14
|
+
validate?: (value: any, values: Record<string, any>) => string | null | undefined
|
|
15
|
+
/** Span full width in the grid. */
|
|
16
|
+
full?: boolean
|
|
17
|
+
}
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<script lang="ts">
|
|
21
|
+
/**
|
|
22
|
+
* SvForm - a schema-driven form that renders the SvGrid UI-kit controls with
|
|
23
|
+
* labels, required + custom validation, and a submit handler. Parity: Smart
|
|
24
|
+
* `smart-form`. Emits `onSubmit(values)` only when valid; `onChange` on edits.
|
|
25
|
+
*/
|
|
26
|
+
import SvNumberInput from './SvNumberInput.svelte'
|
|
27
|
+
import SvPasswordInput from './SvPasswordInput.svelte'
|
|
28
|
+
import SvDropDownList from './SvDropDownList.svelte'
|
|
29
|
+
import SvCheckBox from './SvCheckBox.svelte'
|
|
30
|
+
import SvSwitchButton from './SvSwitchButton.svelte'
|
|
31
|
+
import SvDateTimePicker from './SvDateTimePicker.svelte'
|
|
32
|
+
import SvColorInput from './SvColorInput.svelte'
|
|
33
|
+
import SvRating from './SvRating.svelte'
|
|
34
|
+
import SvButton from './SvButton.svelte'
|
|
35
|
+
|
|
36
|
+
type Props = {
|
|
37
|
+
fields: ReadonlyArray<FormField>
|
|
38
|
+
initial?: Record<string, any>
|
|
39
|
+
onSubmit?: (values: Record<string, any>) => void
|
|
40
|
+
onChange?: (values: Record<string, any>) => void
|
|
41
|
+
submitLabel?: string
|
|
42
|
+
/** Columns in the responsive field grid. */
|
|
43
|
+
columns?: number
|
|
44
|
+
disabled?: boolean
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let { fields, initial = {}, onSubmit, onChange, submitLabel = 'Submit', columns = 1, disabled = false }: Props = $props()
|
|
48
|
+
|
|
49
|
+
let values = $state<Record<string, any>>({})
|
|
50
|
+
let errors = $state<Record<string, string>>({})
|
|
51
|
+
let touched = $state<Record<string, boolean>>({})
|
|
52
|
+
let seededForm = false
|
|
53
|
+
$effect(() => { if (!seededForm) { seededForm = true; values = { ...initial } } })
|
|
54
|
+
|
|
55
|
+
function setValue(name: string, v: any) {
|
|
56
|
+
values = { ...values, [name]: v }
|
|
57
|
+
onChange?.(values)
|
|
58
|
+
if (touched[name]) validateField(name)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function validateField(name: string): boolean {
|
|
62
|
+
const field = fields.find((f) => f.name === name)
|
|
63
|
+
if (!field) return true
|
|
64
|
+
const v = values[name]
|
|
65
|
+
let err: string | null | undefined = null
|
|
66
|
+
if (field.required && (v == null || v === '' || (Array.isArray(v) && !v.length))) err = `${field.label} is required`
|
|
67
|
+
else if (field.validate) err = field.validate(v, values)
|
|
68
|
+
const next = { ...errors }
|
|
69
|
+
if (err) next[name] = err
|
|
70
|
+
else delete next[name]
|
|
71
|
+
errors = next
|
|
72
|
+
return !err
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function submit(e: Event) {
|
|
76
|
+
e.preventDefault()
|
|
77
|
+
let ok = true
|
|
78
|
+
const t: Record<string, boolean> = {}
|
|
79
|
+
for (const f of fields) { t[f.name] = true; if (!validateField(f.name)) ok = false }
|
|
80
|
+
touched = t
|
|
81
|
+
if (ok) onSubmit?.({ ...values })
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const dateVal = (v: any): Date | null => (v instanceof Date ? v : v ? new Date(v) : null)
|
|
85
|
+
</script>
|
|
86
|
+
|
|
87
|
+
<form class="sv-form" style:--cols={columns} onsubmit={submit} novalidate>
|
|
88
|
+
<div class="sv-form__grid">
|
|
89
|
+
{#each fields as field (field.name)}
|
|
90
|
+
{@const type = field.type ?? 'text'}
|
|
91
|
+
<div class="sv-form__field" class:is-full={field.full || columns === 1} class:has-error={errors[field.name]}>
|
|
92
|
+
{#if type !== 'checkbox' && type !== 'switch'}
|
|
93
|
+
<label class="sv-form__label" for={`f-${field.name}`}>
|
|
94
|
+
{field.label}{#if field.required}<span class="sv-form__req" aria-hidden="true"> *</span>{/if}
|
|
95
|
+
</label>
|
|
96
|
+
{/if}
|
|
97
|
+
|
|
98
|
+
{#if type === 'textarea'}
|
|
99
|
+
<textarea id={`f-${field.name}`} class="sv-form__control sv-form__textarea" rows="3" {disabled} placeholder={field.placeholder} value={values[field.name] ?? ''} oninput={(e) => setValue(field.name, e.currentTarget.value)} onblur={() => { touched[field.name] = true; validateField(field.name) }}></textarea>
|
|
100
|
+
{:else if type === 'number'}
|
|
101
|
+
<SvNumberInput value={values[field.name] ?? null} {disabled} onChange={(v) => setValue(field.name, v)} />
|
|
102
|
+
{:else if type === 'password'}
|
|
103
|
+
<SvPasswordInput value={values[field.name] ?? ''} {disabled} onChange={(v) => setValue(field.name, v)} />
|
|
104
|
+
{:else if type === 'select'}
|
|
105
|
+
<SvDropDownList options={field.options ?? []} value={values[field.name] ?? null} {disabled} placeholder={field.placeholder} onChange={(v) => setValue(field.name, v)} />
|
|
106
|
+
{:else if type === 'checkbox'}
|
|
107
|
+
<SvCheckBox checked={!!values[field.name]} {disabled} onChange={(v) => setValue(field.name, v)}>{field.label}</SvCheckBox>
|
|
108
|
+
{:else if type === 'switch'}
|
|
109
|
+
<div class="sv-form__inline"><SvSwitchButton checked={!!values[field.name]} {disabled} onChange={(v) => setValue(field.name, v)} ariaLabel={field.label} /><span>{field.label}</span></div>
|
|
110
|
+
{:else if type === 'date'}
|
|
111
|
+
<SvDateTimePicker value={dateVal(values[field.name])} dropDownDisplayMode="calendar" formatString="yyyy-MM-dd" {disabled} onChange={(v) => setValue(field.name, v)} />
|
|
112
|
+
{:else if type === 'color'}
|
|
113
|
+
<SvColorInput value={values[field.name] ?? '#3b82f6'} {disabled} onChange={(v) => setValue(field.name, v)} />
|
|
114
|
+
{:else if type === 'rating'}
|
|
115
|
+
<SvRating value={values[field.name] ?? 0} {disabled} onChange={(v) => setValue(field.name, v)} />
|
|
116
|
+
{:else}
|
|
117
|
+
<input id={`f-${field.name}`} class="sv-form__control" type={type} {disabled} placeholder={field.placeholder} value={values[field.name] ?? ''} oninput={(e) => setValue(field.name, e.currentTarget.value)} onblur={() => { touched[field.name] = true; validateField(field.name) }} />
|
|
118
|
+
{/if}
|
|
119
|
+
|
|
120
|
+
{#if errors[field.name]}<span class="sv-form__error" role="alert">{errors[field.name]}</span>{/if}
|
|
121
|
+
</div>
|
|
122
|
+
{/each}
|
|
123
|
+
</div>
|
|
124
|
+
<div class="sv-form__actions">
|
|
125
|
+
<SvButton type="submit" {disabled}>{submitLabel}</SvButton>
|
|
126
|
+
</div>
|
|
127
|
+
</form>
|
|
128
|
+
|
|
129
|
+
<style>
|
|
130
|
+
.sv-form { --_accent: var(--sg-accent, #2563eb); width: 100%; }
|
|
131
|
+
.sv-form__grid { display: grid; grid-template-columns: repeat(var(--cols, 1), minmax(0, 1fr)); gap: 14px 18px; }
|
|
132
|
+
.sv-form__field { display: flex; flex-direction: column; gap: 5px; }
|
|
133
|
+
.sv-form__field.is-full { grid-column: 1 / -1; }
|
|
134
|
+
.sv-form__label { font-size: 12.5px; font-weight: 600; color: var(--sg-fg, #0f172a); }
|
|
135
|
+
.sv-form__req { color: var(--sg-danger, #dc2626); }
|
|
136
|
+
.sv-form__control {
|
|
137
|
+
width: 100%; box-sizing: border-box; height: 34px; padding: 0 10px; font: inherit; font-size: 13px;
|
|
138
|
+
background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a);
|
|
139
|
+
border: 1px solid var(--sg-input-border, var(--sg-border, #cbd5e1)); border-radius: var(--sg-radius, 8px); outline: none;
|
|
140
|
+
}
|
|
141
|
+
.sv-form__textarea { height: auto; padding: 8px 10px; resize: vertical; }
|
|
142
|
+
.sv-form__control:focus { border-color: var(--_accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--_accent) 22%, transparent); }
|
|
143
|
+
.sv-form__field.has-error .sv-form__control { border-color: var(--sg-danger, #dc2626); }
|
|
144
|
+
.sv-form__inline { display: flex; align-items: center; gap: 8px; font-size: 13px; }
|
|
145
|
+
.sv-form__error { font-size: 11.5px; color: var(--sg-danger, #dc2626); }
|
|
146
|
+
.sv-form__actions { margin-top: 16px; }
|
|
147
|
+
</style>
|
|
@@ -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>
|
|
@@ -409,6 +409,10 @@ export function createSvGridController<
|
|
|
409
409
|
});
|
|
410
410
|
let columnVirtualizerVersion = $state(0);
|
|
411
411
|
let gridStateVersion = $state(0);
|
|
412
|
+
// Bumps only when a row-model-affecting slice changes (see the store
|
|
413
|
+
// subscription below) - the row-model derivation depends on THIS, not the
|
|
414
|
+
// catch-all gridStateVersion, so navigation doesn't rebuild 1M rows.
|
|
415
|
+
let dataStateVersion = $state(0);
|
|
412
416
|
const selectionColumnWidth = 44;
|
|
413
417
|
const rowNumberColumnWidth = $derived(props.rowNumberWidth ?? 56);
|
|
414
418
|
const showRowNumbersEffective = $derived(props.showRowNumbers ?? false);
|
|
@@ -582,6 +586,10 @@ export function createSvGridController<
|
|
|
582
586
|
// the wrapper records filter state but does not actually filter rows.
|
|
583
587
|
// svelte-ignore state_referenced_locally
|
|
584
588
|
const externalFilterEnabled = props.externalFilter === true;
|
|
589
|
+
// Server-side pagination: the footer reads rowCount/pageIndex from props and
|
|
590
|
+
// emits onPaginationChange instead of slicing locally. Controlled by the
|
|
591
|
+
// consumer. Reactive (unlike sort/filter) so pageIndex/rowCount can change.
|
|
592
|
+
const externalPaginationEnabled = $derived(props.externalPagination === true);
|
|
585
593
|
const passthroughSortedRowModel = ({ rows }: { rows: Array<Row<TData>> }) =>
|
|
586
594
|
rows;
|
|
587
595
|
|
|
@@ -629,9 +637,38 @@ export function createSvGridController<
|
|
|
629
637
|
},
|
|
630
638
|
});
|
|
631
639
|
|
|
640
|
+
// `gridStateVersion` bumps on EVERY store change (incl. moving the active
|
|
641
|
+
// cell). `dataStateVersion` bumps ONLY when a slice that actually changes the
|
|
642
|
+
// row model changes (sort / filter / pagination / grouping / expansion /
|
|
643
|
+
// selection) - so the O(rows) row-model derivation does NOT re-run on plain
|
|
644
|
+
// keyboard navigation. Without this, arrow-keying a 1,000,000-row grid re-ran
|
|
645
|
+
// the entire core->filter->sort->group pipeline on every keystroke.
|
|
646
|
+
let prevDataSlices:
|
|
647
|
+
| { sorting: unknown; columnFilters: unknown; pagination: unknown; grouping: unknown; expanded: unknown; rowSelection: unknown }
|
|
648
|
+
| null = null;
|
|
632
649
|
$effect(() => {
|
|
633
650
|
const unsubscribe = grid.store.subscribe(() => {
|
|
634
651
|
gridStateVersion += 1;
|
|
652
|
+
const s = grid.getState();
|
|
653
|
+
if (
|
|
654
|
+
!prevDataSlices ||
|
|
655
|
+
prevDataSlices.sorting !== s.sorting ||
|
|
656
|
+
prevDataSlices.columnFilters !== s.columnFilters ||
|
|
657
|
+
prevDataSlices.pagination !== s.pagination ||
|
|
658
|
+
prevDataSlices.grouping !== s.grouping ||
|
|
659
|
+
prevDataSlices.expanded !== s.expanded ||
|
|
660
|
+
prevDataSlices.rowSelection !== s.rowSelection
|
|
661
|
+
) {
|
|
662
|
+
dataStateVersion += 1;
|
|
663
|
+
prevDataSlices = {
|
|
664
|
+
sorting: s.sorting,
|
|
665
|
+
columnFilters: s.columnFilters,
|
|
666
|
+
pagination: s.pagination,
|
|
667
|
+
grouping: s.grouping,
|
|
668
|
+
expanded: s.expanded,
|
|
669
|
+
rowSelection: s.rowSelection,
|
|
670
|
+
};
|
|
671
|
+
}
|
|
635
672
|
});
|
|
636
673
|
return unsubscribe;
|
|
637
674
|
});
|
|
@@ -977,7 +1014,11 @@ export function createSvGridController<
|
|
|
977
1014
|
* filters reduce the dataset.
|
|
978
1015
|
*/
|
|
979
1016
|
const allRowsBeforePagination = $derived.by(() => {
|
|
980
|
-
|
|
1017
|
+
// Depend on dataStateVersion (row-model-affecting store changes) NOT
|
|
1018
|
+
// gridStateVersion - so moving the active cell / selection does not force
|
|
1019
|
+
// this O(rows) pipeline to re-run. Filter-input state (globalFilter etc.)
|
|
1020
|
+
// and internalData are read below and tracked as their own dependencies.
|
|
1021
|
+
dataStateVersion;
|
|
981
1022
|
// Touch internalData + internalColumns so the row model re-derives when
|
|
982
1023
|
// the consumer replaces the data array (e.g. via a "Reset" button).
|
|
983
1024
|
void internalData;
|
|
@@ -1081,7 +1122,8 @@ export function createSvGridController<
|
|
|
1081
1122
|
*/
|
|
1082
1123
|
const allRows = $derived.by(() => {
|
|
1083
1124
|
const rows = allRowsBeforePagination;
|
|
1084
|
-
|
|
1125
|
+
// External pagination: `data` already IS the current page - never slice.
|
|
1126
|
+
if (!paginationEnabled || externalPaginationEnabled) return rows;
|
|
1085
1127
|
const { pageIndex, pageSize } = paginationState;
|
|
1086
1128
|
const start = pageIndex * pageSize;
|
|
1087
1129
|
return rows.slice(start, start + pageSize);
|
|
@@ -1089,14 +1131,27 @@ export function createSvGridController<
|
|
|
1089
1131
|
|
|
1090
1132
|
// When a filter reduces the dataset, the stored pageIndex can point beyond
|
|
1091
1133
|
// the last valid page. Reset to page 0 so the grid never shows a blank body.
|
|
1134
|
+
// Skipped for external pagination, where the consumer owns pageIndex.
|
|
1092
1135
|
$effect(() => {
|
|
1093
|
-
if (!paginationEnabled) return;
|
|
1136
|
+
if (!paginationEnabled || externalPaginationEnabled) return;
|
|
1094
1137
|
const { pageIndex, pageSize } = paginationState;
|
|
1095
1138
|
const pageCount = Math.ceil(allRowsBeforePagination.length / pageSize);
|
|
1096
1139
|
if (pageCount > 0 && pageIndex >= pageCount) {
|
|
1097
1140
|
grid.setPagination({ pageIndex: 0, pageSize });
|
|
1098
1141
|
}
|
|
1099
1142
|
});
|
|
1143
|
+
|
|
1144
|
+
// Footer-facing pagination values. In external mode they come from the
|
|
1145
|
+
// consumer-controlled props; otherwise from the local row model + state.
|
|
1146
|
+
const paginationTotalRows = $derived(
|
|
1147
|
+
externalPaginationEnabled ? (props.rowCount ?? 0) : allRowsBeforePagination.length,
|
|
1148
|
+
);
|
|
1149
|
+
const paginationPageIndex = $derived(
|
|
1150
|
+
externalPaginationEnabled ? (props.pageIndex ?? 0) : paginationState.pageIndex,
|
|
1151
|
+
);
|
|
1152
|
+
const paginationPageSize = $derived(
|
|
1153
|
+
externalPaginationEnabled ? (props.pageSize ?? 10) : paginationState.pageSize,
|
|
1154
|
+
);
|
|
1100
1155
|
const rowSelectionState = $derived.by(() => {
|
|
1101
1156
|
gridStateVersion;
|
|
1102
1157
|
return grid.getState().rowSelection ?? {};
|
|
@@ -1501,9 +1556,14 @@ export function createSvGridController<
|
|
|
1501
1556
|
|
|
1502
1557
|
let summaryByColumn = $state<Record<string, string>>({});
|
|
1503
1558
|
$effect(() => {
|
|
1504
|
-
// Re-aggregate whenever the
|
|
1505
|
-
//
|
|
1506
|
-
gridStateVersion
|
|
1559
|
+
// Re-aggregate whenever the data / columns / edits change. We depend on
|
|
1560
|
+
// `allRows` / `allColumns` / `editedCellValues` DIRECTLY - not the
|
|
1561
|
+
// catch-all `gridStateVersion` - because that version bumps on EVERY store
|
|
1562
|
+
// change, including moving the active cell or selection. `allRows` stays
|
|
1563
|
+
// referentially stable across those (sort/filter/paginate produce a new
|
|
1564
|
+
// rows array; navigation does not), so this now skips the
|
|
1565
|
+
// O(rows x cols) aggregation on plain keyboard navigation - which was
|
|
1566
|
+
// making arrow-key movement crawl on huge grids (e.g. 1,000,000 rows).
|
|
1507
1567
|
void editedCellValues;
|
|
1508
1568
|
const rows = allRows;
|
|
1509
1569
|
const columns = allColumns;
|
|
@@ -2209,6 +2269,10 @@ export function createSvGridController<
|
|
|
2209
2269
|
get sortDirectionByColumn() { return sortDirectionByColumn; },
|
|
2210
2270
|
get groupingColumns() { return groupingColumns; },
|
|
2211
2271
|
get paginationState() { return paginationState; },
|
|
2272
|
+
get externalPaginationEnabled() { return externalPaginationEnabled; },
|
|
2273
|
+
get paginationTotalRows() { return paginationTotalRows; },
|
|
2274
|
+
get paginationPageIndex() { return paginationPageIndex; },
|
|
2275
|
+
get paginationPageSize() { return paginationPageSize; },
|
|
2212
2276
|
get getRowColumnValue() { return getRowColumnValue; },
|
|
2213
2277
|
get allRowsBeforePagination() { return allRowsBeforePagination; },
|
|
2214
2278
|
get allRows() { return allRows; },
|
|
@@ -2274,6 +2338,7 @@ export function createSvGridController<
|
|
|
2274
2338
|
get computeRowClass() { return computeRowClass; },
|
|
2275
2339
|
get computeCellClass() { return computeCellClass; },
|
|
2276
2340
|
get computeCellTooltip() { return computeCellTooltip; },
|
|
2341
|
+
get computeCellValidity() { return computeCellValidity; },
|
|
2277
2342
|
get computeCellNote() { return computeCellNote; },
|
|
2278
2343
|
get getCellDisplayValue() { return getCellDisplayValue; },
|
|
2279
2344
|
get getColumnAlign() { return getColumnAlign; },
|
|
@@ -2309,6 +2374,7 @@ export function createSvGridController<
|
|
|
2309
2374
|
get getSelectionRects() { return getSelectionRects; },
|
|
2310
2375
|
get fillHandleCell() { return fillHandleCell; },
|
|
2311
2376
|
get isInFillPreview() { return isInFillPreview; },
|
|
2377
|
+
get fillMarqueeEdges() { return fillMarqueeEdges; },
|
|
2312
2378
|
get findColumnById() { return findColumnById; },
|
|
2313
2379
|
get readCellRaw() { return readCellRaw; },
|
|
2314
2380
|
get writeCellRaw() { return writeCellRaw; },
|
|
@@ -2402,9 +2468,9 @@ export function createSvGridController<
|
|
|
2402
2468
|
const { onGridKeyDown, onWindowKeydown, onHeaderSortClick } = createKeyboard<TFeatures, TData>(ctx);
|
|
2403
2469
|
const { computeSummaries, hasRenderedColumn } = createSummaries<TFeatures, TData>(ctx);
|
|
2404
2470
|
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<TFeatures, TData>(ctx);
|
|
2405
|
-
const { cellConditionalFormat, computeRowClass, computeCellClass, computeCellTooltip, computeCellNote, getColumnEditorOptions, formatListCellValue, formatCellValue, formatPinnedValue, computePinnedCellClass } = createCellRender<TFeatures, TData>(ctx);
|
|
2471
|
+
const { cellConditionalFormat, computeRowClass, computeCellClass, computeCellTooltip, computeCellValidity, computeCellNote, getColumnEditorOptions, formatListCellValue, formatCellValue, formatPinnedValue, computePinnedCellClass } = createCellRender<TFeatures, TData>(ctx);
|
|
2406
2472
|
const { isCellEditable, isCellEditableAt, getRowColumnValue, getCellDisplayValue, startEditingWithChar, startEditing, stopEditing, startFullRowEdit, setFullRowDraft, commitFullRowEdit, cancelFullRowEdit, saveEditingCell, applyHistoryStep, updateEditingCellValue, onEditorKeyDown, focusOnMount, onCellDoubleClick, pasteFromClipboard, onGridPaste } = createEditing<TFeatures, TData>(ctx);
|
|
2407
|
-
const { isRowSelected, toggleRowSelectionById, toggleSelectAllRows, setActiveCell, scrollActiveCellIntoView, setSelection, extendSelection, isCellInSelectedRange, getCellRangeEdges, getSelectionRects, isInFillPreview, findColumnById, onCellPointerDown, onCellPointerEnter, endDragSelection, onWindowPointerMove, onCellClick, emitCellDoubleClick } = createSelection<TFeatures, TData>(ctx);
|
|
2473
|
+
const { isRowSelected, toggleRowSelectionById, toggleSelectAllRows, setActiveCell, scrollActiveCellIntoView, setSelection, extendSelection, isCellInSelectedRange, getCellRangeEdges, getSelectionRects, isInFillPreview, fillMarqueeEdges, findColumnById, onCellPointerDown, onCellPointerEnter, endDragSelection, onWindowPointerMove, onCellClick, emitCellDoubleClick } = createSelection<TFeatures, TData>(ctx);
|
|
2408
2474
|
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<TFeatures, TData>(ctx);
|
|
2409
2475
|
const { onRowDragStart, onRowDragOver, onRowDragLeave, onRowDrop, onRowsContainerDragOver, onRowsContainerDrop, onRowDragEnd } = createRowDrag<TFeatures, TData>(ctx);
|
|
2410
2476
|
const { register: registerAlignedGrid, broadcastScroll: broadcastAlignedScroll, broadcastWidths: broadcastAlignedWidths } = createAlignedGrids<TFeatures, TData>(ctx);
|