@svgrid/grid 1.2.22 → 1.2.24
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 +89 -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 +409 -0
- package/dist/SvCalendar.svelte.d.ts +52 -0
- package/dist/SvCheckBox.svelte +82 -0
- package/dist/SvCheckBox.svelte.d.ts +19 -0
- package/dist/SvColorInput.svelte +160 -0
- package/dist/SvColorInput.svelte.d.ts +15 -0
- package/dist/SvComboBox.svelte +108 -0
- package/dist/SvComboBox.svelte.d.ts +15 -0
- package/dist/SvCountryInput.svelte +125 -0
- package/dist/SvCountryInput.svelte.d.ts +14 -0
- package/dist/SvDateTimePicker.svelte +265 -0
- package/dist/SvDateTimePicker.svelte.d.ts +38 -0
- package/dist/SvDropDownList.svelte +120 -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 +97 -0
- package/dist/SvGauge.svelte.d.ts +26 -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 +89 -0
- package/dist/SvListBox.svelte.d.ts +22 -0
- package/dist/SvMaskedInput.svelte +66 -0
- package/dist/SvMaskedInput.svelte.d.ts +15 -0
- package/dist/SvNumberInput.svelte +116 -0
- package/dist/SvNumberInput.svelte.d.ts +18 -0
- package/dist/SvPasswordInput.svelte +100 -0
- package/dist/SvPasswordInput.svelte.d.ts +18 -0
- package/dist/SvPhoneInput.svelte +87 -0
- package/dist/SvPhoneInput.svelte.d.ts +19 -0
- package/dist/SvRadioGroup.svelte +85 -0
- package/dist/SvRadioGroup.svelte.d.ts +20 -0
- package/dist/SvRating.svelte +91 -0
- package/dist/SvRating.svelte.d.ts +18 -0
- package/dist/SvRepeatButton.svelte +89 -0
- package/dist/SvRepeatButton.svelte.d.ts +21 -0
- package/dist/SvSlider.svelte +155 -0
- package/dist/SvSlider.svelte.d.ts +20 -0
- package/dist/SvSwitchButton.svelte +87 -0
- package/dist/SvSwitchButton.svelte.d.ts +14 -0
- package/dist/SvTabs.svelte +109 -0
- package/dist/SvTabs.svelte.d.ts +26 -0
- package/dist/SvTagsInput.svelte +70 -0
- package/dist/SvTagsInput.svelte.d.ts +14 -0
- package/dist/SvTimePicker.svelte +201 -0
- package/dist/SvTimePicker.svelte.d.ts +30 -0
- package/dist/SvToggleButton.svelte +57 -0
- package/dist/SvToggleButton.svelte.d.ts +16 -0
- package/dist/SvTree.svelte +131 -0
- package/dist/SvTree.svelte.d.ts +27 -0
- package/dist/build-api.js +78 -1
- package/dist/cdn/svgrid.js +11674 -5907
- package/dist/cdn/svgrid.svelte-external.js +10436 -4671
- 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/createAutocomplete.svelte.d.ts +77 -0
- package/dist/createAutocomplete.svelte.js +100 -0
- package/dist/createCalendar.svelte.d.ts +192 -0
- package/dist/createCalendar.svelte.js +402 -0
- package/dist/createCheckbox.svelte.d.ts +45 -0
- package/dist/createCheckbox.svelte.js +26 -0
- package/dist/createColorInput.svelte.d.ts +67 -0
- package/dist/createColorInput.svelte.js +100 -0
- package/dist/createCombobox.svelte.d.ts +102 -0
- package/dist/createCombobox.svelte.js +171 -0
- package/dist/createCountryInput.svelte.d.ts +86 -0
- package/dist/createCountryInput.svelte.js +126 -0
- package/dist/createDateTimePicker.svelte.d.ts +83 -0
- package/dist/createDateTimePicker.svelte.js +202 -0
- package/dist/createDropdownList.svelte.d.ts +78 -0
- package/dist/createDropdownList.svelte.js +117 -0
- package/dist/createGauge.svelte.d.ts +56 -0
- package/dist/createGauge.svelte.js +49 -0
- package/dist/createListbox.svelte.d.ts +79 -0
- package/dist/createListbox.svelte.js +117 -0
- package/dist/createMaskedInput.svelte.d.ts +30 -0
- package/dist/createMaskedInput.svelte.js +62 -0
- package/dist/createNumberInput.svelte.d.ts +72 -0
- package/dist/createNumberInput.svelte.js +167 -0
- package/dist/createPasswordInput.svelte.d.ts +60 -0
- package/dist/createPasswordInput.svelte.js +72 -0
- package/dist/createPhoneInput.svelte.d.ts +46 -0
- package/dist/createPhoneInput.svelte.js +105 -0
- package/dist/createRadioGroup.svelte.d.ts +64 -0
- package/dist/createRadioGroup.svelte.js +73 -0
- package/dist/createRating.svelte.d.ts +70 -0
- package/dist/createRating.svelte.js +85 -0
- package/dist/createSlider.svelte.d.ts +71 -0
- package/dist/createSlider.svelte.js +123 -0
- package/dist/createSwitch.svelte.d.ts +43 -0
- package/dist/createSwitch.svelte.js +41 -0
- package/dist/createTabs.svelte.d.ts +78 -0
- package/dist/createTabs.svelte.js +92 -0
- package/dist/createTagsInput.svelte.d.ts +65 -0
- package/dist/createTagsInput.svelte.js +93 -0
- package/dist/createTimePicker.svelte.d.ts +106 -0
- package/dist/createTimePicker.svelte.js +248 -0
- package/dist/createToggle.svelte.d.ts +43 -0
- package/dist/createToggle.svelte.js +39 -0
- package/dist/createTree.svelte.d.ts +96 -0
- package/dist/createTree.svelte.js +194 -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/editor-contract.d.ts +48 -0
- package/dist/editor-contract.js +26 -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 +60 -0
- package/dist/index.js +72 -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 +89 -0
- package/src/SvButton.svelte +97 -0
- package/src/SvCalendar.svelte +409 -0
- package/src/SvCalendar.test.ts +211 -0
- package/src/SvCheckBox.svelte +82 -0
- package/src/SvColorInput.svelte +160 -0
- package/src/SvComboBox.svelte +108 -0
- package/src/SvCountryInput.svelte +125 -0
- package/src/SvDateTimePicker.svelte +265 -0
- package/src/SvDateTimePicker.test.ts +136 -0
- package/src/SvDropDownList.svelte +120 -0
- package/src/SvForm.svelte +147 -0
- package/src/SvGauge.svelte +97 -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 +89 -0
- package/src/SvMaskedInput.svelte +66 -0
- package/src/SvNumberInput.svelte +116 -0
- package/src/SvPasswordInput.svelte +100 -0
- package/src/SvPhoneInput.svelte +87 -0
- package/src/SvRadioGroup.svelte +85 -0
- package/src/SvRating.svelte +91 -0
- package/src/SvRepeatButton.svelte +89 -0
- package/src/SvSlider.svelte +155 -0
- package/src/SvSwitchButton.svelte +87 -0
- package/src/SvTabs.svelte +109 -0
- package/src/SvTagsInput.svelte +70 -0
- package/src/SvTimePicker.svelte +201 -0
- package/src/SvTimePicker.test.ts +98 -0
- package/src/SvToggleButton.svelte +57 -0
- package/src/SvTree.svelte +131 -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/createAutocomplete.svelte.ts +104 -0
- package/src/createCalendar.svelte.ts +488 -0
- package/src/createCheckbox.svelte.ts +74 -0
- package/src/createColorInput.svelte.ts +115 -0
- package/src/createCombobox.svelte.ts +158 -0
- package/src/createCountryInput.svelte.ts +126 -0
- package/src/createDateTimePicker.svelte.ts +216 -0
- package/src/createDropdownList.svelte.ts +125 -0
- package/src/createGauge.svelte.ts +86 -0
- package/src/createListbox.svelte.ts +186 -0
- package/src/createMaskedInput.svelte.ts +81 -0
- package/src/createNumberInput.svelte.ts +193 -0
- package/src/createPasswordInput.svelte.ts +87 -0
- package/src/createPhoneInput.svelte.ts +119 -0
- package/src/createRadioGroup.svelte.ts +132 -0
- package/src/createRating.svelte.ts +145 -0
- package/src/createSlider.svelte.ts +160 -0
- package/src/createSwitch.svelte.ts +78 -0
- package/src/createTabs.svelte.ts +129 -0
- package/src/createTagsInput.svelte.ts +98 -0
- package/src/createTimePicker.svelte.ts +289 -0
- package/src/createToggle.svelte.ts +69 -0
- package/src/createTree.svelte.ts +216 -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/editor-contract.test.ts +32 -0
- package/src/editor-contract.ts +60 -0
- 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 +110 -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 +118 -0
- package/src/ui-range.test.ts +61 -0
- package/src/ui-selection.test.ts +119 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export type { TimeValue } from './createTimePicker.svelte'
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
/**
|
|
7
|
+
* SvTimePicker - an analog clock-dial time picker (parity: Smart
|
|
8
|
+
* `smart-time-picker`). 12/24-hour, minute snapping, hour->minute auto-switch,
|
|
9
|
+
* pointer-drag + keyboard. It is a styled renderer over the headless
|
|
10
|
+
* `createTimePicker` core: the core owns the value math, dial geometry, keyboard
|
|
11
|
+
* and ARIA; this component keeps the DOM-bound pointer capture + hit-testing.
|
|
12
|
+
* All visuals come from `--sg-*` tokens so every theme applies for free.
|
|
13
|
+
*/
|
|
14
|
+
import { createTimePicker, type TimeValue, type TimeFormat, type TimeSelection } from './createTimePicker.svelte'
|
|
15
|
+
|
|
16
|
+
type Props = {
|
|
17
|
+
/** Date, "HH:MM[:SS]" string, or epoch ms. */
|
|
18
|
+
value?: TimeValue
|
|
19
|
+
/** Fires with a Date (today's date carrying the picked time). */
|
|
20
|
+
onChange?: (value: Date) => void
|
|
21
|
+
format?: TimeFormat
|
|
22
|
+
minuteInterval?: number
|
|
23
|
+
/** After picking an hour, jump the dial to minutes. Default true. */
|
|
24
|
+
autoSwitchToMinutes?: boolean
|
|
25
|
+
/** Show the Now footer button. */
|
|
26
|
+
footer?: boolean
|
|
27
|
+
disabled?: boolean
|
|
28
|
+
readonly?: boolean
|
|
29
|
+
name?: string
|
|
30
|
+
/** Which dial opens first. */
|
|
31
|
+
selection?: TimeSelection
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let {
|
|
35
|
+
value = null,
|
|
36
|
+
onChange,
|
|
37
|
+
format = '24-hour',
|
|
38
|
+
minuteInterval = 1,
|
|
39
|
+
autoSwitchToMinutes = true,
|
|
40
|
+
footer = false,
|
|
41
|
+
disabled = false,
|
|
42
|
+
readonly = false,
|
|
43
|
+
name,
|
|
44
|
+
selection = 'hour',
|
|
45
|
+
}: Props = $props()
|
|
46
|
+
|
|
47
|
+
// The headless core owns all state, geometry, keyboard + ARIA. Reactive inputs
|
|
48
|
+
// are getters; callbacks are closures.
|
|
49
|
+
const tp = createTimePicker({
|
|
50
|
+
value: () => value,
|
|
51
|
+
onChange: (d) => onChange?.(d),
|
|
52
|
+
format: () => format,
|
|
53
|
+
minuteInterval: () => minuteInterval,
|
|
54
|
+
autoSwitchToMinutes: () => autoSwitchToMinutes,
|
|
55
|
+
disabled: () => disabled,
|
|
56
|
+
readonly: () => readonly,
|
|
57
|
+
selection: () => selection,
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// --- Pointer drag on the dial (DOM-bound; stays in the component) -----------
|
|
61
|
+
// The core exposes SIZE/C + a pure `pointerSelect(angle, dist)`; here we only
|
|
62
|
+
// turn a pointer event into that angle via the live element rect.
|
|
63
|
+
let dialEl: SVGSVGElement | null = null
|
|
64
|
+
let dragging = false
|
|
65
|
+
|
|
66
|
+
function angleFromEvent(e: PointerEvent): { angle: number; dist: number } {
|
|
67
|
+
const rect = dialEl!.getBoundingClientRect()
|
|
68
|
+
const scale = tp.SIZE / rect.width
|
|
69
|
+
const x = (e.clientX - rect.left) * scale - tp.C
|
|
70
|
+
const y = (e.clientY - rect.top) * scale - tp.C
|
|
71
|
+
let deg = (Math.atan2(y, x) * 180) / Math.PI + 90
|
|
72
|
+
if (deg < 0) deg += 360
|
|
73
|
+
return { angle: deg, dist: Math.hypot(x, y) }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function onDialPointerDown(e: PointerEvent) {
|
|
77
|
+
if (!tp.isInteractive) return
|
|
78
|
+
dragging = true
|
|
79
|
+
dialEl?.setPointerCapture(e.pointerId)
|
|
80
|
+
const { angle, dist } = angleFromEvent(e)
|
|
81
|
+
tp.pointerSelect(angle, dist)
|
|
82
|
+
}
|
|
83
|
+
function onDialPointerMove(e: PointerEvent) {
|
|
84
|
+
if (!dragging) return
|
|
85
|
+
const { angle, dist } = angleFromEvent(e)
|
|
86
|
+
tp.pointerSelect(angle, dist)
|
|
87
|
+
}
|
|
88
|
+
function onDialPointerUp(e: PointerEvent) {
|
|
89
|
+
if (!dragging) return
|
|
90
|
+
dragging = false
|
|
91
|
+
dialEl?.releasePointerCapture(e.pointerId)
|
|
92
|
+
tp.endPointer()
|
|
93
|
+
}
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
<div class="sv-tp" class:sv-tp--disabled={disabled} role="group" aria-label="Time picker">
|
|
97
|
+
<div class="sv-tp__head">
|
|
98
|
+
<button class="sv-tp__seg" class:is-active={tp.selection === 'hour'} {...tp.segProps('hour')}>
|
|
99
|
+
{String(tp.displayHour).padStart(2, '0')}
|
|
100
|
+
</button>
|
|
101
|
+
<span class="sv-tp__colon">:</span>
|
|
102
|
+
<button class="sv-tp__seg" class:is-active={tp.selection === 'minute'} {...tp.segProps('minute')}>
|
|
103
|
+
{tp.mm}
|
|
104
|
+
</button>
|
|
105
|
+
{#if tp.is12}
|
|
106
|
+
<div class="sv-tp__ampm">
|
|
107
|
+
<button class:is-active={!tp.isPm} {...tp.ampmProps(false)}>AM</button>
|
|
108
|
+
<button class:is-active={tp.isPm} {...tp.ampmProps(true)}>PM</button>
|
|
109
|
+
</div>
|
|
110
|
+
{/if}
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<svg
|
|
114
|
+
bind:this={dialEl}
|
|
115
|
+
class="sv-tp__dial"
|
|
116
|
+
viewBox={`0 0 ${tp.SIZE} ${tp.SIZE}`}
|
|
117
|
+
width="100%"
|
|
118
|
+
{...tp.dialProps()}
|
|
119
|
+
onpointerdown={onDialPointerDown}
|
|
120
|
+
onpointermove={onDialPointerMove}
|
|
121
|
+
onpointerup={onDialPointerUp}
|
|
122
|
+
onpointercancel={onDialPointerUp}
|
|
123
|
+
>
|
|
124
|
+
<circle class="sv-tp__face" cx={tp.C} cy={tp.C} r={tp.C - 4} />
|
|
125
|
+
<!-- hand -->
|
|
126
|
+
<line class="sv-tp__hand" x1={tp.C} y1={tp.C} x2={tp.handEnd.x} y2={tp.handEnd.y} />
|
|
127
|
+
<circle class="sv-tp__hub" cx={tp.C} cy={tp.C} r="3" />
|
|
128
|
+
<circle class="sv-tp__knob" cx={tp.handEnd.x} cy={tp.handEnd.y} r="15" />
|
|
129
|
+
{#if tp.selection === 'hour'}
|
|
130
|
+
{#each tp.hourTicks as t (t.ring + '-' + t.value)}
|
|
131
|
+
<text class="sv-tp__num" class:is-active={tp.isActiveHour(t)} class:is-inner={t.ring === 'inner'} x={t.x} y={t.y} dominant-baseline="central" text-anchor="middle">{t.label}</text>
|
|
132
|
+
{/each}
|
|
133
|
+
{:else}
|
|
134
|
+
{#each tp.minuteTicks as t (t.value)}
|
|
135
|
+
<text class="sv-tp__num" class:is-active={t.value === tp.minutes} x={t.x} y={t.y} dominant-baseline="central" text-anchor="middle">{t.label}</text>
|
|
136
|
+
{/each}
|
|
137
|
+
{/if}
|
|
138
|
+
</svg>
|
|
139
|
+
|
|
140
|
+
{#if footer}
|
|
141
|
+
<div class="sv-tp__footer">
|
|
142
|
+
<button class="sv-tp__now" {...tp.nowProps()}>Now</button>
|
|
143
|
+
</div>
|
|
144
|
+
{/if}
|
|
145
|
+
|
|
146
|
+
{#if name}<input type="hidden" {name} value={`${String(tp.hours).padStart(2, '0')}:${tp.mm}`} />{/if}
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<style>
|
|
150
|
+
.sv-tp {
|
|
151
|
+
--_accent: var(--sg-accent, #2563eb);
|
|
152
|
+
--_bg: var(--sg-bg, #fff);
|
|
153
|
+
--_fg: var(--sg-fg, #0f172a);
|
|
154
|
+
--_muted: var(--sg-muted, #64748b);
|
|
155
|
+
--_border: var(--sg-border, #e2e8f0);
|
|
156
|
+
--_face: var(--sg-header-bg, #f1f5f9);
|
|
157
|
+
--_radius: var(--sg-radius, 8px);
|
|
158
|
+
display: inline-flex; flex-direction: column; gap: 10px; align-items: center;
|
|
159
|
+
width: 260px; padding: 12px;
|
|
160
|
+
background: var(--_bg); color: var(--_fg);
|
|
161
|
+
border: 1px solid var(--_border); border-radius: calc(var(--_radius) + 4px);
|
|
162
|
+
user-select: none;
|
|
163
|
+
}
|
|
164
|
+
.sv-tp--disabled { opacity: 0.55; pointer-events: none; }
|
|
165
|
+
|
|
166
|
+
.sv-tp__head { display: flex; align-items: center; gap: 2px; font-variant-numeric: tabular-nums; }
|
|
167
|
+
.sv-tp__seg {
|
|
168
|
+
font-size: 30px; font-weight: 700; line-height: 1;
|
|
169
|
+
background: none; border: 0; color: var(--_muted); cursor: pointer;
|
|
170
|
+
padding: 2px 6px; border-radius: var(--_radius);
|
|
171
|
+
}
|
|
172
|
+
.sv-tp__seg.is-active { color: var(--_accent); background: color-mix(in srgb, var(--_accent) 12%, transparent); }
|
|
173
|
+
.sv-tp__colon { font-size: 28px; font-weight: 700; color: var(--_muted); }
|
|
174
|
+
.sv-tp__ampm { display: flex; flex-direction: column; margin-left: 8px; gap: 2px; }
|
|
175
|
+
.sv-tp__ampm button {
|
|
176
|
+
font-size: 11px; font-weight: 700; padding: 3px 8px;
|
|
177
|
+
background: none; border: 1px solid var(--_border); border-radius: 6px; color: var(--_muted); cursor: pointer;
|
|
178
|
+
}
|
|
179
|
+
.sv-tp__ampm button.is-active { background: var(--_accent); color: var(--sg-on-accent, #fff); border-color: var(--_accent); }
|
|
180
|
+
|
|
181
|
+
.sv-tp__dial { touch-action: none; cursor: pointer; outline: none; display: block; width: 100%; height: auto; aspect-ratio: 1; }
|
|
182
|
+
.sv-tp__dial:focus-visible { outline: 2px solid var(--sg-focus-ring, var(--_accent)); outline-offset: 2px; border-radius: 50%; }
|
|
183
|
+
.sv-tp__face { fill: var(--_face); }
|
|
184
|
+
.sv-tp__hand { stroke: var(--_accent); stroke-width: 2; }
|
|
185
|
+
.sv-tp__hub { fill: var(--_accent); }
|
|
186
|
+
.sv-tp__knob { fill: var(--_accent); opacity: 0.9; }
|
|
187
|
+
.sv-tp__num {
|
|
188
|
+
font-size: 13px; fill: var(--_fg); pointer-events: none;
|
|
189
|
+
font-family: inherit;
|
|
190
|
+
}
|
|
191
|
+
.sv-tp__num.is-inner { font-size: 11px; fill: var(--_muted); }
|
|
192
|
+
.sv-tp__num.is-active { fill: var(--sg-on-accent, #fff); font-weight: 700; }
|
|
193
|
+
|
|
194
|
+
.sv-tp__footer { width: 100%; display: flex; justify-content: center; border-top: 1px solid var(--_border); padding-top: 8px; }
|
|
195
|
+
.sv-tp__now {
|
|
196
|
+
padding: 5px 14px; font: inherit; font-size: 12px; font-weight: 600;
|
|
197
|
+
background: none; border: 1px solid var(--_border); border-radius: var(--_radius);
|
|
198
|
+
color: var(--_accent); cursor: pointer;
|
|
199
|
+
}
|
|
200
|
+
.sv-tp__now:hover { background: color-mix(in srgb, var(--_accent) 10%, transparent); }
|
|
201
|
+
</style>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type { TimeValue } from './createTimePicker.svelte';
|
|
2
|
+
/**
|
|
3
|
+
* SvTimePicker - an analog clock-dial time picker (parity: Smart
|
|
4
|
+
* `smart-time-picker`). 12/24-hour, minute snapping, hour->minute auto-switch,
|
|
5
|
+
* pointer-drag + keyboard. It is a styled renderer over the headless
|
|
6
|
+
* `createTimePicker` core: the core owns the value math, dial geometry, keyboard
|
|
7
|
+
* and ARIA; this component keeps the DOM-bound pointer capture + hit-testing.
|
|
8
|
+
* All visuals come from `--sg-*` tokens so every theme applies for free.
|
|
9
|
+
*/
|
|
10
|
+
import { type TimeValue, type TimeFormat, type TimeSelection } from './createTimePicker.svelte';
|
|
11
|
+
type Props = {
|
|
12
|
+
/** Date, "HH:MM[:SS]" string, or epoch ms. */
|
|
13
|
+
value?: TimeValue;
|
|
14
|
+
/** Fires with a Date (today's date carrying the picked time). */
|
|
15
|
+
onChange?: (value: Date) => void;
|
|
16
|
+
format?: TimeFormat;
|
|
17
|
+
minuteInterval?: number;
|
|
18
|
+
/** After picking an hour, jump the dial to minutes. Default true. */
|
|
19
|
+
autoSwitchToMinutes?: boolean;
|
|
20
|
+
/** Show the Now footer button. */
|
|
21
|
+
footer?: boolean;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
readonly?: boolean;
|
|
24
|
+
name?: string;
|
|
25
|
+
/** Which dial opens first. */
|
|
26
|
+
selection?: TimeSelection;
|
|
27
|
+
};
|
|
28
|
+
declare const SvTimePicker: import("svelte").Component<Props, {}, "">;
|
|
29
|
+
type SvTimePicker = ReturnType<typeof SvTimePicker>;
|
|
30
|
+
export default SvTimePicker;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* SvToggleButton - a button with a pressed on/off state (aria-pressed).
|
|
4
|
+
* Parity: Smart `smart-toggle-button`. Controlled via `pressed` + `onChange`.
|
|
5
|
+
*/
|
|
6
|
+
import type { Snippet } from 'svelte'
|
|
7
|
+
import { createToggle } from './createToggle.svelte'
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
pressed?: boolean
|
|
11
|
+
onChange?: (pressed: boolean) => void
|
|
12
|
+
disabled?: boolean
|
|
13
|
+
size?: 'sm' | 'md' | 'lg'
|
|
14
|
+
ariaLabel?: string
|
|
15
|
+
children?: Snippet
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let { pressed = false, onChange, disabled = false, size = 'md', ariaLabel, children }: Props = $props()
|
|
19
|
+
|
|
20
|
+
// The styled toggle is just a renderer over the headless core.
|
|
21
|
+
const t = createToggle({
|
|
22
|
+
pressed: () => pressed,
|
|
23
|
+
onChange: (v) => onChange?.(v),
|
|
24
|
+
disabled: () => disabled,
|
|
25
|
+
ariaLabel: () => ariaLabel,
|
|
26
|
+
})
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<button
|
|
30
|
+
class="sv-toggle sv-toggle--{size}"
|
|
31
|
+
class:is-pressed={pressed}
|
|
32
|
+
{...t.buttonProps()}
|
|
33
|
+
>
|
|
34
|
+
{#if children}{@render children()}{/if}
|
|
35
|
+
</button>
|
|
36
|
+
|
|
37
|
+
<style>
|
|
38
|
+
.sv-toggle {
|
|
39
|
+
--_accent: var(--sg-accent, #2563eb);
|
|
40
|
+
--_border: var(--sg-border, #cbd5e1);
|
|
41
|
+
--_radius: var(--sg-radius, 8px);
|
|
42
|
+
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
|
|
43
|
+
font: inherit; font-weight: 600; line-height: 1;
|
|
44
|
+
background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a);
|
|
45
|
+
border: 1px solid var(--_border); border-radius: var(--_radius); cursor: pointer; user-select: none;
|
|
46
|
+
transition: background 0.12s, border-color 0.12s, color 0.12s;
|
|
47
|
+
}
|
|
48
|
+
.sv-toggle--sm { padding: 5px 10px; font-size: 12px; }
|
|
49
|
+
.sv-toggle--md { padding: 8px 13px; font-size: 13px; }
|
|
50
|
+
.sv-toggle--lg { padding: 11px 17px; font-size: 15px; }
|
|
51
|
+
.sv-toggle:hover:not([disabled]):not(.is-pressed) { background: var(--sg-row-hover-bg, #f1f5f9); }
|
|
52
|
+
.sv-toggle.is-pressed {
|
|
53
|
+
background: var(--_accent); color: var(--sg-on-accent, #fff); border-color: var(--_accent);
|
|
54
|
+
}
|
|
55
|
+
.sv-toggle[disabled] { opacity: 0.55; cursor: not-allowed; }
|
|
56
|
+
.sv-toggle:focus-visible { outline: 2px solid var(--sg-focus-ring, var(--_accent)); outline-offset: 2px; }
|
|
57
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SvToggleButton - a button with a pressed on/off state (aria-pressed).
|
|
3
|
+
* Parity: Smart `smart-toggle-button`. Controlled via `pressed` + `onChange`.
|
|
4
|
+
*/
|
|
5
|
+
import type { Snippet } from 'svelte';
|
|
6
|
+
type Props = {
|
|
7
|
+
pressed?: boolean;
|
|
8
|
+
onChange?: (pressed: boolean) => void;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
size?: 'sm' | 'md' | 'lg';
|
|
11
|
+
ariaLabel?: string;
|
|
12
|
+
children?: Snippet;
|
|
13
|
+
};
|
|
14
|
+
declare const SvToggleButton: import("svelte").Component<Props, {}, "">;
|
|
15
|
+
type SvToggleButton = ReturnType<typeof SvToggleButton>;
|
|
16
|
+
export default SvToggleButton;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export type { TreeNode } from './createTree.svelte'
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
/**
|
|
7
|
+
* SvTree - a WAI-ARIA tree view. Expand/collapse, single-select highlight, and
|
|
8
|
+
* optional cascading tri-state checkboxes. Parity: Smart `smart-tree`. Keyboard:
|
|
9
|
+
* up/down move, left collapse/parent, right expand/child, Enter/Space select.
|
|
10
|
+
*
|
|
11
|
+
* The behavior (flattening, cascade math, keyboard, ARIA) lives in the headless
|
|
12
|
+
* `createTree` core; this component is just one styled renderer over it.
|
|
13
|
+
*/
|
|
14
|
+
import { createTree, type TreeNode } from './createTree.svelte'
|
|
15
|
+
|
|
16
|
+
type Props = {
|
|
17
|
+
nodes: ReadonlyArray<TreeNode>
|
|
18
|
+
/** Selected node id (single-select highlight). */
|
|
19
|
+
selected?: string | null
|
|
20
|
+
onSelect?: (id: string) => void
|
|
21
|
+
/** Initially/controlled expanded ids. */
|
|
22
|
+
expandedIds?: string[]
|
|
23
|
+
onToggle?: (id: string, expanded: boolean) => void
|
|
24
|
+
/** Show cascading checkboxes; `checked` is the set of checked ids. */
|
|
25
|
+
checkable?: boolean
|
|
26
|
+
checked?: string[]
|
|
27
|
+
onCheck?: (ids: string[]) => void
|
|
28
|
+
ariaLabel?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let {
|
|
32
|
+
nodes,
|
|
33
|
+
selected = null,
|
|
34
|
+
onSelect,
|
|
35
|
+
expandedIds,
|
|
36
|
+
onToggle,
|
|
37
|
+
checkable = false,
|
|
38
|
+
checked = [],
|
|
39
|
+
onCheck,
|
|
40
|
+
ariaLabel,
|
|
41
|
+
}: Props = $props()
|
|
42
|
+
|
|
43
|
+
const tree = createTree({
|
|
44
|
+
nodes: () => nodes,
|
|
45
|
+
selected: () => selected,
|
|
46
|
+
onSelect: (id) => onSelect?.(id),
|
|
47
|
+
expandedIds: () => expandedIds,
|
|
48
|
+
onToggle: (id, exp) => onToggle?.(id, exp),
|
|
49
|
+
checkable: () => checkable,
|
|
50
|
+
checked: () => checked,
|
|
51
|
+
onCheck: (ids) => onCheck?.(ids),
|
|
52
|
+
ariaLabel: () => ariaLabel,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
// DOM focus movement is a render concern: follow the core's roving focus row.
|
|
56
|
+
let treeEl: HTMLDivElement | null = null
|
|
57
|
+
let lastFocusTick = 0
|
|
58
|
+
$effect(() => {
|
|
59
|
+
if (tree.focusTick !== lastFocusTick) {
|
|
60
|
+
lastFocusTick = tree.focusTick
|
|
61
|
+
const i = tree.activeIndex
|
|
62
|
+
queueMicrotask(() => treeEl?.querySelector<HTMLElement>(`[data-row="${i}"]`)?.focus())
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<div bind:this={treeEl} class="sv-tree" {...tree.treeProps()}>
|
|
68
|
+
{#each tree.rows as item (item.node.id)}
|
|
69
|
+
{@const cs = checkable ? tree.checkStateOf(item.node) : 'unchecked'}
|
|
70
|
+
<div
|
|
71
|
+
class="sv-tree__row"
|
|
72
|
+
class:is-selected={item.node.id === selected}
|
|
73
|
+
class:is-disabled={item.node.disabled}
|
|
74
|
+
style:padding-left={`${item.depth * 18 + 6}px`}
|
|
75
|
+
{...tree.itemProps(item)}
|
|
76
|
+
>
|
|
77
|
+
<button
|
|
78
|
+
type="button"
|
|
79
|
+
class="sv-tree__twist"
|
|
80
|
+
class:is-open={item.open}
|
|
81
|
+
class:is-leaf={!item.hasChildren}
|
|
82
|
+
tabindex="-1"
|
|
83
|
+
aria-hidden={!item.hasChildren}
|
|
84
|
+
onclick={(e) => { e.stopPropagation(); tree.toggleExpand(item.node) }}
|
|
85
|
+
>
|
|
86
|
+
{#if item.hasChildren}
|
|
87
|
+
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6" /></svg>
|
|
88
|
+
{/if}
|
|
89
|
+
</button>
|
|
90
|
+
{#if checkable}
|
|
91
|
+
<button type="button" class="sv-tree__check is-{cs}" tabindex="-1" aria-hidden="true" onclick={(e) => { e.stopPropagation(); tree.toggleCheck(item.node) }}>
|
|
92
|
+
{#if cs === 'checked'}<svg viewBox="0 0 16 16"><path d="M3.5 8.5l3 3 6-6.5" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
|
93
|
+
{:else if cs === 'indeterminate'}<svg viewBox="0 0 16 16"><path d="M4 8h8" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" /></svg>{/if}
|
|
94
|
+
</button>
|
|
95
|
+
{/if}
|
|
96
|
+
<span class="sv-tree__label">{item.node.label}</span>
|
|
97
|
+
</div>
|
|
98
|
+
{/each}
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<style>
|
|
102
|
+
.sv-tree {
|
|
103
|
+
--_accent: var(--sg-accent, #2563eb);
|
|
104
|
+
width: 260px; padding: 4px; user-select: none;
|
|
105
|
+
background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a);
|
|
106
|
+
border: 1px solid var(--sg-border, #e2e8f0); border-radius: var(--sg-radius, 8px);
|
|
107
|
+
font-size: 13px;
|
|
108
|
+
}
|
|
109
|
+
.sv-tree__row {
|
|
110
|
+
display: flex; align-items: center; gap: 4px; height: 30px; padding-right: 8px;
|
|
111
|
+
border-radius: 6px; cursor: pointer; outline: none;
|
|
112
|
+
}
|
|
113
|
+
.sv-tree__row:hover:not(.is-disabled) { background: var(--sg-row-hover-bg, #f1f5f9); }
|
|
114
|
+
.sv-tree__row.is-selected { background: color-mix(in srgb, var(--_accent) 15%, transparent); color: var(--_accent); font-weight: 600; }
|
|
115
|
+
.sv-tree__row.is-disabled { opacity: 0.45; cursor: not-allowed; }
|
|
116
|
+
.sv-tree__row:focus-visible { box-shadow: inset 0 0 0 2px var(--sg-focus-ring, var(--_accent)); }
|
|
117
|
+
.sv-tree__twist {
|
|
118
|
+
display: grid; place-items: center; width: 18px; height: 18px; flex: none;
|
|
119
|
+
background: none; border: 0; color: var(--sg-muted, #64748b); cursor: pointer; transition: transform 0.12s;
|
|
120
|
+
}
|
|
121
|
+
.sv-tree__twist.is-open { transform: rotate(90deg); }
|
|
122
|
+
.sv-tree__twist.is-leaf { visibility: hidden; }
|
|
123
|
+
.sv-tree__check {
|
|
124
|
+
display: grid; place-items: center; width: 16px; height: 16px; flex: none; padding: 0;
|
|
125
|
+
border: 1.5px solid var(--sg-border, #cbd5e1); border-radius: 4px; background: var(--sg-input-bg, #fff);
|
|
126
|
+
color: var(--sg-on-accent, #fff); cursor: pointer;
|
|
127
|
+
}
|
|
128
|
+
.sv-tree__check.is-checked, .sv-tree__check.is-indeterminate { background: var(--_accent); border-color: var(--_accent); }
|
|
129
|
+
.sv-tree__check svg { width: 100%; height: 100%; }
|
|
130
|
+
.sv-tree__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
131
|
+
</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type { TreeNode } from './createTree.svelte';
|
|
2
|
+
/**
|
|
3
|
+
* SvTree - a WAI-ARIA tree view. Expand/collapse, single-select highlight, and
|
|
4
|
+
* optional cascading tri-state checkboxes. Parity: Smart `smart-tree`. Keyboard:
|
|
5
|
+
* up/down move, left collapse/parent, right expand/child, Enter/Space select.
|
|
6
|
+
*
|
|
7
|
+
* The behavior (flattening, cascade math, keyboard, ARIA) lives in the headless
|
|
8
|
+
* `createTree` core; this component is just one styled renderer over it.
|
|
9
|
+
*/
|
|
10
|
+
import { type TreeNode } from './createTree.svelte';
|
|
11
|
+
type Props = {
|
|
12
|
+
nodes: ReadonlyArray<TreeNode>;
|
|
13
|
+
/** Selected node id (single-select highlight). */
|
|
14
|
+
selected?: string | null;
|
|
15
|
+
onSelect?: (id: string) => void;
|
|
16
|
+
/** Initially/controlled expanded ids. */
|
|
17
|
+
expandedIds?: string[];
|
|
18
|
+
onToggle?: (id: string, expanded: boolean) => void;
|
|
19
|
+
/** Show cascading checkboxes; `checked` is the set of checked ids. */
|
|
20
|
+
checkable?: boolean;
|
|
21
|
+
checked?: string[];
|
|
22
|
+
onCheck?: (ids: string[]) => void;
|
|
23
|
+
ariaLabel?: string;
|
|
24
|
+
};
|
|
25
|
+
declare const SvTree: import("svelte").Component<Props, {}, "">;
|
|
26
|
+
type SvTree = ReturnType<typeof SvTree>;
|
|
27
|
+
export default SvTree;
|
package/dist/build-api.js
CHANGED
|
@@ -1,8 +1,35 @@
|
|
|
1
1
|
import "./sv-grid-scrollbar";
|
|
2
|
-
import {
|
|
2
|
+
import { projectGridRows, serializeDelimited, serializeJson, serializeMarkdown, downloadTextFile, copyTextToClipboard, } from "./export-format";
|
|
3
|
+
import { isGroupRow, getColumnAlign, columnDefMatchesId, } from "./cell-values";
|
|
3
4
|
export function createGridApi(ctx) {
|
|
4
5
|
function buildApi() {
|
|
5
6
|
const findColumn = (columnId) => ctx.grid.getAllColumns().find((column) => column.id === columnId);
|
|
7
|
+
// --- Free data export (CSV / TSV / JSON + clipboard) helpers ----------
|
|
8
|
+
// Snapshot the VISIBLE columns (field + label + format + align) so the
|
|
9
|
+
// exporter reproduces on-screen values. Mirrors getColumns()'s visible set.
|
|
10
|
+
const exportColumns = () => ctx.allColumns
|
|
11
|
+
.map((c) => ({
|
|
12
|
+
field: c.columnDef.field,
|
|
13
|
+
header: typeof c.columnDef.header === "string" ? c.columnDef.header : c.id,
|
|
14
|
+
format: c.columnDef.format,
|
|
15
|
+
align: getColumnAlign(c),
|
|
16
|
+
}))
|
|
17
|
+
.filter((c) => !!c.field);
|
|
18
|
+
// Rows for the requested scope: current view / checked / full dataset.
|
|
19
|
+
const exportScopeRows = (scope) => {
|
|
20
|
+
if (scope === "all")
|
|
21
|
+
return ctx.internalData;
|
|
22
|
+
const out = [];
|
|
23
|
+
for (const row of ctx.allRows) {
|
|
24
|
+
if (isGroupRow(row))
|
|
25
|
+
continue;
|
|
26
|
+
if (scope === "selected" && !ctx.rowSelectionState[row.id])
|
|
27
|
+
continue;
|
|
28
|
+
out.push(row.original);
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
};
|
|
32
|
+
const projectForExport = (scope, columns, rawValues) => projectGridRows(exportScopeRows(scope), exportColumns(), { columns, rawValues });
|
|
6
33
|
return {
|
|
7
34
|
getCellValue(rowIndex, columnId) {
|
|
8
35
|
const row = ctx.internalData[rowIndex];
|
|
@@ -281,6 +308,12 @@ export function createGridApi(ctx) {
|
|
|
281
308
|
? c.columnDef.header
|
|
282
309
|
: c.id,
|
|
283
310
|
visible,
|
|
311
|
+
// Surface the format + alignment so exporters can reproduce the
|
|
312
|
+
// on-screen display value (currency symbol, date pattern, etc.)
|
|
313
|
+
// instead of dumping raw values. `getColumnAlign` applies the same
|
|
314
|
+
// editorType-based default the body uses.
|
|
315
|
+
format: c.columnDef.format,
|
|
316
|
+
align: getColumnAlign(c),
|
|
284
317
|
});
|
|
285
318
|
// Visible columns first, in their current visual order...
|
|
286
319
|
const out = ctx.allColumns.map((c) => describe(c, true));
|
|
@@ -294,6 +327,50 @@ export function createGridApi(ctx) {
|
|
|
294
327
|
}
|
|
295
328
|
return out;
|
|
296
329
|
},
|
|
330
|
+
// ---- Free data export (CSV / TSV / JSON + clipboard) -----------------
|
|
331
|
+
async exportCsv(options = {}) {
|
|
332
|
+
const { records, fields } = projectForExport(options.rows ?? "displayed", options.columns, options.rawValues);
|
|
333
|
+
const text = await serializeDelimited(records, fields, {
|
|
334
|
+
csv: { delimiter: ",", bom: options.bom ?? true },
|
|
335
|
+
signal: options.signal,
|
|
336
|
+
onProgress: options.onProgress,
|
|
337
|
+
});
|
|
338
|
+
if (options.download !== false)
|
|
339
|
+
downloadTextFile(text, `${options.filename ?? "grid"}.csv`, "text/csv;charset=utf-8");
|
|
340
|
+
return text;
|
|
341
|
+
},
|
|
342
|
+
async exportTsv(options = {}) {
|
|
343
|
+
const { records, fields } = projectForExport(options.rows ?? "displayed", options.columns, options.rawValues);
|
|
344
|
+
const text = await serializeDelimited(records, fields, {
|
|
345
|
+
csv: { delimiter: "\t", bom: options.bom ?? true },
|
|
346
|
+
signal: options.signal,
|
|
347
|
+
onProgress: options.onProgress,
|
|
348
|
+
});
|
|
349
|
+
if (options.download !== false)
|
|
350
|
+
downloadTextFile(text, `${options.filename ?? "grid"}.tsv`, "text/tab-separated-values;charset=utf-8");
|
|
351
|
+
return text;
|
|
352
|
+
},
|
|
353
|
+
async exportJson(options = {}) {
|
|
354
|
+
const { records, fields } = projectForExport(options.rows ?? "displayed", options.columns, options.rawValues);
|
|
355
|
+
const text = await serializeJson(records, fields, {
|
|
356
|
+
signal: options.signal,
|
|
357
|
+
onProgress: options.onProgress,
|
|
358
|
+
});
|
|
359
|
+
if (options.download !== false)
|
|
360
|
+
downloadTextFile(text, `${options.filename ?? "grid"}.json`, "application/json");
|
|
361
|
+
return text;
|
|
362
|
+
},
|
|
363
|
+
async copyToClipboard(options = {}) {
|
|
364
|
+
const { records, fields, align } = projectForExport(options.rows ?? "displayed", options.columns, options.rawValues);
|
|
365
|
+
const format = options.format ?? "tsv";
|
|
366
|
+
const text = format === "markdown"
|
|
367
|
+
? await serializeMarkdown(records, fields, { align })
|
|
368
|
+
: await serializeDelimited(records, fields, {
|
|
369
|
+
csv: { delimiter: format === "csv" ? "," : "\t", bom: false },
|
|
370
|
+
});
|
|
371
|
+
await copyTextToClipboard(text);
|
|
372
|
+
return text;
|
|
373
|
+
},
|
|
297
374
|
clearRowSelection() {
|
|
298
375
|
// Wipe the internal selection map AND emit the change so any
|
|
299
376
|
// consumer holding a derived `selectedRows` resets too.
|