@svgrid/grid 2.2.31 → 2.2.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/DockNodeView.svelte +1 -1
- package/dist/GridFooter.svelte +0 -40
- package/dist/GridMenus.svelte +0 -77
- package/dist/SvAvatar.svelte +1 -1
- package/dist/SvCheckBox.svelte +2 -1
- package/dist/SvCircularProgress.svelte +1 -1
- package/dist/SvDateRangeInput.svelte +1 -1
- package/dist/SvGrid.controller.svelte.d.ts +20 -0
- package/dist/SvGrid.controller.svelte.js +358 -18
- package/dist/SvGrid.css +103 -3
- package/dist/SvGrid.svelte +164 -35
- package/dist/SvGrid.types.d.ts +81 -0
- package/dist/SvGridChart.svelte +2 -10
- package/dist/SvGridDropdown.svelte +38 -6
- package/dist/SvGridDropdown.svelte.d.ts +4 -0
- package/dist/SvGridSelect.svelte +12 -5
- package/dist/SvGridSelect.svelte.d.ts +6 -0
- package/dist/SvGroupCell.svelte +9 -2
- package/dist/SvKbd.svelte +1 -1
- package/dist/SvList.svelte +1 -1
- package/dist/SvMenubar.svelte +1 -1
- package/dist/SvMultiSelect.svelte +12 -4
- package/dist/SvMultiSelect.svelte.d.ts +6 -0
- package/dist/SvOtpInput.svelte +1 -1
- package/dist/SvPasswordInput.svelte +1 -1
- package/dist/SvProgress.svelte +1 -1
- package/dist/SvRating.svelte +1 -1
- package/dist/SvResult.svelte +1 -1
- package/dist/SvRichText.svelte +35 -16
- package/dist/SvRichText.svelte.d.ts +9 -0
- package/dist/SvSwitchButton.svelte +2 -1
- package/dist/SvToaster.svelte +1 -1
- package/dist/SvTour.svelte +4 -0
- package/dist/SvTree.svelte +9 -2
- package/dist/SvTree.svelte.d.ts +5 -0
- package/dist/SvTreeSelect.svelte +6 -4
- package/dist/build-api.js +16 -0
- package/dist/cdn/{GridMenus-Ds3OpzZT.js → GridMenus-B7XhhqdN.js} +34 -36
- package/dist/cdn/{GridMenus-Da_p7SKS.js → GridMenus-BxmIvCJh.js} +35 -37
- package/dist/cdn/{SvDateTimePicker-DB0aIEk8.js → SvDateTimePicker-ClqtVIj3.js} +2 -1
- package/dist/cdn/{SvDateTimePicker-B8GopjhC.js → SvDateTimePicker-o94wfSUa.js} +3 -2
- package/dist/cdn/{SvGridChart-BcLCYvw9.js → SvGridChart-Cr_90pPD.js} +1 -1
- package/dist/cdn/{SvGridChartPanel-DvjwxLvK.js → SvGridChartPanel-ICbxiUvK.js} +4 -4
- package/dist/cdn/{SvGridChartView-PwqI4s02.js → SvGridChartView-Bx42PMvf.js} +2 -2
- package/dist/cdn/{SvGridDropdown-hpnHvOGt.js → SvGridDropdown-CUOdm7YA.js} +93 -88
- package/dist/cdn/{SvGridDropdown-DjALc_4f.js → SvGridDropdown-D0VdjeR8.js} +129 -123
- package/dist/cdn/{disclose-version-DoD9AFD_.js → disclose-version-DljhZohK.js} +45 -45
- package/dist/cdn/{src-DYDCiC0D.js → src-erjBRYfX.js} +5952 -5615
- package/dist/cdn/{src-BLJfdyL0.js → src-ktbQVOk-.js} +4190 -3851
- package/dist/cdn/svgrid.js +9 -9
- package/dist/cdn/svgrid.svelte-external.js +8 -8
- package/dist/cell-render.d.ts +1 -0
- package/dist/cell-render.js +115 -1
- package/dist/cell-values.js +5 -0
- package/dist/chart.d.ts +2 -0
- package/dist/chart.js +1 -4
- package/dist/core.d.ts +80 -9
- package/dist/core.js +89 -0
- package/dist/createMenu.svelte.js +0 -1
- package/dist/createPopoverSelect.svelte.d.ts +2 -2
- package/dist/editing.js +1 -1
- package/dist/editor-contract.d.ts +12 -5
- package/dist/editor-registry.d.ts +18 -7
- package/dist/editor-registry.js +10 -1
- package/dist/export-format.js +3 -0
- package/dist/group-display.d.ts +39 -0
- package/dist/group-display.js +52 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/keyboard-handlers.js +16 -0
- package/dist/recurrence.js +3 -2
- package/dist/selection.js +0 -1
- package/dist/svgrid-wrapper.types.d.ts +8 -2
- package/dist/themes/index.d.ts +10 -0
- package/dist/themes/index.js +26 -0
- package/package.json +3 -2
- package/src/DockNodeView.svelte +1 -1
- package/src/GridFooter.svelte +0 -40
- package/src/GridMenus.svelte +0 -77
- package/src/SvAccordion.test.ts +5 -5
- package/src/SvAvatar.svelte +1 -1
- package/src/SvButtonGroup.test.ts +6 -6
- package/src/SvCalendar.test.ts +1 -1
- package/src/SvCheckBox.svelte +2 -1
- package/src/SvCircularProgress.svelte +1 -1
- package/src/SvDateRangeInput.svelte +1 -1
- package/src/SvFileUpload.test.ts +1 -1
- package/src/SvGrid.controller.svelte.ts +367 -22
- package/src/SvGrid.css +103 -3
- package/src/SvGrid.svelte +164 -35
- package/src/SvGrid.types.ts +81 -1
- package/src/SvGridChart.svelte +2 -10
- package/src/SvGridDropdown.svelte +38 -6
- package/src/SvGridSelect.svelte +12 -5
- package/src/SvGroupCell.svelte +9 -2
- package/src/SvKbd.svelte +1 -1
- package/src/SvList.svelte +1 -1
- package/src/SvMenubar.svelte +1 -1
- package/src/SvMultiSelect.svelte +12 -4
- package/src/SvOtpInput.svelte +1 -1
- package/src/SvPasswordInput.svelte +1 -1
- package/src/SvProgress.svelte +1 -1
- package/src/SvRating.svelte +1 -1
- package/src/SvResult.svelte +1 -1
- package/src/SvRichText.svelte +35 -16
- package/src/SvSwitchButton.svelte +2 -1
- package/src/SvToaster.svelte +1 -1
- package/src/SvTour.svelte +4 -0
- package/src/SvTree.svelte +9 -2
- package/src/SvTreeSelect.svelte +6 -4
- package/src/ai.test.ts +1 -1
- package/src/build-api.ts +15 -105
- package/src/builtin-editors.test.ts +3 -1
- package/src/cell-formatting.test.ts +1 -1
- package/src/cell-render.test.ts +27 -3
- package/src/cell-render.ts +122 -95
- package/src/cell-values.ts +4 -0
- package/src/chart-export.test.ts +2 -2
- package/src/chart.ts +1 -2
- package/src/clipboard.test.ts +0 -2
- package/src/clipboard.ts +0 -102
- package/src/columns.ts +0 -115
- package/src/core.ts +157 -8
- package/src/createMenu.svelte.ts +0 -1
- package/src/editing.test.ts +1 -1
- package/src/editing.ts +2 -105
- package/src/editor-contract.ts +12 -5
- package/src/editor-registry.grid.test.ts +48 -2
- package/src/editor-registry.test.ts +3 -1
- package/src/editor-registry.ts +23 -7
- package/src/export-format.ts +3 -0
- package/src/facet-buckets.test.ts +33 -33
- package/src/features.ts +0 -90
- package/src/group-display.test.ts +91 -1
- package/src/group-display.ts +83 -0
- package/src/index.ts +9 -0
- package/src/keyboard-handlers.coverage.test.ts +1 -1
- package/src/keyboard-handlers.ts +17 -120
- package/src/list-power.test.ts +2 -2
- package/src/menus.test.ts +4 -5
- package/src/menus.ts +0 -112
- package/src/named-views.coverage.test.ts +3 -3
- package/src/new-features.test.ts +3 -1
- package/src/recurrence.ts +3 -2
- package/src/row-drag.test.ts +1 -1
- package/src/row-resize.test.ts +41 -42
- package/src/scheduler-model.ts +1 -1
- package/src/scroll-sync.test.ts +4 -7
- package/src/scroll-sync.ts +0 -122
- package/src/selection.ts +0 -109
- package/src/server-data-source.coverage.test.ts +1 -1
- package/src/server-group-model.test.ts +8 -8
- package/src/spreadsheet.test.ts +0 -1
- package/src/summaries.ts +0 -112
- package/src/svgrid-wrapper.types.ts +11 -9
- package/src/svgrid.async-editor-options.test.ts +271 -0
- package/src/svgrid.auto-row-height.test.ts +204 -0
- package/src/svgrid.behavior.test.ts +0 -1
- package/src/svgrid.charting.test.ts +4 -4
- package/src/svgrid.column-reorder.test.ts +0 -8
- package/src/svgrid.comments-autocomplete.test.ts +8 -2
- package/src/svgrid.context-menu.test.ts +25 -9
- package/src/svgrid.filter-depth.test.ts +163 -0
- package/src/svgrid.filter-menu-listbox.svelte.test.ts +16 -2
- package/src/svgrid.filter-menu-scroll.test.ts +10 -2
- package/src/svgrid.grand-total.test.ts +188 -0
- package/src/svgrid.group-display-mode.test.ts +171 -0
- package/src/svgrid.group-footers.test.ts +121 -0
- package/src/svgrid.group-pagination.test.ts +153 -0
- package/src/svgrid.tree-data.test.ts +186 -0
- package/src/svgriddropdown.async-panel.svelte.test.ts +195 -0
- package/src/themes/index.ts +42 -0
- package/src/tree-edit.test.ts +4 -4
- package/src/tree-row-model.test.ts +168 -0
- package/src/ui-localization.test.ts +113 -0
- package/themes/ag-alpine.css +22 -0
- package/themes/antd.css +22 -0
- package/themes/atlassian.css +22 -0
- package/themes/bootstrap.css +22 -0
- package/themes/carbon.css +22 -0
- package/themes/catppuccin.css +22 -0
- package/themes/dracula.css +22 -0
- package/themes/ember.css +22 -0
- package/themes/excel.css +22 -0
- package/themes/fluent.css +22 -0
- package/themes/github.css +22 -0
- package/themes/linear.css +22 -0
- package/themes/material.css +22 -0
- package/themes/nord.css +22 -0
- package/themes/notion.css +22 -0
- package/themes/salesforce.css +22 -0
- package/themes/sap.css +22 -0
- package/themes/shadcn.css +22 -0
- package/themes/tailwind.css +22 -0
- package/themes/vercel.css +22 -0
package/src/SvGridSelect.svelte
CHANGED
|
@@ -38,6 +38,12 @@
|
|
|
38
38
|
loadOptions?: (query: string) => Promise<Row[]>
|
|
39
39
|
debounceMs?: number
|
|
40
40
|
loadingText?: string
|
|
41
|
+
/** Shown when the list is empty / a search matches nothing (localization). */
|
|
42
|
+
emptyText?: string
|
|
43
|
+
/** Placeholder in the panel's search box (localization). */
|
|
44
|
+
searchPlaceholder?: string
|
|
45
|
+
/** Accessible name for the panel's search box (localization). */
|
|
46
|
+
searchLabel?: string
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
let {
|
|
@@ -47,13 +53,14 @@
|
|
|
47
53
|
labelField,
|
|
48
54
|
value = $bindable<string | number | null>(null),
|
|
49
55
|
onChange,
|
|
50
|
-
onCommit,
|
|
51
|
-
onCancel,
|
|
52
56
|
placeholder = 'Select…',
|
|
53
57
|
searchable = true,
|
|
54
58
|
loadOptions,
|
|
55
59
|
debounceMs = 250,
|
|
56
60
|
loadingText = 'Loading…',
|
|
61
|
+
emptyText = 'No matches',
|
|
62
|
+
searchPlaceholder = 'Search…',
|
|
63
|
+
searchLabel = 'Search options',
|
|
57
64
|
disabled = false,
|
|
58
65
|
readonly = false,
|
|
59
66
|
label,
|
|
@@ -168,8 +175,8 @@
|
|
|
168
175
|
aria-expanded="true"
|
|
169
176
|
aria-controls={gs.panelId}
|
|
170
177
|
aria-activedescendant={gs.activeDescendant()}
|
|
171
|
-
aria-label=
|
|
172
|
-
placeholder=
|
|
178
|
+
aria-label={searchLabel}
|
|
179
|
+
placeholder={searchPlaceholder}
|
|
173
180
|
value={search}
|
|
174
181
|
oninput={(e) => { search = e.currentTarget.value; gs.active = 0; if (loadOptions) runLoad(search) }}
|
|
175
182
|
/>
|
|
@@ -205,7 +212,7 @@
|
|
|
205
212
|
{/each}
|
|
206
213
|
</div>
|
|
207
214
|
{:else}
|
|
208
|
-
<div class="sv-gs__empty" role="row">{loading ? loadingText :
|
|
215
|
+
<div class="sv-gs__empty" role="row">{loading ? loadingText : emptyText}</div>
|
|
209
216
|
{/each}
|
|
210
217
|
{#if loading && filtered.length}
|
|
211
218
|
<div class="sv-gs__empty" role="row" aria-live="polite">{loadingText}</div>
|
package/src/SvGroupCell.svelte
CHANGED
|
@@ -39,7 +39,12 @@
|
|
|
39
39
|
aria-label={meta.expanded ? 'Collapse group' : 'Expand group'}
|
|
40
40
|
onclick={() => onToggle(row)}
|
|
41
41
|
>
|
|
42
|
-
|
|
42
|
+
<!-- Same chevron the grid's own expanders and SvTree draw, so every
|
|
43
|
+
expander in the library looks identical. -->
|
|
44
|
+
<span class="sv-group-chev" class:open={meta.expanded} aria-hidden="true">
|
|
45
|
+
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor"
|
|
46
|
+
stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6" /></svg>
|
|
47
|
+
</span>
|
|
43
48
|
<span class="sv-group-key">{meta.key}</span>
|
|
44
49
|
{#if meta.loading}<span class="sv-group-load" aria-hidden="true">…</span>{/if}
|
|
45
50
|
</button>
|
|
@@ -76,7 +81,9 @@
|
|
|
76
81
|
cursor: pointer;
|
|
77
82
|
}
|
|
78
83
|
.sv-group-chev {
|
|
79
|
-
display: inline-
|
|
84
|
+
display: inline-flex;
|
|
85
|
+
align-items: center;
|
|
86
|
+
justify-content: center;
|
|
80
87
|
transition: transform 0.15s;
|
|
81
88
|
color: var(--sg-muted, #64748b);
|
|
82
89
|
}
|
package/src/SvKbd.svelte
CHANGED
package/src/SvList.svelte
CHANGED
package/src/SvMenubar.svelte
CHANGED
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
const resolvedDir = $derived(dir === 'ltr' || dir === 'rtl' ? dir : undefined)
|
|
42
42
|
const rtl = $derived(resolvedDir === 'rtl')
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
const triggerEls = $state<(HTMLButtonElement | null)[]>([])
|
|
45
45
|
let panelEl = $state<HTMLDivElement | null>(null)
|
|
46
46
|
let openIndex = $state(-1)
|
|
47
47
|
let activeIndex = $state(0)
|
package/src/SvMultiSelect.svelte
CHANGED
|
@@ -37,6 +37,12 @@
|
|
|
37
37
|
/** Debounce for remote search. Default 250ms. */
|
|
38
38
|
debounceMs?: number
|
|
39
39
|
loadingText?: string
|
|
40
|
+
/** Shown when the list is empty / a search matches nothing (localization). */
|
|
41
|
+
emptyText?: string
|
|
42
|
+
/** Placeholder in the panel's search box (localization). */
|
|
43
|
+
searchPlaceholder?: string
|
|
44
|
+
/** Accessible name for the panel's search box (localization). */
|
|
45
|
+
searchLabel?: string
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
let {
|
|
@@ -44,7 +50,6 @@
|
|
|
44
50
|
value = $bindable<ReadonlyArray<string | number>>([]),
|
|
45
51
|
onChange,
|
|
46
52
|
onCommit,
|
|
47
|
-
onCancel,
|
|
48
53
|
placeholder = 'Select…',
|
|
49
54
|
searchable = true,
|
|
50
55
|
maxTagCount = 3,
|
|
@@ -52,6 +57,9 @@
|
|
|
52
57
|
loadOptions,
|
|
53
58
|
debounceMs = 250,
|
|
54
59
|
loadingText = 'Loading…',
|
|
60
|
+
emptyText = 'No matches',
|
|
61
|
+
searchPlaceholder = 'Search…',
|
|
62
|
+
searchLabel = 'Search options',
|
|
55
63
|
disabled = false,
|
|
56
64
|
readonly = false,
|
|
57
65
|
label,
|
|
@@ -193,8 +201,8 @@
|
|
|
193
201
|
aria-expanded="true"
|
|
194
202
|
aria-controls={ms.panelId}
|
|
195
203
|
aria-activedescendant={ms.activeDescendant()}
|
|
196
|
-
aria-label=
|
|
197
|
-
placeholder=
|
|
204
|
+
aria-label={searchLabel}
|
|
205
|
+
placeholder={searchPlaceholder}
|
|
198
206
|
value={search}
|
|
199
207
|
oninput={(e) => { search = e.currentTarget.value; ms.active = 0; if (loadOptions) runLoad(search) }}
|
|
200
208
|
/>
|
|
@@ -224,7 +232,7 @@
|
|
|
224
232
|
<span class="sv-ms__label">{opt.label}</span>
|
|
225
233
|
</li>
|
|
226
234
|
{:else}
|
|
227
|
-
<li class="sv-ms__empty" aria-disabled="true">{loading ? loadingText :
|
|
235
|
+
<li class="sv-ms__empty" aria-disabled="true">{loading ? loadingText : emptyText}</li>
|
|
228
236
|
{/each}
|
|
229
237
|
{#if loading && filtered.length}
|
|
230
238
|
<li class="sv-ms__loading" aria-live="polite">{loadingText}</li>
|
package/src/SvOtpInput.svelte
CHANGED
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
const autoId = nextEditorId('sv-otp')
|
|
53
53
|
const uid = $derived(id ?? autoId)
|
|
54
54
|
const cells = $derived(otpCells(sanitizeOtp(value, length, numeric), length))
|
|
55
|
-
|
|
55
|
+
const inputs = $state<HTMLInputElement[]>([])
|
|
56
56
|
|
|
57
57
|
function commit(next: string) {
|
|
58
58
|
const clean = sanitizeOtp(next, length, numeric)
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
.sv-pw__meter { display: flex; gap: 4px; }
|
|
143
143
|
.sv-pw__bar { flex: 1; height: 4px; border-radius: 2px; background: var(--sg-border, #e2e8f0); transition: background 0.15s; }
|
|
144
144
|
.sv-pw__bar.on.is-lvl1 { background: var(--sg-danger, #dc2626); }
|
|
145
|
-
.sv-pw__bar.on.is-lvl2 { background: var(--sg-warning, #
|
|
145
|
+
.sv-pw__bar.on.is-lvl2 { background: var(--sg-warning, #d97706); }
|
|
146
146
|
.sv-pw__bar.on.is-lvl3 { background: #eab308; }
|
|
147
147
|
.sv-pw__bar.on.is-lvl4 { background: var(--sg-success, #16a34a); }
|
|
148
148
|
.sv-pw__strength { font-size: 11px; font-weight: 600; color: var(--sg-muted, #64748b); }
|
package/src/SvProgress.svelte
CHANGED
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
display: flex; align-items: center; gap: 10px; width: 100%;
|
|
76
76
|
}
|
|
77
77
|
.sv-prog--success { --_c: var(--sg-success, #16a34a); }
|
|
78
|
-
.sv-prog--warning { --_c: var(--sg-warning, #
|
|
78
|
+
.sv-prog--warning { --_c: var(--sg-warning, #d97706); }
|
|
79
79
|
.sv-prog--danger { --_c: var(--sg-danger, #dc2626); }
|
|
80
80
|
.sv-prog__track {
|
|
81
81
|
position: relative; flex: 1; overflow: hidden;
|
package/src/SvRating.svelte
CHANGED
package/src/SvResult.svelte
CHANGED
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
}
|
|
60
60
|
.sv-result--success { --_c: var(--sg-success, #16a34a); }
|
|
61
61
|
.sv-result--error { --_c: var(--sg-danger, #dc2626); }
|
|
62
|
-
.sv-result--warning { --_c: var(--sg-warning, #
|
|
62
|
+
.sv-result--warning { --_c: var(--sg-warning, #d97706); }
|
|
63
63
|
.sv-result--notfound { --_c: var(--sg-muted, #64748b); }
|
|
64
64
|
.sv-result__icon {
|
|
65
65
|
display: grid; place-items: center; width: 60px; height: 60px; border-radius: 50%; margin-bottom: 4px;
|
package/src/SvRichText.svelte
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* <SvRichText bind:value={html} placeholder="Write something…" />
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
-
import { nextEditorId } from './editor-contract'
|
|
22
|
+
import { nextEditorId, resolveMessages } from './editor-contract'
|
|
23
23
|
|
|
24
24
|
const DEFAULT_TOOLS: RichTextTool[] = [
|
|
25
25
|
'bold', 'italic', 'underline', 'strike', '|',
|
|
@@ -29,16 +29,27 @@
|
|
|
29
29
|
'link', 'clear', '|', 'undo', 'redo',
|
|
30
30
|
]
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
ul:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
/** Toolbar glyphs. Mode-independent and not language-dependent, so they stay
|
|
33
|
+
* fixed; the LABELS beside them are localizable via `messages`. */
|
|
34
|
+
const GLYPH: Record<Exclude<RichTextTool, '|'>, string> = {
|
|
35
|
+
bold: 'B', italic: 'I', underline: 'U', strike: 'S',
|
|
36
|
+
h1: 'H1', h2: 'H2', h3: 'H3', p: '¶',
|
|
37
|
+
ul: '•', ol: '1.', quote: '❝', code: '</>',
|
|
38
|
+
alignLeft: '≡', alignCenter: '≣', alignRight: '≡', link: '🔗',
|
|
39
|
+
clear: '⌫', undo: '↶', redo: '↷',
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** User-facing strings (localizable via `messages`). Every one is a tooltip +
|
|
43
|
+
* accessible name on a toolbar button, so leaving them hard-coded made the
|
|
44
|
+
* whole toolbar English-only for screen-reader users. */
|
|
45
|
+
type RichTextMessages = Record<Exclude<RichTextTool, '|'>, string> & { linkPrompt: string; toolbar: string }
|
|
46
|
+
const DEFAULT_MESSAGES: RichTextMessages = {
|
|
47
|
+
bold: 'Bold', italic: 'Italic', underline: 'Underline', strike: 'Strikethrough',
|
|
48
|
+
h1: 'Heading 1', h2: 'Heading 2', h3: 'Heading 3', p: 'Paragraph',
|
|
49
|
+
ul: 'Bullet list', ol: 'Numbered list', quote: 'Quote', code: 'Code block',
|
|
50
|
+
alignLeft: 'Align left', alignCenter: 'Align center', alignRight: 'Align right',
|
|
51
|
+
link: 'Link', clear: 'Clear formatting', undo: 'Undo', redo: 'Redo',
|
|
52
|
+
linkPrompt: 'Link URL', toolbar: 'Formatting',
|
|
42
53
|
}
|
|
43
54
|
|
|
44
55
|
type Props = {
|
|
@@ -52,6 +63,8 @@
|
|
|
52
63
|
/** Toolbar tools + order; use '|' for a separator. */
|
|
53
64
|
tools?: RichTextTool[]
|
|
54
65
|
ariaLabel?: string
|
|
66
|
+
/** Override any toolbar label / prompt (localization). */
|
|
67
|
+
messages?: Partial<RichTextMessages>
|
|
55
68
|
}
|
|
56
69
|
|
|
57
70
|
let {
|
|
@@ -63,8 +76,10 @@
|
|
|
63
76
|
minHeight = '160px',
|
|
64
77
|
tools = DEFAULT_TOOLS,
|
|
65
78
|
ariaLabel = 'Rich text editor',
|
|
79
|
+
messages,
|
|
66
80
|
}: Props = $props()
|
|
67
81
|
|
|
82
|
+
const M = $derived(resolveMessages(DEFAULT_MESSAGES, messages))
|
|
68
83
|
const uid = nextEditorId('sv-rt')
|
|
69
84
|
let editorEl = $state<HTMLDivElement | null>(null)
|
|
70
85
|
let focused = $state(false)
|
|
@@ -75,6 +90,10 @@
|
|
|
75
90
|
// (writing innerHTML while focused would collapse the caret).
|
|
76
91
|
$effect(() => {
|
|
77
92
|
const v = value ?? ''
|
|
93
|
+
// A contenteditable owns its own DOM: letting Svelte re-render the body
|
|
94
|
+
// would wipe the caret mid-typing, so the value is pushed in by hand and
|
|
95
|
+
// only while the editor is unfocused.
|
|
96
|
+
// eslint-disable-next-line svelte/no-dom-manipulating
|
|
78
97
|
if (editorEl && !focused && editorEl.innerHTML !== v) editorEl.innerHTML = v
|
|
79
98
|
if (editorEl) empty = editorEl.textContent!.trim().length === 0
|
|
80
99
|
})
|
|
@@ -109,14 +128,14 @@
|
|
|
109
128
|
ul: () => exec('insertUnorderedList'), ol: () => exec('insertOrderedList'),
|
|
110
129
|
quote: () => exec('formatBlock', 'BLOCKQUOTE'), code: () => exec('formatBlock', 'PRE'),
|
|
111
130
|
alignLeft: () => exec('justifyLeft'), alignCenter: () => exec('justifyCenter'), alignRight: () => exec('justifyRight'),
|
|
112
|
-
link: () => { const url = typeof prompt === 'function' ? prompt(
|
|
131
|
+
link: () => { const url = typeof prompt === 'function' ? prompt(M.linkPrompt, 'https://') : null; if (url) exec('createLink', url) },
|
|
113
132
|
clear: () => exec('removeFormat'), undo: () => exec('undo'), redo: () => exec('redo'),
|
|
114
133
|
}
|
|
115
134
|
</script>
|
|
116
135
|
|
|
117
136
|
<div class="sv-rt" class:is-disabled={disabled} class:is-readonly={readonly}>
|
|
118
137
|
{#if !readonly}
|
|
119
|
-
<div class="sv-rt__toolbar" role="toolbar" aria-label=
|
|
138
|
+
<div class="sv-rt__toolbar" role="toolbar" aria-label={M.toolbar} aria-controls={uid}>
|
|
120
139
|
{#each tools as t, i (i)}
|
|
121
140
|
{#if t === '|'}
|
|
122
141
|
<span class="sv-rt__sep" aria-hidden="true"></span>
|
|
@@ -126,13 +145,13 @@
|
|
|
126
145
|
class="sv-rt__btn"
|
|
127
146
|
class:is-on={activeState[t]}
|
|
128
147
|
data-tool={t}
|
|
129
|
-
title={
|
|
130
|
-
aria-label={
|
|
148
|
+
title={M[t]}
|
|
149
|
+
aria-label={M[t]}
|
|
131
150
|
aria-pressed={activeState[t] ? true : undefined}
|
|
132
151
|
{disabled}
|
|
133
152
|
onmousedown={(e) => e.preventDefault()}
|
|
134
153
|
onclick={run[t]}
|
|
135
|
-
>{
|
|
154
|
+
>{GLYPH[t]}</button>
|
|
136
155
|
{/if}
|
|
137
156
|
{/each}
|
|
138
157
|
</div>
|
|
@@ -53,8 +53,9 @@
|
|
|
53
53
|
</script>
|
|
54
54
|
|
|
55
55
|
<SvField id={uid} {label} {hint} {error} {required} {dir}>
|
|
56
|
+
<!-- role="switch" arrives via switchProps(), which the a11y pass cannot see through -->
|
|
57
|
+
<!-- svelte-ignore a11y_role_supports_aria_props_implicit -->
|
|
56
58
|
<button
|
|
57
|
-
role="switch"
|
|
58
59
|
class="sv-switch sv-switch--{size}"
|
|
59
60
|
class:is-on={checked}
|
|
60
61
|
class:is-readonly={readonly}
|
package/src/SvToaster.svelte
CHANGED
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
|
|
136
136
|
:global(.sv-toast--success) { --_accent: var(--sg-success, #16a34a); }
|
|
137
137
|
:global(.sv-toast--error) { --_accent: var(--sg-danger, #dc2626); }
|
|
138
|
-
:global(.sv-toast--warning) { --_accent: var(--sg-warning, #
|
|
138
|
+
:global(.sv-toast--warning) { --_accent: var(--sg-warning, #d97706); }
|
|
139
139
|
:global(.sv-toast--info) { --_accent: var(--sg-accent, #2563eb); }
|
|
140
140
|
|
|
141
141
|
:global(.sv-toast__icon) { flex: none; width: 18px; height: 18px; display: grid; place-items: center; color: var(--_accent); font-weight: 700; }
|
package/src/SvTour.svelte
CHANGED
|
@@ -156,6 +156,10 @@
|
|
|
156
156
|
>
|
|
157
157
|
{#if step.image}
|
|
158
158
|
<div class="sv-tour__media">
|
|
159
|
+
<!-- A step's `image` is either a URL (rendered as an img above) or
|
|
160
|
+
inline SVG markup the app author wrote for its own tour - never
|
|
161
|
+
user input, so rendering it as markup is the intent. -->
|
|
162
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
159
163
|
{#if /^(https?:|data:|\/)/.test(step.image)}<img class="sv-tour__img" src={step.image} alt="" />{:else}{@html step.image}{/if}
|
|
160
164
|
</div>
|
|
161
165
|
{/if}
|
package/src/SvTree.svelte
CHANGED
|
@@ -48,6 +48,11 @@
|
|
|
48
48
|
/** Render a built-in search box that drives the filter. */
|
|
49
49
|
searchable?: boolean
|
|
50
50
|
searchPlaceholder?: string
|
|
51
|
+
/** Placeholder row shown under a lazy node while its children load
|
|
52
|
+
* (localization). */
|
|
53
|
+
loadingText?: string
|
|
54
|
+
/** Accessible name for the search box's clear button (localization). */
|
|
55
|
+
clearSearchLabel?: string
|
|
51
56
|
/** Sort siblings by label ('asc'/'desc') or a custom comparator. */
|
|
52
57
|
sort?: 'asc' | 'desc' | ((a: TreeNode, b: TreeNode) => number)
|
|
53
58
|
/** Allow inline rename (double-click or F2). Fires onRename. */
|
|
@@ -77,6 +82,8 @@
|
|
|
77
82
|
filter,
|
|
78
83
|
searchable = false,
|
|
79
84
|
searchPlaceholder = 'Search...',
|
|
85
|
+
loadingText = 'Loading...',
|
|
86
|
+
clearSearchLabel = 'Clear search',
|
|
80
87
|
sort,
|
|
81
88
|
editable = false,
|
|
82
89
|
onRename,
|
|
@@ -173,7 +180,7 @@
|
|
|
173
180
|
if (!node?.lazy || loaded.has(id) || loadingIds.has(id)) return
|
|
174
181
|
loadingIds = new Set(loadingIds).add(id)
|
|
175
182
|
// A placeholder row shows immediately while the promise resolves.
|
|
176
|
-
loaded = new Map(loaded).set(id, [{ id: `${id}__loading`, label:
|
|
183
|
+
loaded = new Map(loaded).set(id, [{ id: `${id}__loading`, label: loadingText, disabled: true }])
|
|
177
184
|
try {
|
|
178
185
|
const kids = await loadChildren(node)
|
|
179
186
|
loaded = new Map(loaded).set(id, kids)
|
|
@@ -268,7 +275,7 @@
|
|
|
268
275
|
<div class="sv-tree__search">
|
|
269
276
|
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="11" cy="11" r="7" /><path d="m21 21-4.3-4.3" /></svg>
|
|
270
277
|
<input class="sv-tree__search-input" type="text" placeholder={searchPlaceholder} bind:value={query} aria-label={searchPlaceholder} />
|
|
271
|
-
{#if query}<button type="button" class="sv-tree__search-clear" aria-label=
|
|
278
|
+
{#if query}<button type="button" class="sv-tree__search-clear" aria-label={clearSearchLabel} onclick={() => (query = '')}>×</button>{/if}
|
|
272
279
|
</div>
|
|
273
280
|
{/if}
|
|
274
281
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
package/src/SvTreeSelect.svelte
CHANGED
|
@@ -36,8 +36,6 @@
|
|
|
36
36
|
nodes,
|
|
37
37
|
value = $bindable<string | number | null>(null),
|
|
38
38
|
onChange,
|
|
39
|
-
onCommit,
|
|
40
|
-
onCancel,
|
|
41
39
|
placeholder = 'Select…',
|
|
42
40
|
showPath = false,
|
|
43
41
|
expandedIds = [],
|
|
@@ -186,7 +184,11 @@
|
|
|
186
184
|
{...ts.itemProps(i)}
|
|
187
185
|
>
|
|
188
186
|
{#if row.hasChildren}
|
|
189
|
-
|
|
187
|
+
<!-- Same chevron as SvTree and the grid's expanders. -->
|
|
188
|
+
<span class="sv-ts__chev" class:is-open={row.expanded} aria-hidden="true">
|
|
189
|
+
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor"
|
|
190
|
+
stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6" /></svg>
|
|
191
|
+
</span>
|
|
190
192
|
{:else}
|
|
191
193
|
<span class="sv-ts__chev sv-ts__chev--leaf" aria-hidden="true"></span>
|
|
192
194
|
{/if}
|
|
@@ -226,7 +228,7 @@
|
|
|
226
228
|
:global(.sv-ts__row.is-active) { background: var(--sg-row-hover-bg, #f1f5f9); }
|
|
227
229
|
:global(.sv-ts__row.is-selected) { color: var(--sg-accent, #2563eb); font-weight: 600; }
|
|
228
230
|
:global(.sv-ts__row.is-disabled) { opacity: 0.5; cursor: not-allowed; }
|
|
229
|
-
:global(.sv-ts__chev) { width: 14px; flex: none; color: var(--sg-muted, #64748b); transition: transform 0.12s;
|
|
231
|
+
:global(.sv-ts__chev) { width: 14px; flex: none; display: inline-flex; align-items: center; justify-content: center; color: var(--sg-muted, #64748b); transition: transform 0.12s; }
|
|
230
232
|
:global(.sv-ts__chev.is-open) { transform: rotate(90deg); }
|
|
231
233
|
:global(.sv-ts__chev--leaf) { visibility: hidden; }
|
|
232
234
|
:global(.sv-ts__label) { flex: 1; white-space: nowrap; }
|
package/src/ai.test.ts
CHANGED
package/src/build-api.ts
CHANGED
|
@@ -2,52 +2,12 @@
|
|
|
2
2
|
// reading/writing controller state via the `ctx` handle; the reactive core
|
|
3
3
|
// ($state/$derived/$effect) stays in the controller.
|
|
4
4
|
import {
|
|
5
|
-
applyExcelFilter,
|
|
6
|
-
normalizeForFilter,
|
|
7
|
-
createColumnVirtualizer,
|
|
8
|
-
createCoreRowModel,
|
|
9
|
-
createExpandedRowModel,
|
|
10
|
-
createFilteredRowModel,
|
|
11
|
-
createGroupedRowModel,
|
|
12
|
-
createPaginatedRowModel,
|
|
13
|
-
createSvelteVirtualizer,
|
|
14
|
-
createSortedRowModel,
|
|
15
|
-
createSvGrid,
|
|
16
|
-
filterFns,
|
|
17
|
-
getGridCellA11yProps,
|
|
18
|
-
getGridCellDomId,
|
|
19
|
-
getGridHeaderA11yProps,
|
|
20
|
-
getGridRootA11yProps,
|
|
21
|
-
getGridRowA11yProps,
|
|
22
|
-
parseEditorValue,
|
|
23
|
-
normalizeEditorOptions,
|
|
24
|
-
sortFns,
|
|
25
|
-
tableFeatures,
|
|
26
|
-
rowSortingFeature,
|
|
27
|
-
columnFilteringFeature,
|
|
28
|
-
columnGroupingFeature,
|
|
29
|
-
type CellContext,
|
|
30
|
-
type EditorContext,
|
|
31
|
-
type CellEditorOption,
|
|
32
|
-
type CellEditorType,
|
|
33
|
-
type CellFormatter,
|
|
34
|
-
type CellFormatConfig,
|
|
35
|
-
type Column,
|
|
36
|
-
type ColumnDef,
|
|
37
|
-
type Row,
|
|
38
5
|
type RowData,
|
|
39
6
|
type SvGridApi,
|
|
40
7
|
type TableFeatures,
|
|
41
8
|
} from "./index";
|
|
42
9
|
import "./sv-grid-scrollbar";
|
|
43
10
|
import { untrack } from "svelte";
|
|
44
|
-
import type { Snippet } from "svelte";
|
|
45
|
-
import { getKeyboardIntent, getNextActiveCell } from "./keyboard";
|
|
46
|
-
import {
|
|
47
|
-
formatNumericWithConfig,
|
|
48
|
-
getDateFormatter,
|
|
49
|
-
resolveDatePattern,
|
|
50
|
-
} from "./cell-formatting";
|
|
51
11
|
// The export serializers load lazily (their own chunk) so export-format.ts stays
|
|
52
12
|
// out of the base bundle - it is fetched only when an export/copy method is
|
|
53
13
|
// actually invoked (an explicit, already-async user action).
|
|
@@ -57,77 +17,12 @@ import type {
|
|
|
57
17
|
GridExportOptions,
|
|
58
18
|
GridClipboardOptions,
|
|
59
19
|
} from "./export-format";
|
|
60
|
-
import {
|
|
61
|
-
RenderSnippetConfig,
|
|
62
|
-
RenderComponentConfig,
|
|
63
|
-
} from "./render-component";
|
|
64
|
-
import { buildFillPattern } from "./fill-patterns";
|
|
65
|
-
import { buildSparkline, toSparklineValues } from "./sparkline";
|
|
66
|
-
import {
|
|
67
|
-
resolveCellFormat,
|
|
68
|
-
computeColumnStat,
|
|
69
|
-
formatsNeedingStats,
|
|
70
|
-
type ColumnStat,
|
|
71
|
-
type ResolvedCellFormat,
|
|
72
|
-
} from "./conditional-formatting";
|
|
73
|
-
import SvGridDropdown from "./SvGridDropdown.svelte";
|
|
74
20
|
import type {
|
|
75
|
-
Props,
|
|
76
|
-
SelectionPoint,
|
|
77
|
-
SelectionRange,
|
|
78
|
-
CellEditState,
|
|
79
21
|
FilterOperator,
|
|
80
|
-
FilterOption,
|
|
81
|
-
MenuPosition,
|
|
82
22
|
} from "./SvGrid.types";
|
|
83
23
|
import {
|
|
84
|
-
cfTextStyle,
|
|
85
|
-
fmtStat,
|
|
86
|
-
getCellKey,
|
|
87
|
-
resolveClassList,
|
|
88
|
-
toDateInputValue,
|
|
89
|
-
toDateTimeLocalInputValue,
|
|
90
|
-
getEditableInputValue,
|
|
91
|
-
getEditorInputType,
|
|
92
|
-
toValueArray,
|
|
93
|
-
getOptionLabel,
|
|
94
|
-
getOptionColor,
|
|
95
|
-
colorfulChipStyle,
|
|
96
|
-
getEditorClass,
|
|
97
|
-
asDate,
|
|
98
|
-
clampMenuX,
|
|
99
|
-
cssEscape,
|
|
100
|
-
rawToNumber,
|
|
101
|
-
formatFacetNumber,
|
|
102
|
-
formatFacetDate,
|
|
103
|
-
} from "./SvGrid.helpers";
|
|
104
|
-
import { createClipboard } from "./clipboard";
|
|
105
|
-
import {
|
|
106
|
-
filterOperatorOptions,
|
|
107
|
-
fallbackOperatorOption,
|
|
108
|
-
TEXT_OPERATORS,
|
|
109
|
-
NUMBER_OPERATORS,
|
|
110
|
-
DATE_OPERATORS,
|
|
111
|
-
CHECKBOX_OPERATORS,
|
|
112
|
-
operatorOption,
|
|
113
|
-
operatorsForColumn,
|
|
114
|
-
defaultOperatorFor,
|
|
115
|
-
operatorLabelFor,
|
|
116
|
-
} from "./filter-operators";
|
|
117
|
-
import {
|
|
118
|
-
type FacetBucket,
|
|
119
|
-
isBucketableColumn,
|
|
120
|
-
buildBuckets,
|
|
121
|
-
isInBucket,
|
|
122
|
-
} from "./facet-buckets";
|
|
123
|
-
import {
|
|
124
|
-
getColumnBaseValue,
|
|
125
24
|
isGroupRow,
|
|
126
|
-
toolPanelHeaderLabel,
|
|
127
|
-
formatSummaryNumeric,
|
|
128
25
|
getColumnAlign,
|
|
129
|
-
getPinnedCellValue,
|
|
130
|
-
getColumnAccessorValue,
|
|
131
26
|
columnDefMatchesId,
|
|
132
27
|
} from "./cell-values";
|
|
133
28
|
|
|
@@ -455,6 +350,21 @@ export function createGridApi<
|
|
|
455
350
|
clearFilter(columnId) {
|
|
456
351
|
ctx.clearColumnFilter(columnId);
|
|
457
352
|
},
|
|
353
|
+
refreshEditorOptions(columnId) {
|
|
354
|
+
// Async `editorOptions` are cached per column (or per column+row for a
|
|
355
|
+
// per-row source) so opening an editor twice does not refetch. This
|
|
356
|
+
// drops those entries, so the next render re-runs the source - use it
|
|
357
|
+
// when the underlying list changed server-side.
|
|
358
|
+
if (columnId == null) {
|
|
359
|
+
ctx.asyncEditorOptions = {};
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const next = { ...ctx.asyncEditorOptions };
|
|
363
|
+
for (const key of Object.keys(next)) {
|
|
364
|
+
if (key === columnId || key.startsWith(`${columnId}::`)) delete next[key];
|
|
365
|
+
}
|
|
366
|
+
ctx.asyncEditorOptions = next;
|
|
367
|
+
},
|
|
458
368
|
clearAllFilters() {
|
|
459
369
|
// See `setFilter` for why the read-modify-write is untracked.
|
|
460
370
|
untrack(() => {
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
type CellEditorContext,
|
|
10
10
|
} from './editor-registry'
|
|
11
11
|
|
|
12
|
+
// onCommitAndMove / onRequestClose / inCell are the hooks the grid injects at
|
|
13
|
+
// mount; the registry tests here never exercise them.
|
|
12
14
|
function ctx(overrides: Partial<CellEditorContext> = {}): CellEditorContext {
|
|
13
15
|
return {
|
|
14
16
|
value: null,
|
|
@@ -18,7 +20,7 @@ function ctx(overrides: Partial<CellEditorContext> = {}): CellEditorContext {
|
|
|
18
20
|
onCommit: () => {},
|
|
19
21
|
onCancel: () => {},
|
|
20
22
|
...overrides,
|
|
21
|
-
}
|
|
23
|
+
} as CellEditorContext
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
afterEach(() => {
|
|
@@ -158,7 +158,7 @@ describe('getDateFormatter', () => {
|
|
|
158
158
|
|
|
159
159
|
describe('formatNumericWithConfig - number', () => {
|
|
160
160
|
it('formats a basic integer with default locale', () => {
|
|
161
|
-
expect(formatNumericWithConfig(1234, { type: 'number' })).toMatch(/1[
|
|
161
|
+
expect(formatNumericWithConfig(1234, { type: 'number' })).toMatch(/1[.,]?234/)
|
|
162
162
|
})
|
|
163
163
|
|
|
164
164
|
it('respects locale grouping and decimal separators', () => {
|