@svgrid/grid 2.6.21 → 2.6.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/CHANGELOG.md +62 -0
- package/README.md +22 -0
- package/dist/GridMenus.svelte +201 -18
- package/dist/SvAutoComplete.svelte +4 -0
- package/dist/SvComboBox.svelte +5 -1
- package/dist/SvDateRangeInput.svelte +25 -3
- package/dist/SvDropDownList.svelte +4 -0
- package/dist/SvForm.svelte +8 -8
- package/dist/SvGrid.controller.svelte.d.ts +61 -9
- package/dist/SvGrid.controller.svelte.js +363 -123
- package/dist/SvGrid.css +222 -1
- package/dist/SvGrid.helpers.d.ts +55 -0
- package/dist/SvGrid.helpers.js +83 -0
- package/dist/SvGrid.svelte +255 -56
- package/dist/SvGrid.types.d.ts +146 -1
- package/dist/SvGridCellEditor.svelte +99 -17
- package/dist/SvGridSelect.svelte +6 -2
- package/dist/SvGridSelect.svelte.d.ts +1 -1
- package/dist/SvListBox.svelte +8 -0
- package/dist/SvMultiSelect.svelte +6 -2
- package/dist/SvMultiSelect.svelte.d.ts +1 -1
- package/dist/SvNumberInput.svelte +4 -0
- package/dist/SvRichCell.svelte +125 -0
- package/dist/SvRichCell.svelte.d.ts +15 -0
- package/dist/SvTextArea.svelte +5 -1
- package/dist/SvTreeSelect.svelte +6 -2
- package/dist/SvTreeSelect.svelte.d.ts +1 -1
- package/dist/builtin-editors.d.ts +2 -2
- package/dist/builtin-editors.js +19 -3
- package/dist/cdn/GridMenus-BzWXQjv3.js +600 -0
- package/dist/cdn/GridMenus-xr_rHx8F.js +601 -0
- package/dist/cdn/SvDateRangeInput-CaOuMs8O.js +198 -0
- package/dist/cdn/SvDateRangeInput-DMLKmGEc.js +199 -0
- package/dist/cdn/SvDateTimePicker-CCbDZNZB.js +816 -0
- package/dist/cdn/SvDateTimePicker-sonaH0oh.js +812 -0
- package/dist/cdn/SvGridCellEditor-BAPSC7EL.js +550 -0
- package/dist/cdn/SvGridCellEditor-BwzmUWtL.js +549 -0
- package/dist/cdn/column-resize-DsfNXMom.js +102 -0
- package/dist/cdn/date-format-BNii4zeD.js +1393 -0
- package/dist/cdn/date-format-BnnHlqGw.js +1395 -0
- package/dist/cdn/{editor-registry-CiI0xlKg.js → editor-registry-BhgE3S84.js} +51 -25
- package/dist/cdn/row-resize-BRcimkUT.js +95 -0
- package/dist/cdn/{src-DBel9wRZ.js → src-DGo7IHug.js} +10017 -9488
- package/dist/cdn/{src-BYq-qyrp.js → src-skQN5eqh.js} +7829 -7300
- package/dist/cdn/svgrid.js +14 -10
- package/dist/cdn/svgrid.svelte-external.js +14 -10
- package/dist/cell-values.d.ts +16 -0
- package/dist/cell-values.js +28 -0
- package/dist/clipboard.d.ts +22 -0
- package/dist/clipboard.js +405 -27
- package/dist/column-groups.js +1 -1
- package/dist/column-resize.d.ts +46 -0
- package/dist/column-resize.js +205 -0
- package/dist/columns.d.ts +0 -3
- package/dist/columns.js +0 -57
- package/dist/core.d.ts +19 -4
- package/dist/core.js +460 -119
- package/dist/editing.js +12 -7
- package/dist/editors/cell-editors.d.ts +11 -0
- package/dist/editors/cell-editors.js +18 -1
- package/dist/filtering/excel-filters.js +28 -0
- package/dist/grid-messages.d.ts +15 -0
- package/dist/grid-messages.js +15 -0
- package/dist/group-display.d.ts +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +10 -0
- package/dist/list-option.d.ts +6 -0
- package/dist/menus.js +41 -8
- package/dist/row-resize.d.ts +11 -0
- package/dist/row-resize.js +7 -1
- package/dist/sanitize-html.d.ts +37 -0
- package/dist/sanitize-html.js +234 -0
- package/dist/selection-bar-view.svelte.d.ts +25 -0
- package/dist/selection-bar-view.svelte.js +13 -0
- package/dist/selection.d.ts +2 -1
- package/dist/selection.js +133 -8
- package/dist/spreadsheet.d.ts +1 -1
- package/dist/spreadsheet.js +1 -1
- package/package.json +1 -1
- package/src/GridMenus.svelte +201 -18
- package/src/SvAutoComplete.svelte +4 -0
- package/src/SvComboBox.svelte +5 -1
- package/src/SvDateRangeInput.svelte +25 -3
- package/src/SvDropDownList.svelte +4 -0
- package/src/SvForm.svelte +8 -8
- package/src/SvGrid.controller.svelte.ts +404 -133
- package/src/SvGrid.css +222 -1
- package/src/SvGrid.helpers.ts +87 -0
- package/src/SvGrid.svelte +255 -56
- package/src/SvGrid.types.ts +153 -1
- package/src/SvGridCellEditor.svelte +99 -17
- package/src/SvGridSelect.svelte +6 -2
- package/src/SvListBox.svelte +8 -0
- package/src/SvMultiSelect.svelte +6 -2
- package/src/SvNumberInput.svelte +4 -0
- package/src/SvRichCell.svelte +125 -0
- package/src/SvTextArea.svelte +5 -1
- package/src/SvTreeSelect.svelte +6 -2
- package/src/builtin-editors.test.ts +18 -1
- package/src/builtin-editors.ts +19 -3
- package/src/cell-values.ts +30 -0
- package/src/clipboard.test.ts +93 -20
- package/src/clipboard.ts +433 -33
- package/src/column-groups.ts +1 -1
- package/src/column-resize.test.ts +381 -0
- package/src/column-resize.ts +227 -0
- package/src/columns.test.ts +0 -103
- package/src/columns.ts +0 -58
- package/src/core.aggregate.test.ts +134 -0
- package/src/core.filter.test.ts +156 -0
- package/src/core.grouping.test.ts +146 -0
- package/src/core.row-shape.test.ts +119 -0
- package/src/core.rowmodel-cache.test.ts +121 -0
- package/src/core.sort.test.ts +293 -0
- package/src/core.ts +516 -119
- package/src/date-string-column.test.ts +87 -0
- package/src/editing.test.ts +25 -0
- package/src/editing.ts +12 -9
- package/src/editor-block-prop.test.ts +132 -0
- package/src/editors/cell-editors.ts +27 -1
- package/src/filtering/excel-filters.ts +30 -0
- package/src/filtering/normalize-fast-path.test.ts +104 -0
- package/src/grid-messages.ts +34 -0
- package/src/group-display.ts +1 -1
- package/src/index.ts +25 -1
- package/src/list-option.ts +6 -0
- package/src/menus.test.ts +43 -0
- package/src/menus.ts +38 -8
- package/src/move-cells.test.ts +850 -0
- package/src/number-editor-literal.test.ts +84 -0
- package/src/number-editor.grid.test.ts +160 -0
- package/src/resize-props.test.ts +361 -0
- package/src/row-resize.test.ts +31 -0
- package/src/row-resize.ts +21 -3
- package/src/sanitize-html.test.ts +182 -0
- package/src/sanitize-html.ts +235 -0
- package/src/selection-bar-view.svelte.ts +36 -0
- package/src/selection.test.ts +157 -0
- package/src/selection.ts +135 -8
- package/src/spreadsheet.ts +1 -1
- package/src/svgrid.filter-menu-listbox.svelte.test.ts +56 -10
- package/src/svgrid.in-suggest-snapshot.svelte.test.ts +91 -0
- package/src/svgrid.menu-scroll-close.test.ts +204 -0
- package/src/svgrid.selection-bar-seam.test.ts +185 -0
- package/src/svgrid.upsell-license.test.ts +117 -0
- package/dist/cdn/GridMenus-BfTAKn84.js +0 -488
- package/dist/cdn/GridMenus-C3bJd7w8.js +0 -489
- package/dist/cdn/SvDateTimePicker-CHnUkWcH.js +0 -2206
- package/dist/cdn/SvDateTimePicker-CRQH_U7E.js +0 -2201
- package/dist/cdn/SvGridCellEditor-BGUCzuPB.js +0 -523
- package/dist/cdn/SvGridCellEditor-G8IMWIws.js +0 -522
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,68 @@ Releases above 1.2.3 are generated from the commit subjects in each release tag
|
|
|
4
4
|
(`node tools/build-changelog.mjs`), so they describe what changed rather than
|
|
5
5
|
reading as polished release notes. Entries from 1.2.3 down are hand-written.
|
|
6
6
|
|
|
7
|
+
## 2.6.21
|
|
8
|
+
|
|
9
|
+
_2026-08-31_
|
|
10
|
+
|
|
11
|
+
- mobile fixes and 2 more property shortcuts
|
|
12
|
+
- Add a stars scorecard, drop the misleading downloads badge, add a code of conduct
|
|
13
|
+
- Make the MCP discovery tools usable, and stop the twice-daily version churn
|
|
14
|
+
|
|
15
|
+
## 2.6.18
|
|
16
|
+
|
|
17
|
+
_2026-08-29_
|
|
18
|
+
|
|
19
|
+
- Ship @svgrid/mcp as 2.6.4 after npm stalled the 2.6.3 publish
|
|
20
|
+
|
|
21
|
+
## 2.6.17
|
|
22
|
+
|
|
23
|
+
_2026-08-29_
|
|
24
|
+
|
|
25
|
+
- mobile demo fixes
|
|
26
|
+
|
|
27
|
+
## 2.6.13
|
|
28
|
+
|
|
29
|
+
_2026-08-28_
|
|
30
|
+
|
|
31
|
+
- mobile demos fixes
|
|
32
|
+
- Update image width attributes in README.md
|
|
33
|
+
- Enhance README with SvGrid props details
|
|
34
|
+
|
|
35
|
+
## 2.6.12
|
|
36
|
+
|
|
37
|
+
_2026-08-27_
|
|
38
|
+
|
|
39
|
+
- Advance the website gitlink to today's SEO commit
|
|
40
|
+
|
|
41
|
+
## 2.6.10
|
|
42
|
+
|
|
43
|
+
_2026-08-26_
|
|
44
|
+
|
|
45
|
+
- chore: untrack Claude Code auto-memory leaked into the repo
|
|
46
|
+
- chore: untrack maintainer-only scripts in tools/
|
|
47
|
+
- small fixes
|
|
48
|
+
|
|
49
|
+
## 2.6.9
|
|
50
|
+
|
|
51
|
+
_2026-08-25_
|
|
52
|
+
|
|
53
|
+
- SvelteKit with SvGrid Quick Start template
|
|
54
|
+
- Fix the red CI step, and give the SvelteKit guide a runnable example
|
|
55
|
+
- Correct the CSV-as-paid claim and the stale testing numbers in docs
|
|
56
|
+
|
|
57
|
+
## 2.6.8
|
|
58
|
+
|
|
59
|
+
_2026-08-25_
|
|
60
|
+
|
|
61
|
+
- Server-render grid rows, and a SvelteKit tutorial
|
|
62
|
+
|
|
63
|
+
## 2.6.7
|
|
64
|
+
|
|
65
|
+
_2026-08-25_
|
|
66
|
+
|
|
67
|
+
- Headless subpath, lazy cell editor, row-drag fixes, MIT MCP
|
|
68
|
+
|
|
7
69
|
## 2.6.5
|
|
8
70
|
|
|
9
71
|
_2026-08-24_
|
package/README.md
CHANGED
|
@@ -165,6 +165,28 @@ on the export- and analytics-heavy features teams tend to need last:
|
|
|
165
165
|
|
|
166
166
|
OSS projects receive an Enterprise key free. See [Pricing](https://svgrid.com/pricing/).
|
|
167
167
|
|
|
168
|
+
## Also in the box
|
|
169
|
+
|
|
170
|
+
The grid is the hero, but it is not everything in the package. The component suite below already ships
|
|
171
|
+
inside `@svgrid/grid`; the tools after it install separately and are entirely optional.
|
|
172
|
+
|
|
173
|
+
- **[SvGrid UI](https://svgrid.com/docs/help/ui-components/index/)** (MIT) - 84 Svelte 5 components
|
|
174
|
+
already inside this package, no extra install: `SvButton`, `SvCalendar`, `SvDateTimePicker`,
|
|
175
|
+
`SvDockLayout`, `SvToaster`, selects, dialogs, split panes. Import them the same way you import
|
|
176
|
+
`SvGrid`. If you would rather own and edit the source, `npx @svgrid/ui try calendar` previews a
|
|
177
|
+
component and copies it into your project, shadcn-style.
|
|
178
|
+
- **[`@svgrid/grid-wc`](https://www.npmjs.com/package/@svgrid/grid-wc)** (MIT) - the same grid as a
|
|
179
|
+
`<sv-grid>` custom element for React, Vue, Angular, or plain HTML. No build step.
|
|
180
|
+
- **[`@svgrid/mcp`](https://www.npmjs.com/package/@svgrid/mcp)** (MIT) - MCP server that checks the code
|
|
181
|
+
your AI writes against the real API surface, with version-pinned docs and every demo source.
|
|
182
|
+
- **[`@svgrid/migrate`](https://www.npmjs.com/package/@svgrid/migrate)** (MIT) - codemod that ports a
|
|
183
|
+
`svelte-headless-table` or TanStack Table v9 component to SvGrid, including the shadcn-svelte data
|
|
184
|
+
table.
|
|
185
|
+
- **[`@svgrid/sv`](https://www.npmjs.com/package/@svgrid/sv)** (MIT) - `npx sv add @svgrid` add-on for
|
|
186
|
+
the Svelte CLI.
|
|
187
|
+
- **[SvGrid Studio](https://svgrid.com/docs/studio/)** (commercial) - visual designer that generates a
|
|
188
|
+
SvelteKit CRUD app from a database schema: screens, forms, API routes, auth. Code you own, no runtime.
|
|
189
|
+
|
|
168
190
|
## Comparison
|
|
169
191
|
|
|
170
192
|
| | SvGrid | AG Grid Community | TanStack Table |
|
package/dist/GridMenus.svelte
CHANGED
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
} from "./SvGrid.types";
|
|
17
17
|
import {
|
|
18
18
|
getEditorInputType,
|
|
19
|
+
usesRichDateFilter,
|
|
20
|
+
toIsoDateLocal,
|
|
21
|
+
fromIsoDateLocal,
|
|
19
22
|
} from "./SvGrid.helpers";
|
|
20
23
|
|
|
21
24
|
import type { SvGridController } from "./SvGrid.controller.svelte";
|
|
@@ -72,11 +75,45 @@
|
|
|
72
75
|
// anything OUTSIDE them scrolls, or on resize. Scrolling the menu's own body
|
|
73
76
|
// or its facet list has to keep it open - hence onScrollOutside rather than a
|
|
74
77
|
// bare capture-phase listener.
|
|
78
|
+
//
|
|
79
|
+
// The cell context menu and the comment editor were missing from this gate.
|
|
80
|
+
// Both are positioned from a one-time cursor measurement and rendered
|
|
81
|
+
// `position: fixed`, so after a scroll they float over unrelated cells - the
|
|
82
|
+
// exact defect the effect exists to prevent. Their backdrop swallows a wheel
|
|
83
|
+
// over the grid, but it does not stop the PAGE scrolling underneath them, and
|
|
84
|
+
// it does not stop a programmatic scroll.
|
|
85
|
+
//
|
|
86
|
+
// The comment editor is dismissed by SAVING rather than discarding. Clicking
|
|
87
|
+
// the backdrop or pressing Escape throws the draft away, and that is fine -
|
|
88
|
+
// both are the user saying "away with this". A scroll is not a dismissal
|
|
89
|
+
// gesture, so it must not destroy text the user has typed.
|
|
75
90
|
$effect(() => {
|
|
76
|
-
if (
|
|
77
|
-
|
|
91
|
+
if (
|
|
92
|
+
!(
|
|
93
|
+
columnMenuFor ||
|
|
94
|
+
filterMenuFor ||
|
|
95
|
+
operatorMenuFor ||
|
|
96
|
+
chooseColumnsPos ||
|
|
97
|
+
contextMenuFor ||
|
|
98
|
+
commentEditFor
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
return;
|
|
102
|
+
const close = () => {
|
|
103
|
+
closeMenus();
|
|
104
|
+
closeContextMenu();
|
|
105
|
+
if (ctrl.commentEditFor) saveComment();
|
|
106
|
+
};
|
|
78
107
|
const offScroll = onScrollOutside(
|
|
79
|
-
|
|
108
|
+
// Scoped to THIS grid's menus. A bare document query treats a second
|
|
109
|
+
// grid's open menu as "inside", so scrolling in that other menu would
|
|
110
|
+
// leave this one stranded at its stale coordinates.
|
|
111
|
+
() =>
|
|
112
|
+
Array.from(
|
|
113
|
+
document.querySelectorAll<HTMLElement>(
|
|
114
|
+
`.sv-grid-menu[data-svgrid-menu="${ctrl.gridDomId}"]`,
|
|
115
|
+
),
|
|
116
|
+
),
|
|
80
117
|
close,
|
|
81
118
|
);
|
|
82
119
|
window.addEventListener('resize', close);
|
|
@@ -96,6 +133,71 @@
|
|
|
96
133
|
const clearGroupingFromMenu = $derived(ctrl.clearGroupingFromMenu);
|
|
97
134
|
const columnMenuFacetValues = $derived(ctrl.columnMenuFacetValues);
|
|
98
135
|
const columnMenuVisibleFacets = $derived(ctrl.columnMenuVisibleFacets);
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* How many value rows the checklist shows. It was pinned at 7 regardless of
|
|
139
|
+
* the window, so a tall screen left most of the space under the menu empty
|
|
140
|
+
* while the list stayed a 7-row porthole over a few hundred values.
|
|
141
|
+
*
|
|
142
|
+
* Read once per open rather than on resize: the popover is transient, and
|
|
143
|
+
* the menu is already capped by its own `max-height`.
|
|
144
|
+
*/
|
|
145
|
+
const FACET_ROW_HEIGHT = 24;
|
|
146
|
+
const facetRows = $derived.by(function facetRows_d() {
|
|
147
|
+
if (typeof window === "undefined") return 7;
|
|
148
|
+
const top = (filterMenuFor ? filterMenuPos.y : columnMenuPos.y) || 0;
|
|
149
|
+
// What is left under the menu's top edge once the condition block, the
|
|
150
|
+
// search row and the action bar have taken their share.
|
|
151
|
+
const room = window.innerHeight - top - 320;
|
|
152
|
+
return Math.max(7, Math.min(16, Math.floor(room / FACET_ROW_HEIGHT)));
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* The same date controls the filter ROW uses. Without them a date column
|
|
157
|
+
* offered two different date UIs depending on which surface you touched:
|
|
158
|
+
* a rich picker under the header, a bare `<input type="date">` in the menu.
|
|
159
|
+
*
|
|
160
|
+
* Lazy even though GridMenus is itself a lazy chunk - the picker drags in
|
|
161
|
+
* the date-format engine, and most grids that open a column menu have no
|
|
162
|
+
* date column at all.
|
|
163
|
+
*/
|
|
164
|
+
let MenuDatePicker = $state<typeof import("./SvDateTimePicker.svelte").default | null>(null);
|
|
165
|
+
let MenuRangePicker = $state<typeof import("./SvDateRangeInput.svelte").default | null>(null);
|
|
166
|
+
$effect(() => {
|
|
167
|
+
const colId = filterMenuFor ?? columnMenuFor;
|
|
168
|
+
if (!colId) return;
|
|
169
|
+
const column = allColumns.find((c) => c.id === colId);
|
|
170
|
+
if (!usesRichDateFilter(column?.columnDef.editorType)) return;
|
|
171
|
+
if (!MenuDatePicker)
|
|
172
|
+
import("./SvDateTimePicker.svelte").then((m) => (MenuDatePicker = m.default));
|
|
173
|
+
if (!MenuRangePicker)
|
|
174
|
+
import("./SvDateRangeInput.svelte").then((m) => (MenuRangePicker = m.default));
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Opening the filter popover used to leave focus on the funnel button
|
|
179
|
+
* outside it, so a keyboard user landed nowhere and had to tab in past the
|
|
180
|
+
* whole menu. Focus the first control instead - the condition value if the
|
|
181
|
+
* column has one, else the value search.
|
|
182
|
+
*/
|
|
183
|
+
function focusFilterPanel(node: HTMLElement) {
|
|
184
|
+
requestAnimationFrame(() => {
|
|
185
|
+
const first = node.querySelector<HTMLElement>(
|
|
186
|
+
".sv-grid-menu-condition-value, .sv-dtp__input, .sv-dri__input, .sv-grid-menu-search",
|
|
187
|
+
);
|
|
188
|
+
first?.focus({ preventScroll: true });
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Enter anywhere in the panel means "done" - filtering is already live. */
|
|
193
|
+
function onFilterPanelKeydown(event: KeyboardEvent) {
|
|
194
|
+
if (event.key !== "Enter" || event.shiftKey) return;
|
|
195
|
+
const target = event.target as HTMLElement | null;
|
|
196
|
+
// Let the listbox and real buttons keep Enter for their own activation.
|
|
197
|
+
if (target?.closest(".sv-listbox") || target?.tagName === "BUTTON") return;
|
|
198
|
+
event.preventDefault();
|
|
199
|
+
closeMenus();
|
|
200
|
+
}
|
|
99
201
|
const columnMenuFacetOptions = $derived(ctrl.columnMenuFacetOptions);
|
|
100
202
|
const columnMenuSelectedFacets = $derived(ctrl.columnMenuSelectedFacets);
|
|
101
203
|
const setFacetSelection = $derived(ctrl.setFacetSelection);
|
|
@@ -187,6 +289,21 @@
|
|
|
187
289
|
{@const menuOperatorOptions = operatorsForColumn(menuColumn)}
|
|
188
290
|
{@const menuActiveOperator = filterMenuValues[colId]?.operator ?? defaultOperatorFor(menuColumn)}
|
|
189
291
|
{@const menuInputType = getEditorInputType((menuColumn?.columnDef.editorType ?? "text") as CellEditorType)}
|
|
292
|
+
{@const richDate =
|
|
293
|
+
usesRichDateFilter(menuColumn?.columnDef.editorType) &&
|
|
294
|
+
menuActiveOperator !== "isBlank" &&
|
|
295
|
+
menuActiveOperator !== "isNotBlank" &&
|
|
296
|
+
menuActiveOperator !== "regex" &&
|
|
297
|
+
menuActiveOperator !== "in" &&
|
|
298
|
+
menuActiveOperator !== "notIn"}
|
|
299
|
+
{@const searching =
|
|
300
|
+
columnMenuVisibleFacets.length !== columnMenuFacetValues.length}
|
|
301
|
+
{@const conditionActive =
|
|
302
|
+
menuActiveOperator === "isBlank" ||
|
|
303
|
+
menuActiveOperator === "isNotBlank" ||
|
|
304
|
+
(filterMenuValues[colId]?.value ?? "").trim().length > 0}
|
|
305
|
+
{@const pickedCount = columnMenuSelectedFacets.size}
|
|
306
|
+
{@const totalCount = columnMenuFacetValues.length}
|
|
190
307
|
<div class="sv-grid-menu-filter">
|
|
191
308
|
<div class="sv-grid-menu-filter-head">
|
|
192
309
|
<span class="sv-grid-header-icon">{@render icon("filter")}</span> Filter
|
|
@@ -204,6 +321,40 @@
|
|
|
204
321
|
{/each}
|
|
205
322
|
</select>
|
|
206
323
|
{#if menuActiveOperator !== "isBlank" && menuActiveOperator !== "isNotBlank"}
|
|
324
|
+
{#if richDate}
|
|
325
|
+
<!-- Same controls the filter row uses, so a date column behaves the
|
|
326
|
+
same wherever you touch it: a picker, or a range field for
|
|
327
|
+
`between`. `regex` and the set operators keep the text box - a
|
|
328
|
+
pattern and a token list are not dates. -->
|
|
329
|
+
{#if menuActiveOperator === "between"}
|
|
330
|
+
{#if MenuRangePicker}
|
|
331
|
+
{@const RangePicker = MenuRangePicker}
|
|
332
|
+
{@const from = fromIsoDateLocal(filterMenuValues[colId]?.value)}
|
|
333
|
+
{@const to = fromIsoDateLocal(filterMenuValues[colId]?.valueTo)}
|
|
334
|
+
<RangePicker
|
|
335
|
+
value={from && to ? [from, to] : null}
|
|
336
|
+
formatString="yyyy-MM-dd"
|
|
337
|
+
block
|
|
338
|
+
placeholder="Pick a range..."
|
|
339
|
+
onChange={(range) => {
|
|
340
|
+
updateFilterMenuValue(colId, range ? toIsoDateLocal(range[0]) : "");
|
|
341
|
+
updateFilterMenuValueTo(colId, range ? toIsoDateLocal(range[1]) : "");
|
|
342
|
+
}}
|
|
343
|
+
/>
|
|
344
|
+
{/if}
|
|
345
|
+
{:else if MenuDatePicker}
|
|
346
|
+
{@const DatePicker = MenuDatePicker}
|
|
347
|
+
<DatePicker
|
|
348
|
+
value={(filterMenuValues[colId]?.value ?? "") || null}
|
|
349
|
+
formatString="yyyy-MM-dd"
|
|
350
|
+
dropDownDisplayMode="calendar"
|
|
351
|
+
block
|
|
352
|
+
nullable
|
|
353
|
+
placeholder="Filter value..."
|
|
354
|
+
onChange={(d) => updateFilterMenuValue(colId, toIsoDateLocal(d))}
|
|
355
|
+
/>
|
|
356
|
+
{/if}
|
|
357
|
+
{:else}
|
|
207
358
|
{@const isSetOp =
|
|
208
359
|
menuActiveOperator === "in" || menuActiveOperator === "notIn"}
|
|
209
360
|
<!-- in/notIn drives the shared value-suggestions dropdown (a windowed
|
|
@@ -239,6 +390,9 @@
|
|
|
239
390
|
oninput={(event) => updateFilterMenuValueTo(colId, (event.currentTarget as HTMLInputElement).value)}
|
|
240
391
|
/>
|
|
241
392
|
{/if}
|
|
393
|
+
{/if}
|
|
394
|
+
<!-- A second condition is offered for every column type, date
|
|
395
|
+
included, so this sits outside the value-control branch. -->
|
|
242
396
|
{@const mf = filterMenuValues[colId]}
|
|
243
397
|
{#if mf?.operator2}
|
|
244
398
|
{@const op2 = mf.operator2 ?? defaultOperatorFor(menuColumn)}
|
|
@@ -288,7 +442,16 @@
|
|
|
288
442
|
{/if}
|
|
289
443
|
{/if}
|
|
290
444
|
<div class="sv-grid-menu-sep"></div>
|
|
291
|
-
<div class="sv-grid-menu-filter-head">
|
|
445
|
+
<div class="sv-grid-menu-filter-head">
|
|
446
|
+
Values
|
|
447
|
+
<!-- The condition above and this checklist are ANDed. Nothing said so,
|
|
448
|
+
so a row could vanish for a reason neither half showed on its own. -->
|
|
449
|
+
{#if conditionActive}
|
|
450
|
+
<span class="sv-grid-menu-head-note">and the condition above</span>
|
|
451
|
+
{:else if pickedCount < totalCount}
|
|
452
|
+
<span class="sv-grid-menu-head-note">{pickedCount} of {totalCount}</span>
|
|
453
|
+
{/if}
|
|
454
|
+
</div>
|
|
292
455
|
<input
|
|
293
456
|
class="sv-grid-menu-search"
|
|
294
457
|
placeholder="Search values..."
|
|
@@ -296,7 +459,11 @@
|
|
|
296
459
|
/>
|
|
297
460
|
<label class="sv-grid-facet sv-grid-facet-all">
|
|
298
461
|
<input type="checkbox" checked={isAllFacetsChecked(colId)} onchange={() => toggleAllFacets(colId)} />
|
|
299
|
-
|
|
462
|
+
<!-- Says what it will actually do: with a query typed it acts on the
|
|
463
|
+
matches only, leaving the rest of the selection untouched. -->
|
|
464
|
+
<span class="sv-grid-facet-label"
|
|
465
|
+
>{searching ? "(Select all results)" : "(Select all)"}</span
|
|
466
|
+
>
|
|
300
467
|
</label>
|
|
301
468
|
<!-- Windowed checklist. A high-cardinality column (10k distinct symbols
|
|
302
469
|
on a live feed) would otherwise mount a label + checkbox per value -
|
|
@@ -308,7 +475,7 @@
|
|
|
308
475
|
checkbox
|
|
309
476
|
multiple
|
|
310
477
|
virtual={columnMenuFacetOptions.length > FACET_VIRTUAL_THRESHOLD}
|
|
311
|
-
rows={
|
|
478
|
+
rows={facetRows}
|
|
312
479
|
rowHeight={24}
|
|
313
480
|
size="sm"
|
|
314
481
|
ariaLabel="Filter values"
|
|
@@ -348,6 +515,7 @@
|
|
|
348
515
|
{@const menuTab = ctrl.columnMenuTab}
|
|
349
516
|
<div
|
|
350
517
|
class="sv-grid-menu sv-grid-column-menu"
|
|
518
|
+
data-svgrid-menu={ctrl.gridDomId}
|
|
351
519
|
role="menu"
|
|
352
520
|
style={`left: ${columnMenuPos.x}px; top: ${columnMenuPos.y}px; max-height: calc(100vh - ${columnMenuPos.y}px - 12px);`}
|
|
353
521
|
>
|
|
@@ -450,18 +618,23 @@
|
|
|
450
618
|
</button>
|
|
451
619
|
{/if}
|
|
452
620
|
<div class="sv-grid-menu-sep"></div>
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
621
|
+
<!-- `resizable: false` means the width is part of the layout, so the
|
|
622
|
+
menu must not offer to change it either - otherwise removing the
|
|
623
|
+
drag handle just moves the same action one click away. -->
|
|
624
|
+
{#if ctrl.columnResizable(menuColumnId)}
|
|
625
|
+
<button
|
|
626
|
+
type="button"
|
|
627
|
+
class="sv-grid-menu-item"
|
|
628
|
+
role="menuitem"
|
|
629
|
+
onclick={() => {
|
|
630
|
+
autosizeColumn(menuColumnId);
|
|
631
|
+
closeMenus();
|
|
632
|
+
}}
|
|
633
|
+
>
|
|
634
|
+
<span class="sv-grid-header-icon">{@render icon("autosize")}</span> Autosize
|
|
635
|
+
this column
|
|
636
|
+
</button>
|
|
637
|
+
{/if}
|
|
465
638
|
<button
|
|
466
639
|
type="button"
|
|
467
640
|
class="sv-grid-menu-item"
|
|
@@ -530,10 +703,15 @@
|
|
|
530
703
|
{/if}
|
|
531
704
|
|
|
532
705
|
{#if filterMenuFor && showColumnFiltersEffective}
|
|
706
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
533
707
|
<div
|
|
534
708
|
class="sv-grid-menu sv-grid-filter-menu"
|
|
709
|
+
data-svgrid-menu={ctrl.gridDomId}
|
|
535
710
|
role="menu"
|
|
536
711
|
style={`left: ${filterMenuPos.x}px; top: ${filterMenuPos.y}px; max-height: calc(100vh - ${filterMenuPos.y}px - 12px);`}
|
|
712
|
+
tabindex="-1"
|
|
713
|
+
use:focusFilterPanel
|
|
714
|
+
onkeydown={onFilterPanelKeydown}
|
|
537
715
|
>
|
|
538
716
|
{@render filterPanelBody(filterMenuFor)}
|
|
539
717
|
</div>
|
|
@@ -543,6 +721,7 @@
|
|
|
543
721
|
{@const everyColumn = grid.getAllColumns()}
|
|
544
722
|
<div
|
|
545
723
|
class="sv-grid-menu sv-grid-choose-columns-menu"
|
|
724
|
+
data-svgrid-menu={ctrl.gridDomId}
|
|
546
725
|
role="menu"
|
|
547
726
|
style={`left: ${chooseColumnsPos.x}px; top: ${chooseColumnsPos.y}px;`}
|
|
548
727
|
>
|
|
@@ -571,6 +750,7 @@
|
|
|
571
750
|
filterMenuValues[opColumnId]?.operator ?? defaultOperatorFor(opColumn)}
|
|
572
751
|
<div
|
|
573
752
|
class="sv-grid-menu sv-grid-operator-menu"
|
|
753
|
+
data-svgrid-menu={ctrl.gridDomId}
|
|
574
754
|
role="menu"
|
|
575
755
|
style={`left: ${operatorMenuPos.x}px; top: ${operatorMenuPos.y}px;`}
|
|
576
756
|
>
|
|
@@ -608,6 +788,7 @@
|
|
|
608
788
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
609
789
|
<div
|
|
610
790
|
class="sv-grid-menu sv-grid-in-suggest"
|
|
791
|
+
data-svgrid-menu={ctrl.gridDomId}
|
|
611
792
|
style={`left: ${inSuggestPos.x}px; top: ${inSuggestPos.y}px;`}
|
|
612
793
|
onmousedown={(event) => event.preventDefault()}
|
|
613
794
|
>
|
|
@@ -651,6 +832,7 @@
|
|
|
651
832
|
></div>
|
|
652
833
|
<div
|
|
653
834
|
class="sv-grid-menu sv-grid-context-menu"
|
|
835
|
+
data-svgrid-menu={ctrl.gridDomId}
|
|
654
836
|
role="menu"
|
|
655
837
|
style={`left: ${contextMenuPos.x}px; top: ${contextMenuPos.y}px;`}
|
|
656
838
|
>
|
|
@@ -678,6 +860,7 @@
|
|
|
678
860
|
></div>
|
|
679
861
|
<div
|
|
680
862
|
class="sv-grid-menu sv-grid-comment-editor"
|
|
863
|
+
data-svgrid-menu={ctrl.gridDomId}
|
|
681
864
|
role="dialog"
|
|
682
865
|
aria-label="Edit comment"
|
|
683
866
|
style={`left: ${commentEditFor.x}px; top: ${commentEditFor.y}px;`}
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
size = 'md',
|
|
40
40
|
ariaLabel,
|
|
41
41
|
invalid = false,
|
|
42
|
+
block = false,
|
|
42
43
|
required = false,
|
|
43
44
|
error,
|
|
44
45
|
label,
|
|
@@ -111,6 +112,7 @@
|
|
|
111
112
|
<input
|
|
112
113
|
bind:this={fieldEl}
|
|
113
114
|
class="sv-ac sv-ac--{size}"
|
|
115
|
+
class:is-block={block}
|
|
114
116
|
class:is-invalid={invalid}
|
|
115
117
|
type="text"
|
|
116
118
|
{placeholder}
|
|
@@ -162,4 +164,6 @@
|
|
|
162
164
|
background-size: 6px 4px; background-position: center; background-repeat: repeat-x;
|
|
163
165
|
}
|
|
164
166
|
:global(.sv-ddl__grip:hover .sv-ddl__grip-dots) { opacity: 1; }
|
|
167
|
+
/* Fill the container - see `block` in SvEditorProps. */
|
|
168
|
+
.sv-ac.is-block { width: 100%; max-width: 100%; }
|
|
165
169
|
</style>
|
package/dist/SvComboBox.svelte
CHANGED
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
size = 'md',
|
|
64
64
|
ariaLabel,
|
|
65
65
|
invalid = false,
|
|
66
|
+
block = false,
|
|
66
67
|
required = false,
|
|
67
68
|
error,
|
|
68
69
|
label,
|
|
@@ -182,7 +183,8 @@
|
|
|
182
183
|
</script>
|
|
183
184
|
|
|
184
185
|
<SvField id={uid} {label} {hint} {error} {required} {dir} loading={loadingProp || loading}>
|
|
185
|
-
<div bind:this={fieldEl} class="sv-combo sv-combo--{size}"
|
|
186
|
+
<div bind:this={fieldEl} class="sv-combo sv-combo--{size}"
|
|
187
|
+
class:is-block={block} class:is-open={combo.open} class:is-disabled={disabled} class:is-invalid={invalid}>
|
|
186
188
|
<input
|
|
187
189
|
bind:this={inputEl}
|
|
188
190
|
class="sv-combo__input"
|
|
@@ -272,4 +274,6 @@
|
|
|
272
274
|
background-size: 6px 4px; background-position: center; background-repeat: repeat-x;
|
|
273
275
|
}
|
|
274
276
|
:global(.sv-ddl__grip:hover .sv-ddl__grip-dots) { opacity: 1; }
|
|
277
|
+
/* Fill the container - see `block` in SvEditorProps. */
|
|
278
|
+
.sv-combo.is-block { width: 100%; max-width: 100%; }
|
|
275
279
|
</style>
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
size = 'md',
|
|
75
75
|
ariaLabel,
|
|
76
76
|
invalid = false,
|
|
77
|
+
block = false,
|
|
77
78
|
required = false,
|
|
78
79
|
error,
|
|
79
80
|
label,
|
|
@@ -117,6 +118,7 @@
|
|
|
117
118
|
|
|
118
119
|
// --- Portalled popover positioning (DOM-bound; stays in the component) -------
|
|
119
120
|
let triggerEl = $state<HTMLDivElement | null>(null)
|
|
121
|
+
let inputEl = $state<HTMLInputElement | null>(null)
|
|
120
122
|
let panelEl = $state<HTMLDivElement | null>(null)
|
|
121
123
|
let panelRect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false, maxHeight: 0, availHeight: 0 })
|
|
122
124
|
|
|
@@ -157,8 +159,26 @@
|
|
|
157
159
|
</script>
|
|
158
160
|
|
|
159
161
|
<SvField id={uid} {label} {hint} {error} {required} {dir}>
|
|
160
|
-
<div class="sv-dri" class:sv-dri--disabled={disabled}>
|
|
161
|
-
|
|
162
|
+
<div class="sv-dri" class:is-block={block} class:sv-dri--disabled={disabled}>
|
|
163
|
+
<!-- The whole field is the trigger, not just the <input>. The input is
|
|
164
|
+
readonly and carries the dialog semantics, but it is the flex child that
|
|
165
|
+
shrinks: squeezed into a narrow container (a grid filter cell, say) it
|
|
166
|
+
collapses to a couple of pixels and the icon, padding and range text
|
|
167
|
+
around it were all dead to a click. The input keeps the a11y role and
|
|
168
|
+
focus; this only widens the hit area. -->
|
|
169
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
170
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
171
|
+
<div
|
|
172
|
+
class="sv-dri__field sv-dri__field--{size}"
|
|
173
|
+
class:is-invalid={invalid}
|
|
174
|
+
bind:this={triggerEl}
|
|
175
|
+
dir={resolvedDir}
|
|
176
|
+
onclick={() => {
|
|
177
|
+
if (!isInteractive) return
|
|
178
|
+
inputEl?.focus()
|
|
179
|
+
toggle()
|
|
180
|
+
}}
|
|
181
|
+
>
|
|
162
182
|
<svg class="sv-dri__icon" viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2" /><path d="M16 2v4M8 2v4M3 10h18" /></svg>
|
|
163
183
|
<input
|
|
164
184
|
id={uid}
|
|
@@ -171,8 +191,8 @@
|
|
|
171
191
|
{...editorAria({ id: uid, invalid, required, error, hint, ariaLabel })}
|
|
172
192
|
aria-haspopup="dialog"
|
|
173
193
|
aria-expanded={open}
|
|
174
|
-
onclick={() => isInteractive && toggle()}
|
|
175
194
|
onkeydown={onFieldKeydown}
|
|
195
|
+
bind:this={inputEl}
|
|
176
196
|
use:focusOpen
|
|
177
197
|
/>
|
|
178
198
|
{#if value && isInteractive}
|
|
@@ -253,4 +273,6 @@
|
|
|
253
273
|
background: var(--sg-bg, #fff); border: 1px solid var(--sg-border, #e2e8f0);
|
|
254
274
|
border-radius: 12px; box-shadow: 0 16px 48px -12px rgba(15, 23, 42, 0.35);
|
|
255
275
|
}
|
|
276
|
+
/* Fill the container - see `block` in SvEditorProps. */
|
|
277
|
+
.sv-dri.is-block, .sv-dri.is-block .sv-dri__field { width: 100%; max-width: 100%; }
|
|
256
278
|
</style>
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
size = 'md',
|
|
58
58
|
ariaLabel,
|
|
59
59
|
invalid = false,
|
|
60
|
+
block = false,
|
|
60
61
|
required = false,
|
|
61
62
|
error,
|
|
62
63
|
label,
|
|
@@ -231,6 +232,7 @@
|
|
|
231
232
|
<button
|
|
232
233
|
bind:this={triggerEl}
|
|
233
234
|
class="sv-ddl sv-ddl--{size}"
|
|
235
|
+
class:is-block={block}
|
|
234
236
|
class:is-open={ddl.open}
|
|
235
237
|
class:is-disabled={disabled}
|
|
236
238
|
class:is-invalid={invalid}
|
|
@@ -371,4 +373,6 @@
|
|
|
371
373
|
:global(.sv-ddl__empty) { padding: 12px 12px; font-size: 13px; color: var(--sg-muted, #64748b); text-align: center; }
|
|
372
374
|
:global(.sv-ddl__header) { padding: 8px 10px; border-bottom: 1px solid var(--sg-border, #e2e8f0); font-size: 12.5px; color: var(--sg-muted, #64748b); }
|
|
373
375
|
:global(.sv-ddl__footer) { padding: 8px 10px; border-top: 1px solid var(--sg-border, #e2e8f0); font-size: 12.5px; }
|
|
376
|
+
/* Fill the container - see `block` in SvEditorProps. */
|
|
377
|
+
.sv-ddl.is-block { width: 100%; max-width: 100%; }
|
|
374
378
|
</style>
|
package/dist/SvForm.svelte
CHANGED
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
{#if o.type === 'textarea'}
|
|
178
178
|
<textarea id={o.id} class="sv-form__control sv-form__textarea" rows="3" disabled={o.disabled} readonly={o.readonly} placeholder={o.placeholder} value={o.value ?? ''} aria-invalid={o.invalid ? 'true' : undefined} aria-describedby={o.describedby} oninput={(e) => o.onChange(e.currentTarget.value)} onblur={o.onBlur}></textarea>
|
|
179
179
|
{:else if o.type === 'number'}
|
|
180
|
-
<SvNumberInput id={o.id} value={o.value ?? null} disabled={o.disabled} readonly={o.readonly} invalid={o.invalid} block min={o.field?.min ?? -Infinity} max={o.field?.max ?? Infinity} step={o.field?.step ?? 1} precision={o.field?.precision} prefix={o.field?.prefix ?? ''} suffix={o.field?.suffix ?? ''} onChange={o.onChange} />
|
|
180
|
+
<SvNumberInput id={o.id} value={o.value ?? null} disabled={o.disabled} readonly={o.readonly} invalid={o.invalid} block placeholder={o.placeholder} min={o.field?.min ?? -Infinity} max={o.field?.max ?? Infinity} step={o.field?.step ?? 1} precision={o.field?.precision} prefix={o.field?.prefix ?? ''} suffix={o.field?.suffix ?? ''} onChange={o.onChange} />
|
|
181
181
|
{:else if o.type === 'password'}
|
|
182
182
|
<SvPasswordInput id={o.id} value={o.value ?? ''} disabled={o.disabled} readonly={o.readonly} invalid={o.invalid} block onChange={o.onChange} />
|
|
183
183
|
{:else if o.type === 'mask'}
|
|
@@ -185,17 +185,17 @@
|
|
|
185
185
|
{:else if o.type === 'phone'}
|
|
186
186
|
<SvPhoneInput id={o.id} value={o.value ?? ''} disabled={o.disabled} invalid={o.invalid} block placeholder={o.placeholder} onChange={(v) => o.onChange(v)} />
|
|
187
187
|
{:else if o.type === 'country'}
|
|
188
|
-
<SvCountryInput id={o.id} value={o.value ?? null} disabled={o.disabled} invalid={o.invalid} onChange={o.onChange} />
|
|
188
|
+
<SvCountryInput id={o.id} value={o.value ?? null} disabled={o.disabled} invalid={o.invalid} block onChange={o.onChange} />
|
|
189
189
|
{:else if o.type === 'select'}
|
|
190
|
-
<SvDropDownList id={o.id} options={o.options ?? []} value={o.value ?? null} disabled={o.disabled} readonly={o.readonly} invalid={o.invalid} placeholder={o.placeholder} onChange={o.onChange} />
|
|
190
|
+
<SvDropDownList id={o.id} options={o.options ?? []} value={o.value ?? null} disabled={o.disabled} readonly={o.readonly} invalid={o.invalid} block placeholder={o.placeholder} onChange={o.onChange} />
|
|
191
191
|
{:else if o.type === 'combobox'}
|
|
192
|
-
<SvComboBox id={o.id} options={o.options ?? []} value={o.value ?? null} disabled={o.disabled} readonly={o.readonly} invalid={o.invalid} clearable placeholder={o.placeholder} loadOptions={o.field?.loadOptions ? (q) => o.field!.loadOptions!(q, form.values) : undefined} onChange={o.onChange} />
|
|
192
|
+
<SvComboBox id={o.id} options={o.options ?? []} value={o.value ?? null} disabled={o.disabled} readonly={o.readonly} invalid={o.invalid} block clearable placeholder={o.placeholder} loadOptions={o.field?.loadOptions ? (q) => o.field!.loadOptions!(q, form.values) : undefined} onChange={o.onChange} />
|
|
193
193
|
{:else if o.type === 'multiselect'}
|
|
194
|
-
<SvMultiSelect id={o.id} options={o.options ?? []} value={Array.isArray(o.value) ? o.value : []} disabled={o.disabled} readonly={o.readonly} invalid={o.invalid} placeholder={o.placeholder} onChange={o.onChange} />
|
|
194
|
+
<SvMultiSelect id={o.id} options={o.options ?? []} value={Array.isArray(o.value) ? o.value : []} disabled={o.disabled} readonly={o.readonly} invalid={o.invalid} block placeholder={o.placeholder} onChange={o.onChange} />
|
|
195
195
|
{:else if o.type === 'radio'}
|
|
196
196
|
<SvRadioGroup id={o.id} options={o.options ?? []} value={o.value ?? null} disabled={o.disabled} readonly={o.readonly} invalid={o.invalid} orientation="horizontal" onChange={o.onChange} />
|
|
197
197
|
{:else if o.type === 'tags'}
|
|
198
|
-
<SvTagsInput id={o.id} value={Array.isArray(o.value) ? o.value : []} disabled={o.disabled} invalid={o.invalid} placeholder={o.placeholder} onChange={o.onChange} />
|
|
198
|
+
<SvTagsInput id={o.id} value={Array.isArray(o.value) ? o.value : []} disabled={o.disabled} invalid={o.invalid} block placeholder={o.placeholder} onChange={o.onChange} />
|
|
199
199
|
{:else if o.type === 'slider'}
|
|
200
200
|
<SvSlider id={o.id} value={o.value ?? o.field?.min ?? 0} min={o.field?.min ?? 0} max={o.field?.max ?? 100} step={o.field?.step ?? 1} disabled={o.disabled} readonly={o.readonly} invalid={o.invalid} showValue onChange={o.onChange} />
|
|
201
201
|
{:else if o.type === 'checkbox'}
|
|
@@ -203,9 +203,9 @@
|
|
|
203
203
|
{:else if o.type === 'switch'}
|
|
204
204
|
<div class="sv-form__inline"><SvSwitchButton checked={!!o.value} disabled={o.disabled} readonly={o.readonly} onChange={o.onChange} ariaLabel={o.label} /><span>{o.label}</span></div>
|
|
205
205
|
{:else if o.type === 'date'}
|
|
206
|
-
<SvDateTimePicker value={dateVal(o.value)} dropDownDisplayMode="calendar" formatString="yyyy-MM-dd" disabled={o.disabled} onChange={o.onChange} />
|
|
206
|
+
<SvDateTimePicker id={o.id} value={dateVal(o.value)} dropDownDisplayMode="calendar" formatString="yyyy-MM-dd" disabled={o.disabled} readonly={o.readonly} invalid={o.invalid} block placeholder={o.placeholder ?? 'Select date'} onChange={o.onChange} />
|
|
207
207
|
{:else if o.type === 'datetime'}
|
|
208
|
-
<SvDateTimePicker value={dateVal(o.value)} dropDownDisplayMode="both" disabled={o.disabled} onChange={o.onChange} />
|
|
208
|
+
<SvDateTimePicker id={o.id} value={dateVal(o.value)} dropDownDisplayMode="both" disabled={o.disabled} readonly={o.readonly} invalid={o.invalid} block placeholder={o.placeholder} onChange={o.onChange} />
|
|
209
209
|
{:else if o.type === 'color'}
|
|
210
210
|
<SvColorInput id={o.id} value={o.value ?? '#3b82f6'} disabled={o.disabled} invalid={o.invalid} onChange={o.onChange} />
|
|
211
211
|
{:else if o.type === 'file'}
|