@svgrid/grid 2.2.15 → 2.2.17
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/GridFooter.svelte +12 -11
- package/dist/SvAnchor.svelte +65 -0
- package/dist/SvAnchor.svelte.d.ts +24 -0
- package/dist/SvAspectRatio.svelte +38 -0
- package/dist/SvAspectRatio.svelte.d.ts +16 -0
- package/dist/SvAutoComplete.svelte +52 -7
- package/dist/SvAutoComplete.svelte.d.ts +4 -1
- package/dist/SvBlockquote.svelte +43 -0
- package/dist/SvBlockquote.svelte.d.ts +16 -0
- package/dist/SvCalendar.svelte +33 -2
- package/dist/SvCalendar.svelte.d.ts +8 -1
- package/dist/SvCheckBox.svelte +6 -2
- package/dist/SvCode.svelte +52 -0
- package/dist/SvCode.svelte.d.ts +19 -0
- package/dist/SvCollapsible.svelte +75 -0
- package/dist/SvCollapsible.svelte.d.ts +27 -0
- package/dist/SvColorInput.svelte +11 -17
- package/dist/SvColorInput.svelte.d.ts +2 -0
- package/dist/SvComboBox.svelte +85 -8
- package/dist/SvComboBox.svelte.d.ts +14 -0
- package/dist/SvCountryInput.svelte +2 -2
- package/dist/SvCountryInput.svelte.d.ts +1 -1
- package/dist/SvDropDownList.svelte +154 -51
- package/dist/SvDropDownList.svelte.d.ts +17 -3
- package/dist/SvDurationInput.svelte +59 -41
- package/dist/SvDurationInput.svelte.d.ts +18 -0
- package/dist/SvField.svelte +203 -12
- package/dist/SvField.svelte.d.ts +45 -8
- package/dist/SvForm.svelte +195 -21
- package/dist/SvForm.svelte.d.ts +23 -0
- package/dist/SvGrid.controller.svelte.d.ts +12 -1
- package/dist/SvGrid.controller.svelte.js +87 -7
- package/dist/SvGrid.svelte +383 -181
- package/dist/SvGrid.types.d.ts +123 -2
- package/dist/SvGridChartView.svelte +70 -0
- package/dist/SvGridChartView.svelte.d.ts +11 -0
- package/dist/SvGridDropdown.svelte +9 -2
- package/dist/SvGridSelect.svelte +5 -2
- package/dist/SvGridSelect.svelte.d.ts +1 -1
- package/dist/SvGroup.svelte +41 -0
- package/dist/SvGroup.svelte.d.ts +27 -0
- package/dist/SvHoverCard.svelte +70 -0
- package/dist/SvHoverCard.svelte.d.ts +41 -0
- package/dist/SvKbd.svelte +51 -0
- package/dist/SvKbd.svelte.d.ts +18 -0
- package/dist/SvList.svelte +55 -0
- package/dist/SvList.svelte.d.ts +23 -0
- package/dist/SvListBox.svelte +166 -77
- package/dist/SvListBox.svelte.d.ts +17 -4
- package/dist/SvLoadingOverlay.svelte +53 -0
- package/dist/SvLoadingOverlay.svelte.d.ts +28 -0
- package/dist/SvMark.svelte +36 -0
- package/dist/SvMark.svelte.d.ts +14 -0
- package/dist/SvMaskedInput.svelte +50 -35
- package/dist/SvMaskedInput.svelte.d.ts +17 -2
- package/dist/SvMenuList.svelte +32 -3
- package/dist/SvMenubar.svelte +200 -0
- package/dist/SvMenubar.svelte.d.ts +18 -0
- package/dist/SvMultiSelect.svelte +7 -4
- package/dist/SvMultiSelect.svelte.d.ts +1 -1
- package/dist/SvNumberInput.svelte +79 -42
- package/dist/SvNumberInput.svelte.d.ts +25 -0
- package/dist/SvOtpInput.svelte +2 -2
- package/dist/SvPasswordInput.svelte +51 -31
- package/dist/SvPasswordInput.svelte.d.ts +17 -0
- package/dist/SvPhoneInput.svelte +39 -28
- package/dist/SvPhoneInput.svelte.d.ts +4 -0
- package/dist/SvPopconfirm.svelte +79 -0
- package/dist/SvPopconfirm.svelte.d.ts +42 -0
- package/dist/SvPopover.svelte +87 -29
- package/dist/SvPopover.svelte.d.ts +18 -4
- package/dist/SvRadioGroup.svelte +5 -1
- package/dist/SvResult.svelte +73 -0
- package/dist/SvResult.svelte.d.ts +28 -0
- package/dist/SvSegmented.svelte +114 -0
- package/dist/SvSegmented.svelte.d.ts +29 -0
- package/dist/SvSimpleGrid.svelte +41 -0
- package/dist/SvSimpleGrid.svelte.d.ts +25 -0
- package/dist/SvSlider.svelte +23 -10
- package/dist/SvSpinner.svelte +43 -0
- package/dist/SvSpinner.svelte.d.ts +20 -0
- package/dist/SvStack.svelte +37 -0
- package/dist/SvStack.svelte.d.ts +24 -0
- package/dist/SvSwitchButton.svelte +6 -1
- package/dist/SvTagsInput.svelte +9 -5
- package/dist/SvText.svelte +76 -0
- package/dist/SvText.svelte.d.ts +31 -0
- package/dist/SvTextInput.svelte +75 -43
- package/dist/SvTextInput.svelte.d.ts +28 -1
- package/dist/SvTimePicker.svelte +32 -2
- package/dist/SvTimePicker.svelte.d.ts +8 -9
- package/dist/SvTitle.svelte +57 -0
- package/dist/SvTitle.svelte.d.ts +24 -0
- package/dist/SvToaster.svelte +41 -6
- package/dist/SvToggleButton.svelte +4 -1
- package/dist/SvTooltip.svelte +68 -34
- package/dist/SvTooltip.svelte.d.ts +14 -2
- package/dist/SvTree.svelte +81 -21
- package/dist/SvTree.svelte.d.ts +3 -2
- package/dist/SvTreeSelect.svelte +5 -2
- package/dist/SvTreeSelect.svelte.d.ts +1 -1
- package/dist/SvVisuallyHidden.svelte +49 -0
- package/dist/SvVisuallyHidden.svelte.d.ts +19 -0
- package/dist/ai.d.ts +289 -0
- package/dist/ai.js +1013 -0
- package/dist/board-view.svelte.d.ts +22 -0
- package/dist/board-view.svelte.js +13 -0
- package/dist/build-api.js +36 -6
- package/dist/cdn/GridMenus-0AK9O7qG.js +420 -0
- package/dist/cdn/GridMenus-BjUoCXoD.js +416 -0
- package/dist/cdn/SvDateTimePicker-CYC7gRER.js +2176 -0
- package/dist/cdn/SvDateTimePicker-DtQKd4Ns.js +2198 -0
- package/dist/cdn/SvGridChart-BcLCYvw9.js +1195 -0
- package/dist/cdn/SvGridChart-BhnFdLd0.js +1203 -0
- package/dist/cdn/SvGridChartPanel-Cmx9DHAS.js +561 -0
- package/dist/cdn/SvGridChartPanel-DvjwxLvK.js +577 -0
- package/dist/cdn/SvGridChartView-B40DTJSX.js +54 -0
- package/dist/cdn/SvGridChartView-PwqI4s02.js +53 -0
- package/dist/cdn/SvGridDropdown-DjALc_4f.js +255 -0
- package/dist/cdn/SvGridDropdown-hpnHvOGt.js +254 -0
- package/dist/cdn/chart-T1usDN0o.js +1212 -0
- package/dist/cdn/disclose-version-DoD9AFD_.js +2958 -0
- package/dist/cdn/export-format-JT5pWVT8.js +561 -0
- package/dist/cdn/popover-P-9P2YYn.js +69 -0
- package/dist/cdn/rolldown-runtime-Dy4uBu1J.js +11 -0
- package/dist/cdn/src-BLhdLmoN.js +25707 -0
- package/dist/cdn/src-BwnUVj3T.js +25623 -0
- package/dist/cdn/svgrid.js +9 -32897
- package/dist/cdn/svgrid.svelte-external.js +9 -30052
- package/dist/cell-formatting.d.ts +2 -2
- package/dist/cell-render.js +4 -4
- package/dist/chart-view.svelte.d.ts +25 -0
- package/dist/chart-view.svelte.js +14 -0
- package/dist/createCombobox.svelte.d.ts +5 -0
- package/dist/createCombobox.svelte.js +21 -5
- package/dist/createDropdownList.svelte.d.ts +3 -0
- package/dist/createDropdownList.svelte.js +8 -2
- package/dist/createForm.svelte.d.ts +18 -0
- package/dist/createForm.svelte.js +51 -6
- package/dist/createMenu.svelte.js +27 -0
- package/dist/createPopoverSelect.svelte.d.ts +3 -0
- package/dist/createPopoverSelect.svelte.js +9 -1
- package/dist/createSlider.svelte.d.ts +8 -21
- package/dist/createSlider.svelte.js +34 -0
- package/dist/createTooltip.svelte.d.ts +12 -0
- package/dist/createTooltip.svelte.js +72 -9
- package/dist/editor-contract.d.ts +15 -0
- package/dist/export-provider.d.ts +27 -0
- package/dist/export-provider.js +9 -0
- package/dist/filter-operators.d.ts +6 -0
- package/dist/filter-operators.js +33 -0
- package/dist/form-field.d.ts +45 -2
- package/dist/grid-messages.d.ts +61 -0
- package/dist/grid-messages.js +72 -0
- package/dist/group-display.d.ts +62 -0
- package/dist/group-display.js +78 -0
- package/dist/index.d.ts +36 -4
- package/dist/index.js +43 -2
- package/dist/js-scroller.svelte.d.ts +35 -0
- package/dist/js-scroller.svelte.js +154 -0
- package/dist/list-option.d.ts +46 -0
- package/dist/list-option.js +53 -0
- package/dist/menus.js +25 -11
- package/dist/pivot-view.svelte.d.ts +72 -0
- package/dist/pivot-view.svelte.js +13 -0
- package/dist/positioning.d.ts +111 -0
- package/dist/positioning.js +191 -0
- package/dist/scheduler-model.d.ts +3 -0
- package/dist/scheduler-model.js +22 -5
- package/dist/svgrid-wrapper.types.d.ts +18 -0
- package/dist/toast-store.svelte.d.ts +54 -0
- package/dist/toast-store.svelte.js +65 -15
- package/package.json +9 -1
- package/src/GridFooter.svelte +12 -11
- package/src/SvAnchor.svelte +65 -0
- package/src/SvAspectRatio.svelte +38 -0
- package/src/SvAutoComplete.svelte +52 -7
- package/src/SvBlockquote.svelte +43 -0
- package/src/SvCalendar.svelte +33 -2
- package/src/SvCalendar.test.ts +15 -0
- package/src/SvCheckBox.svelte +6 -2
- package/src/SvCode.svelte +52 -0
- package/src/SvCollapsible.svelte +75 -0
- package/src/SvColorInput.svelte +11 -17
- package/src/SvComboBox.svelte +85 -8
- package/src/SvCountryInput.svelte +2 -2
- package/src/SvDropDownList.svelte +154 -51
- package/src/SvDurationInput.svelte +59 -41
- package/src/SvField.svelte +203 -12
- package/src/SvForm.svelte +195 -21
- package/src/SvForm.test.ts +112 -0
- package/src/SvGrid.controller.svelte.ts +88 -7
- package/src/SvGrid.svelte +383 -181
- package/src/SvGrid.types.ts +125 -1
- package/src/SvGridChartView.svelte +70 -0
- package/src/SvGridDropdown.svelte +9 -2
- package/src/SvGridSelect.svelte +5 -2
- package/src/SvGroup.svelte +41 -0
- package/src/SvHoverCard.svelte +70 -0
- package/src/SvKbd.svelte +51 -0
- package/src/SvList.svelte +55 -0
- package/src/SvListBox.svelte +166 -77
- package/src/SvLoadingOverlay.svelte +53 -0
- package/src/SvMark.svelte +36 -0
- package/src/SvMaskedInput.svelte +50 -35
- package/src/SvMenuList.svelte +32 -3
- package/src/SvMenubar.svelte +200 -0
- package/src/SvMultiSelect.svelte +7 -4
- package/src/SvNumberInput.svelte +79 -42
- package/src/SvOtpInput.svelte +2 -2
- package/src/SvPasswordInput.svelte +51 -31
- package/src/SvPhoneInput.svelte +39 -28
- package/src/SvPopconfirm.svelte +79 -0
- package/src/SvPopover.svelte +87 -29
- package/src/SvRadioGroup.svelte +5 -1
- package/src/SvResult.svelte +73 -0
- package/src/SvSegmented.svelte +114 -0
- package/src/SvSimpleGrid.svelte +41 -0
- package/src/SvSlider.svelte +23 -10
- package/src/SvSpinner.svelte +43 -0
- package/src/SvStack.svelte +37 -0
- package/src/SvSwitchButton.svelte +6 -1
- package/src/SvTagsInput.svelte +9 -5
- package/src/SvText.svelte +76 -0
- package/src/SvTextInput.svelte +75 -43
- package/src/SvTimePicker.svelte +32 -2
- package/src/SvTitle.svelte +57 -0
- package/src/SvToaster.svelte +41 -6
- package/src/SvToaster.test.ts +32 -0
- package/src/SvToggleButton.svelte +4 -1
- package/src/SvTooltip.svelte +68 -34
- package/src/SvTree.svelte +81 -21
- package/src/SvTreeSelect.svelte +5 -2
- package/src/SvVisuallyHidden.svelte +49 -0
- package/src/ai.test.ts +502 -0
- package/src/ai.ts +1391 -0
- package/src/board-view.svelte.ts +33 -0
- package/src/build-api.coverage.test.ts +101 -0
- package/src/build-api.ts +43 -18
- package/src/builtin-editors.grid.test.ts +1 -1
- package/src/cell-formatting.ts +5 -5
- package/src/cell-render.ts +4 -4
- package/src/chart-view.svelte.ts +36 -0
- package/src/createCombobox.svelte.ts +17 -5
- package/src/createDropdownList.svelte.ts +8 -2
- package/src/createForm.svelte.ts +59 -6
- package/src/createMenu.svelte.ts +23 -0
- package/src/createPopoverSelect.svelte.ts +8 -1
- package/src/createSlider.svelte.ts +20 -0
- package/src/createTooltip.svelte.ts +67 -9
- package/src/editor-contract.ts +16 -0
- package/src/export-provider.ts +40 -0
- package/src/filter-operators.ts +37 -0
- package/src/form-field.ts +42 -2
- package/src/grid-messages.test.ts +24 -0
- package/src/grid-messages.ts +137 -0
- package/src/group-display.test.ts +77 -0
- package/src/group-display.ts +117 -0
- package/src/i18n.grid.test.ts +73 -0
- package/src/index.ts +91 -2
- package/src/js-scroller.svelte.ts +173 -0
- package/src/list-option.test.ts +56 -0
- package/src/list-option.ts +81 -0
- package/src/menus.test.ts +32 -0
- package/src/menus.ts +25 -12
- package/src/pivot-view.svelte.ts +90 -0
- package/src/pivot-view.test.ts +38 -0
- package/src/pivot.grid.test.ts +102 -0
- package/src/positioning.test.ts +141 -0
- package/src/positioning.ts +266 -0
- package/src/scheduler-model.test.ts +16 -0
- package/src/scheduler-model.ts +24 -5
- package/src/svgrid-wrapper.types.ts +23 -0
- package/src/svgrid.charting.test.ts +8 -1
- package/src/svgrid.comments-autocomplete.test.ts +7 -2
- package/src/svgrid.context-menu.test.ts +8 -3
- package/src/toast-store.svelte.ts +98 -0
- package/src/toast-store.test.ts +62 -1
- package/src/ui-breadth.test.ts +138 -0
- package/src/ui-buttons.test.ts +39 -0
- package/src/ui-inputs.test.ts +2 -2
- package/src/ui-range.test.ts +9 -0
- package/src/ui-selection.test.ts +36 -0
- package/src/ui-tier1.test.ts +1 -1
- package/src/virtual.test.ts +20 -6
- package/dist/SvGridBoard.svelte +0 -2339
- package/dist/SvGridBoard.svelte.d.ts +0 -31
- package/src/SvGridBoard.svelte +0 -2339
- package/src/board.test.ts +0 -927
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* SvResult - a centered result / status page for the outcome of an operation or
|
|
4
|
+
* a route state: success, error, warning, info, or not-found. An icon (built-in
|
|
5
|
+
* per status, or your own), a title, an optional description, and an actions
|
|
6
|
+
* slot. For empty data tables prefer SvEmptyState; use this for full-page
|
|
7
|
+
* outcomes ("Payment complete", "Something went wrong", "404").
|
|
8
|
+
*
|
|
9
|
+
* ```svelte
|
|
10
|
+
* <SvResult status="success" title="Payment complete" description="A receipt is on its way.">
|
|
11
|
+
* {#snippet actions()}<SvButton>Back to dashboard</SvButton>{/snippet}
|
|
12
|
+
* </SvResult>
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
import type { Snippet } from 'svelte'
|
|
16
|
+
|
|
17
|
+
type Status = 'success' | 'error' | 'warning' | 'info' | 'notfound'
|
|
18
|
+
|
|
19
|
+
type Props = {
|
|
20
|
+
status?: Status
|
|
21
|
+
title: string
|
|
22
|
+
description?: string
|
|
23
|
+
/** Replace the built-in status icon. */
|
|
24
|
+
icon?: Snippet
|
|
25
|
+
/** Action buttons row under the text. */
|
|
26
|
+
actions?: Snippet
|
|
27
|
+
children?: Snippet
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let { status = 'info', title, description, icon, actions, children }: Props = $props()
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<div class="sv-result sv-result--{status}" role="status">
|
|
34
|
+
<div class="sv-result__icon" aria-hidden="true">
|
|
35
|
+
{#if icon}{@render icon()}
|
|
36
|
+
{:else if status === 'success'}
|
|
37
|
+
<svg viewBox="0 0 24 24" width="30" height="30" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5" /></svg>
|
|
38
|
+
{:else if status === 'error'}
|
|
39
|
+
<svg viewBox="0 0 24 24" width="30" height="30" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12" /></svg>
|
|
40
|
+
{:else if status === 'warning'}
|
|
41
|
+
<svg viewBox="0 0 24 24" width="30" height="30" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z" /></svg>
|
|
42
|
+
{:else if status === 'notfound'}
|
|
43
|
+
<span class="sv-result__code">404</span>
|
|
44
|
+
{:else}
|
|
45
|
+
<svg viewBox="0 0 24 24" width="30" height="30" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9" /><path d="M12 8h.01M11 12h1v4h1" /></svg>
|
|
46
|
+
{/if}
|
|
47
|
+
</div>
|
|
48
|
+
<h2 class="sv-result__title">{title}</h2>
|
|
49
|
+
{#if description}<p class="sv-result__desc">{description}</p>{/if}
|
|
50
|
+
{#if children}<div class="sv-result__body">{@render children()}</div>{/if}
|
|
51
|
+
{#if actions}<div class="sv-result__actions">{@render actions()}</div>{/if}
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<style>
|
|
55
|
+
.sv-result {
|
|
56
|
+
--_c: var(--sg-accent, #2563eb);
|
|
57
|
+
display: flex; flex-direction: column; align-items: center; text-align: center; gap: 8px;
|
|
58
|
+
padding: 40px 24px; max-width: 460px; margin: 0 auto;
|
|
59
|
+
}
|
|
60
|
+
.sv-result--success { --_c: var(--sg-success, #16a34a); }
|
|
61
|
+
.sv-result--error { --_c: var(--sg-danger, #dc2626); }
|
|
62
|
+
.sv-result--warning { --_c: var(--sg-warning, #f59e0b); }
|
|
63
|
+
.sv-result--notfound { --_c: var(--sg-muted, #64748b); }
|
|
64
|
+
.sv-result__icon {
|
|
65
|
+
display: grid; place-items: center; width: 60px; height: 60px; border-radius: 50%; margin-bottom: 4px;
|
|
66
|
+
color: var(--_c); background: color-mix(in srgb, var(--_c) 12%, transparent);
|
|
67
|
+
}
|
|
68
|
+
.sv-result__code { font-size: 22px; font-weight: 800; letter-spacing: 0.02em; }
|
|
69
|
+
.sv-result__title { margin: 0; font-size: 19px; font-weight: 700; color: var(--sg-fg, #0f172a); }
|
|
70
|
+
.sv-result__desc { margin: 0; font-size: 14px; line-height: 1.5; color: var(--sg-muted, #64748b); }
|
|
71
|
+
.sv-result__body { margin-top: 6px; }
|
|
72
|
+
.sv-result__actions { display: flex; gap: 10px; margin-top: 14px; flex-wrap: wrap; justify-content: center; }
|
|
73
|
+
</style>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SvResult - a centered result / status page for the outcome of an operation or
|
|
3
|
+
* a route state: success, error, warning, info, or not-found. An icon (built-in
|
|
4
|
+
* per status, or your own), a title, an optional description, and an actions
|
|
5
|
+
* slot. For empty data tables prefer SvEmptyState; use this for full-page
|
|
6
|
+
* outcomes ("Payment complete", "Something went wrong", "404").
|
|
7
|
+
*
|
|
8
|
+
* ```svelte
|
|
9
|
+
* <SvResult status="success" title="Payment complete" description="A receipt is on its way.">
|
|
10
|
+
* {#snippet actions()}<SvButton>Back to dashboard</SvButton>{/snippet}
|
|
11
|
+
* </SvResult>
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
import type { Snippet } from 'svelte';
|
|
15
|
+
type Status = 'success' | 'error' | 'warning' | 'info' | 'notfound';
|
|
16
|
+
type Props = {
|
|
17
|
+
status?: Status;
|
|
18
|
+
title: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
/** Replace the built-in status icon. */
|
|
21
|
+
icon?: Snippet;
|
|
22
|
+
/** Action buttons row under the text. */
|
|
23
|
+
actions?: Snippet;
|
|
24
|
+
children?: Snippet;
|
|
25
|
+
};
|
|
26
|
+
declare const SvResult: import("svelte").Component<Props, {}, "">;
|
|
27
|
+
type SvResult = ReturnType<typeof SvResult>;
|
|
28
|
+
export default SvResult;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export type { RadioOption as SegmentedOption } from './createRadioGroup.svelte'
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
/**
|
|
7
|
+
* SvSegmented - a segmented control: a compact row of mutually-exclusive
|
|
8
|
+
* options in a shared track (the modern alternative to a small radio group or a
|
|
9
|
+
* tab strip for a setting). Single-select, keyboard-accessible (it reuses the
|
|
10
|
+
* radio-group core: arrow keys roam, Space/Enter select), and themed from the
|
|
11
|
+
* shared `--sg-*` tokens.
|
|
12
|
+
*
|
|
13
|
+
* ```svelte
|
|
14
|
+
* <SvSegmented options={[{value:'list',label:'List'},{value:'grid',label:'Grid'}]} bind:value />
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
import type { Snippet } from 'svelte'
|
|
18
|
+
import SvField from './SvField.svelte'
|
|
19
|
+
import { createRadioGroup, type RadioOption } from './createRadioGroup.svelte'
|
|
20
|
+
import { nextEditorId, type SvEditorProps } from './editor-contract'
|
|
21
|
+
|
|
22
|
+
type SegOption = RadioOption & { icon?: Snippet }
|
|
23
|
+
|
|
24
|
+
type Props = Pick<
|
|
25
|
+
SvEditorProps,
|
|
26
|
+
'disabled' | 'size' | 'dir' | 'id' | 'ariaLabel' | 'label' | 'hint' | 'error' | 'required' | 'invalid'
|
|
27
|
+
> & {
|
|
28
|
+
options: ReadonlyArray<SegOption>
|
|
29
|
+
value?: string | number | null
|
|
30
|
+
onChange?: (value: string | number) => void
|
|
31
|
+
/** Stretch the control (and split options evenly) to the container width. */
|
|
32
|
+
block?: boolean
|
|
33
|
+
name?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let {
|
|
37
|
+
options,
|
|
38
|
+
value = $bindable<string | number | null>(null),
|
|
39
|
+
onChange,
|
|
40
|
+
disabled = false,
|
|
41
|
+
size = 'md',
|
|
42
|
+
dir,
|
|
43
|
+
id,
|
|
44
|
+
ariaLabel,
|
|
45
|
+
label,
|
|
46
|
+
hint,
|
|
47
|
+
error,
|
|
48
|
+
required = false,
|
|
49
|
+
invalid = false,
|
|
50
|
+
block = false,
|
|
51
|
+
name,
|
|
52
|
+
}: Props = $props()
|
|
53
|
+
|
|
54
|
+
const autoId = nextEditorId('sv-seg')
|
|
55
|
+
const uid = $derived(id ?? autoId)
|
|
56
|
+
|
|
57
|
+
const rg = createRadioGroup({
|
|
58
|
+
options: () => options,
|
|
59
|
+
value: () => value,
|
|
60
|
+
onChange: (v) => { value = v; onChange?.(v) },
|
|
61
|
+
disabled: () => disabled,
|
|
62
|
+
ariaLabel: () => ariaLabel ?? label,
|
|
63
|
+
id: () => uid,
|
|
64
|
+
invalid: () => invalid,
|
|
65
|
+
required: () => required,
|
|
66
|
+
error: () => error,
|
|
67
|
+
})
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<SvField id={uid} {label} {hint} {error} {required} {dir}>
|
|
71
|
+
<div class="sv-seg sv-seg--{size}" class:is-block={block} class:is-disabled={disabled} class:is-invalid={invalid} {...rg.groupProps()}>
|
|
72
|
+
{#each options as opt (opt.value)}
|
|
73
|
+
<button
|
|
74
|
+
class="sv-seg__opt"
|
|
75
|
+
class:is-selected={rg.isChecked(opt)}
|
|
76
|
+
{...rg.radioProps(opt)}
|
|
77
|
+
>
|
|
78
|
+
{#if opt.icon}<span class="sv-seg__icon">{@render opt.icon()}</span>{/if}
|
|
79
|
+
<span class="sv-seg__label">{opt.label}</span>
|
|
80
|
+
</button>
|
|
81
|
+
{/each}
|
|
82
|
+
{#if name}<input type="hidden" {name} value={value ?? ''} />{/if}
|
|
83
|
+
</div>
|
|
84
|
+
</SvField>
|
|
85
|
+
|
|
86
|
+
<style>
|
|
87
|
+
.sv-seg {
|
|
88
|
+
--_accent: var(--sg-accent, #2563eb);
|
|
89
|
+
display: inline-flex; gap: 2px; padding: 2px; box-sizing: border-box;
|
|
90
|
+
background: var(--sg-row-hover-bg, #f1f5f9); border-radius: calc(var(--sg-radius, 8px) + 2px);
|
|
91
|
+
border: 1px solid var(--sg-border, #e2e8f0);
|
|
92
|
+
}
|
|
93
|
+
.sv-seg.is-block { display: flex; width: 100%; }
|
|
94
|
+
.sv-seg.is-disabled { opacity: 0.6; }
|
|
95
|
+
.sv-seg__opt {
|
|
96
|
+
flex: 1 1 auto; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
|
|
97
|
+
padding: 0 12px; border: 0; border-radius: var(--sg-radius, 8px); background: none; cursor: pointer;
|
|
98
|
+
font: inherit; font-weight: 550; color: var(--sg-muted, #64748b); white-space: nowrap;
|
|
99
|
+
transition: background 0.14s, color 0.14s, box-shadow 0.14s;
|
|
100
|
+
}
|
|
101
|
+
.sv-seg--sm .sv-seg__opt { height: 26px; font-size: 12px; }
|
|
102
|
+
.sv-seg--md .sv-seg__opt { height: 32px; font-size: 13px; }
|
|
103
|
+
.sv-seg--lg .sv-seg__opt { height: 38px; font-size: 14.5px; }
|
|
104
|
+
.sv-seg__opt:hover:not(.is-selected):not([disabled]) { color: var(--sg-fg, #0f172a); }
|
|
105
|
+
.sv-seg__opt.is-selected {
|
|
106
|
+
background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a);
|
|
107
|
+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.12);
|
|
108
|
+
}
|
|
109
|
+
.sv-seg__opt[disabled] { opacity: 0.5; cursor: default; }
|
|
110
|
+
.sv-seg__opt:focus-visible { outline: 2px solid var(--sg-focus-ring, var(--_accent)); outline-offset: -2px; }
|
|
111
|
+
.sv-seg.is-invalid { border-color: var(--sg-danger, #dc2626); }
|
|
112
|
+
.sv-seg__icon { display: inline-flex; }
|
|
113
|
+
@media (prefers-reduced-motion: reduce) { .sv-seg__opt { transition: none; } }
|
|
114
|
+
</style>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type { RadioOption as SegmentedOption } from './createRadioGroup.svelte';
|
|
2
|
+
/**
|
|
3
|
+
* SvSegmented - a segmented control: a compact row of mutually-exclusive
|
|
4
|
+
* options in a shared track (the modern alternative to a small radio group or a
|
|
5
|
+
* tab strip for a setting). Single-select, keyboard-accessible (it reuses the
|
|
6
|
+
* radio-group core: arrow keys roam, Space/Enter select), and themed from the
|
|
7
|
+
* shared `--sg-*` tokens.
|
|
8
|
+
*
|
|
9
|
+
* ```svelte
|
|
10
|
+
* <SvSegmented options={[{value:'list',label:'List'},{value:'grid',label:'Grid'}]} bind:value />
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
import type { Snippet } from 'svelte';
|
|
14
|
+
import { type RadioOption } from './createRadioGroup.svelte';
|
|
15
|
+
import { type SvEditorProps } from './editor-contract';
|
|
16
|
+
type SegOption = RadioOption & {
|
|
17
|
+
icon?: Snippet;
|
|
18
|
+
};
|
|
19
|
+
type Props = Pick<SvEditorProps, 'disabled' | 'size' | 'dir' | 'id' | 'ariaLabel' | 'label' | 'hint' | 'error' | 'required' | 'invalid'> & {
|
|
20
|
+
options: ReadonlyArray<SegOption>;
|
|
21
|
+
value?: string | number | null;
|
|
22
|
+
onChange?: (value: string | number) => void;
|
|
23
|
+
/** Stretch the control (and split options evenly) to the container width. */
|
|
24
|
+
block?: boolean;
|
|
25
|
+
name?: string;
|
|
26
|
+
};
|
|
27
|
+
declare const SvSegmented: import("svelte").Component<Props, {}, "value">;
|
|
28
|
+
type SvSegmented = ReturnType<typeof SvSegmented>;
|
|
29
|
+
export default SvSegmented;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* SvSimpleGrid - a responsive grid primitive: lays children into `cols` equal
|
|
4
|
+
* columns that auto-fit down to a `minChildWidth` before wrapping, with a
|
|
5
|
+
* consistent `gap`. Use it for card galleries, stat tiles, and dashboards.
|
|
6
|
+
*
|
|
7
|
+
* ```svelte
|
|
8
|
+
* <SvSimpleGrid minChildWidth={220} gap={12}>{#each tiles as t}<Card {t} />{/each}</SvSimpleGrid>
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
import type { Snippet } from 'svelte'
|
|
12
|
+
|
|
13
|
+
type Props = {
|
|
14
|
+
/**
|
|
15
|
+
* Fixed column count. When omitted, columns auto-fit based on
|
|
16
|
+
* `minChildWidth` (responsive by default).
|
|
17
|
+
*/
|
|
18
|
+
cols?: number
|
|
19
|
+
/** Minimum child width (px) before wrapping, for the auto-fit layout. Default 200. */
|
|
20
|
+
minChildWidth?: number
|
|
21
|
+
/** Gap between cells (px, or any CSS length). Default 12. */
|
|
22
|
+
gap?: number | string
|
|
23
|
+
children?: Snippet
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let { cols, minChildWidth = 200, gap = 12, children }: Props = $props()
|
|
27
|
+
const g = $derived(typeof gap === 'number' ? `${gap}px` : gap)
|
|
28
|
+
const template = $derived(
|
|
29
|
+
cols != null
|
|
30
|
+
? `repeat(${cols}, minmax(0, 1fr))`
|
|
31
|
+
: `repeat(auto-fit, minmax(min(${minChildWidth}px, 100%), 1fr))`,
|
|
32
|
+
)
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<div class="sv-simplegrid" style:gap={g} style:grid-template-columns={template}>
|
|
36
|
+
{@render children?.()}
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<style>
|
|
40
|
+
.sv-simplegrid { display: grid; min-width: 0; }
|
|
41
|
+
</style>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SvSimpleGrid - a responsive grid primitive: lays children into `cols` equal
|
|
3
|
+
* columns that auto-fit down to a `minChildWidth` before wrapping, with a
|
|
4
|
+
* consistent `gap`. Use it for card galleries, stat tiles, and dashboards.
|
|
5
|
+
*
|
|
6
|
+
* ```svelte
|
|
7
|
+
* <SvSimpleGrid minChildWidth={220} gap={12}>{#each tiles as t}<Card {t} />{/each}</SvSimpleGrid>
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
import type { Snippet } from 'svelte';
|
|
11
|
+
type Props = {
|
|
12
|
+
/**
|
|
13
|
+
* Fixed column count. When omitted, columns auto-fit based on
|
|
14
|
+
* `minChildWidth` (responsive by default).
|
|
15
|
+
*/
|
|
16
|
+
cols?: number;
|
|
17
|
+
/** Minimum child width (px) before wrapping, for the auto-fit layout. Default 200. */
|
|
18
|
+
minChildWidth?: number;
|
|
19
|
+
/** Gap between cells (px, or any CSS length). Default 12. */
|
|
20
|
+
gap?: number | string;
|
|
21
|
+
children?: Snippet;
|
|
22
|
+
};
|
|
23
|
+
declare const SvSimpleGrid: import("svelte").Component<Props, {}, "">;
|
|
24
|
+
type SvSimpleGrid = ReturnType<typeof SvSimpleGrid>;
|
|
25
|
+
export default SvSimpleGrid;
|
package/dist/SvSlider.svelte
CHANGED
|
@@ -39,6 +39,8 @@
|
|
|
39
39
|
labels = 'none',
|
|
40
40
|
showValue = false,
|
|
41
41
|
disabled = false,
|
|
42
|
+
readonly = false,
|
|
43
|
+
size = 'md',
|
|
42
44
|
orientation = 'horizontal',
|
|
43
45
|
name,
|
|
44
46
|
ariaLabel,
|
|
@@ -60,7 +62,7 @@
|
|
|
60
62
|
// in the headless `createSlider` core; the component keeps the DOM measuring.
|
|
61
63
|
const slider = createSlider({
|
|
62
64
|
value: () => value,
|
|
63
|
-
onChange: (v) => onChange?.(v),
|
|
65
|
+
onChange: (v) => { if (readonly) return; onChange?.(v) },
|
|
64
66
|
min: () => min,
|
|
65
67
|
max: () => max,
|
|
66
68
|
step: () => step,
|
|
@@ -69,6 +71,11 @@
|
|
|
69
71
|
disabled: () => disabled,
|
|
70
72
|
ariaLabel: () => ariaLabel ?? label,
|
|
71
73
|
rtl: () => resolvedDir === 'rtl',
|
|
74
|
+
id: () => uid,
|
|
75
|
+
invalid: () => invalid,
|
|
76
|
+
required: () => required,
|
|
77
|
+
error: () => error,
|
|
78
|
+
hint: () => hint,
|
|
72
79
|
})
|
|
73
80
|
const lo = $derived(slider.lo)
|
|
74
81
|
const hi = $derived(slider.hi)
|
|
@@ -87,7 +94,7 @@
|
|
|
87
94
|
// The rect is measured here (a DOM concern) and passed INTO the core.
|
|
88
95
|
let trackEl: HTMLDivElement | null = null
|
|
89
96
|
function onTrackDown(e: PointerEvent) {
|
|
90
|
-
if (disabled) return
|
|
97
|
+
if (disabled || readonly) return
|
|
91
98
|
slider.pointerDown({ x: e.clientX, y: e.clientY }, trackEl!.getBoundingClientRect())
|
|
92
99
|
trackEl?.setPointerCapture(e.pointerId)
|
|
93
100
|
}
|
|
@@ -102,8 +109,9 @@
|
|
|
102
109
|
|
|
103
110
|
<SvField id={uid} {label} {hint} {error} {required} {dir}>
|
|
104
111
|
<div
|
|
105
|
-
class="sv-slider sv-slider--{orientation}"
|
|
112
|
+
class="sv-slider sv-slider--{orientation} sv-slider--{size}"
|
|
106
113
|
class:is-disabled={disabled}
|
|
114
|
+
class:is-readonly={readonly}
|
|
107
115
|
dir={resolvedDir}
|
|
108
116
|
>
|
|
109
117
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
@@ -126,14 +134,14 @@
|
|
|
126
134
|
{/each}
|
|
127
135
|
|
|
128
136
|
{#if range}
|
|
129
|
-
<div class="sv-slider__thumb" style:--p={`${pct(lo)}%`} {...slider.thumbProps(0)}>
|
|
137
|
+
<div class="sv-slider__thumb" style:--p={`${pct(lo)}%`} {...slider.thumbProps(0)} aria-readonly={readonly ? 'true' : undefined}>
|
|
130
138
|
{#if showValue}<span class="sv-slider__bubble">{fmt(lo)}</span>{/if}
|
|
131
139
|
</div>
|
|
132
|
-
<div class="sv-slider__thumb" style:--p={`${pct(hi)}%`} {...slider.thumbProps(1)}>
|
|
140
|
+
<div class="sv-slider__thumb" style:--p={`${pct(hi)}%`} {...slider.thumbProps(1)} aria-readonly={readonly ? 'true' : undefined}>
|
|
133
141
|
{#if showValue}<span class="sv-slider__bubble">{fmt(hi)}</span>{/if}
|
|
134
142
|
</div>
|
|
135
143
|
{:else}
|
|
136
|
-
<div class="sv-slider__thumb" style:--p={`${pct(hi)}%`} {...slider.thumbProps(0)}>
|
|
144
|
+
<div class="sv-slider__thumb" style:--p={`${pct(hi)}%`} {...slider.thumbProps(0)} aria-readonly={readonly ? 'true' : undefined}>
|
|
137
145
|
{#if showValue}<span class="sv-slider__bubble">{fmt(hi)}</span>{/if}
|
|
138
146
|
</div>
|
|
139
147
|
{/if}
|
|
@@ -152,15 +160,20 @@
|
|
|
152
160
|
</SvField>
|
|
153
161
|
|
|
154
162
|
<style>
|
|
155
|
-
.sv-slider { --_accent: var(--sg-accent, #2563eb); --_track: var(--sg-border, #e2e8f0); padding: 10px; }
|
|
163
|
+
.sv-slider { --_accent: var(--sg-accent, #2563eb); --_track: var(--sg-border, #e2e8f0); --_th: 16px; --_trk: 6px; padding: 10px; }
|
|
164
|
+
.sv-slider--sm { --_th: 13px; --_trk: 4px; }
|
|
165
|
+
.sv-slider--lg { --_th: 20px; --_trk: 8px; }
|
|
156
166
|
.sv-slider--horizontal { width: 240px; }
|
|
167
|
+
.sv-slider--horizontal.sv-slider--sm { width: 180px; }
|
|
168
|
+
.sv-slider--horizontal.sv-slider--lg { width: 300px; }
|
|
157
169
|
.sv-slider--vertical { height: 200px; width: 40px; }
|
|
158
170
|
.sv-slider.is-disabled { opacity: 0.5; pointer-events: none; }
|
|
171
|
+
.sv-slider.is-readonly .sv-slider__track, .sv-slider.is-readonly .sv-slider__thumb { cursor: default; }
|
|
159
172
|
.sv-slider__track {
|
|
160
173
|
position: relative; background: var(--_track); border-radius: 999px; cursor: pointer; touch-action: none;
|
|
161
174
|
}
|
|
162
|
-
.sv-slider--horizontal .sv-slider__track { height:
|
|
163
|
-
.sv-slider--vertical .sv-slider__track { width:
|
|
175
|
+
.sv-slider--horizontal .sv-slider__track { height: var(--_trk); }
|
|
176
|
+
.sv-slider--vertical .sv-slider__track { width: var(--_trk); height: 100%; margin: 0 auto; }
|
|
164
177
|
.sv-slider__scale { position: relative; height: 15px; margin-top: 8px; font-size: 11px; color: var(--sg-muted, #64748b); font-variant-numeric: tabular-nums; }
|
|
165
178
|
.sv-slider__scale--ends { display: flex; justify-content: space-between; }
|
|
166
179
|
.sv-slider__scale-lbl { position: absolute; inset-inline-start: var(--p); transform: translateX(-50%); white-space: nowrap; }
|
|
@@ -168,7 +181,7 @@
|
|
|
168
181
|
.sv-slider--horizontal .sv-slider__fill { inset-inline-start: var(--a); inset-inline-end: calc(100% - var(--b)); top: 0; bottom: 0; }
|
|
169
182
|
.sv-slider--vertical .sv-slider__fill { bottom: var(--a); top: calc(100% - var(--b)); left: 0; right: 0; }
|
|
170
183
|
.sv-slider__thumb {
|
|
171
|
-
position: absolute; width:
|
|
184
|
+
position: absolute; width: var(--_th); height: var(--_th); border-radius: 50%;
|
|
172
185
|
background: #fff; border: 2px solid var(--_accent); box-shadow: 0 1px 3px rgba(0,0,0,0.25);
|
|
173
186
|
cursor: grab; touch-action: none;
|
|
174
187
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* SvSpinner - a dedicated indeterminate loading spinner (distinct from the
|
|
4
|
+
* determinate SvProgress bars). Sizeable, colorable, and it respects
|
|
5
|
+
* `prefers-reduced-motion`. Give it a `label` for screen readers.
|
|
6
|
+
*
|
|
7
|
+
* ```svelte
|
|
8
|
+
* <SvSpinner size="lg" label="Loading results" />
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
type Props = {
|
|
12
|
+
/** Diameter in px, or a named size. Default `md` (20px). */
|
|
13
|
+
size?: 'sm' | 'md' | 'lg' | number
|
|
14
|
+
/** Stroke color (defaults to the accent token). */
|
|
15
|
+
color?: string
|
|
16
|
+
/** Accessible label; when omitted the spinner is `aria-hidden`. */
|
|
17
|
+
label?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let { size = 'md', color, label }: Props = $props()
|
|
21
|
+
const px = $derived(typeof size === 'number' ? size : size === 'sm' ? 15 : size === 'lg' ? 28 : 20)
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<span
|
|
25
|
+
class="sv-spinner"
|
|
26
|
+
role={label ? 'status' : undefined}
|
|
27
|
+
aria-label={label}
|
|
28
|
+
aria-hidden={label ? undefined : 'true'}
|
|
29
|
+
style:width={`${px}px`}
|
|
30
|
+
style:height={`${px}px`}
|
|
31
|
+
style:color={color}
|
|
32
|
+
></span>
|
|
33
|
+
|
|
34
|
+
<style>
|
|
35
|
+
.sv-spinner {
|
|
36
|
+
display: inline-block; box-sizing: border-box; flex: none; border-radius: 50%;
|
|
37
|
+
border: 2px solid color-mix(in srgb, currentcolor 25%, transparent);
|
|
38
|
+
border-top-color: currentcolor; color: var(--sg-accent, #2563eb);
|
|
39
|
+
animation: sv-spinner-rot 0.6s linear infinite;
|
|
40
|
+
}
|
|
41
|
+
@keyframes sv-spinner-rot { to { transform: rotate(360deg); } }
|
|
42
|
+
@media (prefers-reduced-motion: reduce) { .sv-spinner { animation-duration: 1.4s; } }
|
|
43
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SvSpinner - a dedicated indeterminate loading spinner (distinct from the
|
|
3
|
+
* determinate SvProgress bars). Sizeable, colorable, and it respects
|
|
4
|
+
* `prefers-reduced-motion`. Give it a `label` for screen readers.
|
|
5
|
+
*
|
|
6
|
+
* ```svelte
|
|
7
|
+
* <SvSpinner size="lg" label="Loading results" />
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
type Props = {
|
|
11
|
+
/** Diameter in px, or a named size. Default `md` (20px). */
|
|
12
|
+
size?: 'sm' | 'md' | 'lg' | number;
|
|
13
|
+
/** Stroke color (defaults to the accent token). */
|
|
14
|
+
color?: string;
|
|
15
|
+
/** Accessible label; when omitted the spinner is `aria-hidden`. */
|
|
16
|
+
label?: string;
|
|
17
|
+
};
|
|
18
|
+
declare const SvSpinner: import("svelte").Component<Props, {}, "">;
|
|
19
|
+
type SvSpinner = ReturnType<typeof SvSpinner>;
|
|
20
|
+
export default SvSpinner;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* SvStack - a vertical flex layout primitive: stacks children in a column with a
|
|
4
|
+
* consistent `gap` and optional cross-axis `align`. The building block for form
|
|
5
|
+
* rows, card bodies, and sidebars. (For a horizontal row use SvGroup.)
|
|
6
|
+
*
|
|
7
|
+
* ```svelte
|
|
8
|
+
* <SvStack gap={12}><A /><B /><C /></SvStack>
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
import type { Snippet } from 'svelte'
|
|
12
|
+
|
|
13
|
+
type Props = {
|
|
14
|
+
/** Gap between children (px, or any CSS length). Default 12. */
|
|
15
|
+
gap?: number | string
|
|
16
|
+
/** Cross-axis alignment. */
|
|
17
|
+
align?: 'start' | 'center' | 'end' | 'stretch'
|
|
18
|
+
/** Main-axis distribution. */
|
|
19
|
+
justify?: 'start' | 'center' | 'end' | 'between' | 'around'
|
|
20
|
+
/** Wrapping element tag. Default `div`. */
|
|
21
|
+
as?: string
|
|
22
|
+
children?: Snippet
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let { gap = 12, align = 'stretch', justify = 'start', as = 'div', children }: Props = $props()
|
|
26
|
+
const g = $derived(typeof gap === 'number' ? `${gap}px` : gap)
|
|
27
|
+
const alignMap = { start: 'flex-start', center: 'center', end: 'flex-end', stretch: 'stretch' } as const
|
|
28
|
+
const justifyMap = { start: 'flex-start', center: 'center', end: 'flex-end', between: 'space-between', around: 'space-around' } as const
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<svelte:element this={as} class="sv-stack" style:gap={g} style:align-items={alignMap[align]} style:justify-content={justifyMap[justify]}>
|
|
32
|
+
{@render children?.()}
|
|
33
|
+
</svelte:element>
|
|
34
|
+
|
|
35
|
+
<style>
|
|
36
|
+
.sv-stack { display: flex; flex-direction: column; min-width: 0; }
|
|
37
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SvStack - a vertical flex layout primitive: stacks children in a column with a
|
|
3
|
+
* consistent `gap` and optional cross-axis `align`. The building block for form
|
|
4
|
+
* rows, card bodies, and sidebars. (For a horizontal row use SvGroup.)
|
|
5
|
+
*
|
|
6
|
+
* ```svelte
|
|
7
|
+
* <SvStack gap={12}><A /><B /><C /></SvStack>
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
import type { Snippet } from 'svelte';
|
|
11
|
+
type Props = {
|
|
12
|
+
/** Gap between children (px, or any CSS length). Default 12. */
|
|
13
|
+
gap?: number | string;
|
|
14
|
+
/** Cross-axis alignment. */
|
|
15
|
+
align?: 'start' | 'center' | 'end' | 'stretch';
|
|
16
|
+
/** Main-axis distribution. */
|
|
17
|
+
justify?: 'start' | 'center' | 'end' | 'between' | 'around';
|
|
18
|
+
/** Wrapping element tag. Default `div`. */
|
|
19
|
+
as?: string;
|
|
20
|
+
children?: Snippet;
|
|
21
|
+
};
|
|
22
|
+
declare const SvStack: import("svelte").Component<Props, {}, "">;
|
|
23
|
+
type SvStack = ReturnType<typeof SvStack>;
|
|
24
|
+
export default SvStack;
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
checked = false,
|
|
21
21
|
onChange,
|
|
22
22
|
disabled = false,
|
|
23
|
+
readonly = false,
|
|
23
24
|
size = 'md',
|
|
24
25
|
name,
|
|
25
26
|
ariaLabel,
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
// The styled switch is just a renderer over the headless core.
|
|
41
42
|
const sw = createSwitch({
|
|
42
43
|
checked: () => checked,
|
|
43
|
-
onChange: (v) => onChange?.(v),
|
|
44
|
+
onChange: (v) => { if (readonly) return; onChange?.(v) },
|
|
44
45
|
disabled: () => disabled,
|
|
45
46
|
ariaLabel: () => ariaLabel,
|
|
46
47
|
id: () => uid,
|
|
@@ -53,9 +54,12 @@
|
|
|
53
54
|
|
|
54
55
|
<SvField id={uid} {label} {hint} {error} {required} {dir}>
|
|
55
56
|
<button
|
|
57
|
+
role="switch"
|
|
56
58
|
class="sv-switch sv-switch--{size}"
|
|
57
59
|
class:is-on={checked}
|
|
60
|
+
class:is-readonly={readonly}
|
|
58
61
|
{...sw.switchProps()}
|
|
62
|
+
aria-readonly={readonly ? 'true' : undefined}
|
|
59
63
|
>
|
|
60
64
|
<span class="sv-switch__track">
|
|
61
65
|
{#if onLabel || offLabel}
|
|
@@ -77,6 +81,7 @@
|
|
|
77
81
|
.sv-switch--sm { --_h: 18px; --_w: 32px; }
|
|
78
82
|
.sv-switch--lg { --_h: 28px; --_w: 52px; }
|
|
79
83
|
.sv-switch[disabled] { opacity: 0.5; cursor: not-allowed; }
|
|
84
|
+
.sv-switch.is-readonly { cursor: default; }
|
|
80
85
|
.sv-switch__track {
|
|
81
86
|
position: relative; width: var(--_w); height: var(--_h); border-radius: 999px;
|
|
82
87
|
background: var(--sg-border, #cbd5e1); transition: background 0.16s;
|
package/dist/SvTagsInput.svelte
CHANGED
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
disabled = false,
|
|
34
34
|
unique = true,
|
|
35
35
|
max = Infinity,
|
|
36
|
+
size = 'md',
|
|
36
37
|
name,
|
|
37
38
|
ariaLabel,
|
|
38
39
|
invalid = false,
|
|
@@ -68,7 +69,7 @@
|
|
|
68
69
|
</script>
|
|
69
70
|
|
|
70
71
|
<SvField id={uid} {label} {hint} {error} {required} {dir}>
|
|
71
|
-
<div class="sv-tags" class:is-disabled={disabled} class:is-invalid={invalid} {...ti.rootProps()}>
|
|
72
|
+
<div class="sv-tags sv-tags--{size}" class:is-disabled={disabled} class:is-invalid={invalid} {...ti.rootProps()}>
|
|
72
73
|
{#each value as tag, i (tag + i)}
|
|
73
74
|
<span class="sv-tags__chip" {...ti.tagProps(i)}>
|
|
74
75
|
<span class="sv-tags__label">{tag}</span>
|
|
@@ -89,20 +90,23 @@
|
|
|
89
90
|
<style>
|
|
90
91
|
.sv-tags {
|
|
91
92
|
--_accent: var(--sg-accent, #2563eb);
|
|
92
|
-
|
|
93
|
+
--_minh: 34px; --_chip-h: 22px; --_fs: 13px;
|
|
94
|
+
display: flex; flex-wrap: wrap; gap: 5px; align-items: center; width: 280px; min-height: var(--_minh); padding: 4px 6px;
|
|
93
95
|
background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a);
|
|
94
96
|
border: 1px solid var(--sg-input-border, var(--sg-border, #cbd5e1)); border-radius: var(--sg-radius, 8px);
|
|
95
97
|
}
|
|
98
|
+
.sv-tags--sm { --_minh: 28px; --_chip-h: 19px; --_fs: 12px; }
|
|
99
|
+
.sv-tags--lg { --_minh: 40px; --_chip-h: 26px; --_fs: 15px; }
|
|
96
100
|
.sv-tags:focus-within { border-color: var(--_accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--_accent) 22%, transparent); }
|
|
97
101
|
.sv-tags.is-invalid { border-color: var(--sg-danger, #dc2626); }
|
|
98
102
|
.sv-tags.is-invalid:focus-within { box-shadow: 0 0 0 2px color-mix(in srgb, var(--sg-danger, #dc2626) 22%, transparent); }
|
|
99
103
|
.sv-tags.is-disabled { opacity: 0.6; }
|
|
100
104
|
.sv-tags__chip {
|
|
101
|
-
display: inline-flex; align-items: center; gap: 4px; height:
|
|
105
|
+
display: inline-flex; align-items: center; gap: 4px; height: var(--_chip-h); padding-block: 0; padding-inline: 8px 4px;
|
|
102
106
|
background: color-mix(in srgb, var(--_accent) 14%, transparent); color: var(--_accent);
|
|
103
|
-
border-radius: 5px; font-size:
|
|
107
|
+
border-radius: 5px; font-size: calc(var(--_fs) - 1px); font-weight: 600;
|
|
104
108
|
}
|
|
105
109
|
.sv-tags__x { background: none; border: 0; color: inherit; cursor: pointer; font-size: 15px; line-height: 1; padding: 0 2px; opacity: 0.7; }
|
|
106
110
|
.sv-tags__x:hover { opacity: 1; }
|
|
107
|
-
.sv-tags__input { flex: 1; min-width: 80px; border: 0; background: none; outline: none; color: inherit; font: inherit; font-size:
|
|
111
|
+
.sv-tags__input { flex: 1; min-width: 80px; border: 0; background: none; outline: none; color: inherit; font: inherit; font-size: var(--_fs); height: 24px; }
|
|
108
112
|
</style>
|