@svgrid/grid 2.1.21 → 2.1.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/README.md +9 -2
- package/dist/DockNodeView.svelte +306 -0
- package/dist/DockNodeView.svelte.d.ts +7 -0
- package/dist/DockPopoutHost.svelte +17 -0
- package/dist/DockPopoutHost.svelte.d.ts +14 -0
- package/dist/GridMenus.svelte +83 -3
- package/dist/SvAutoComplete.svelte +5 -3
- package/dist/SvCarousel.svelte +20 -42
- package/dist/SvColorInput.svelte +4 -3
- package/dist/SvColorInput.svelte.d.ts +1 -1
- package/dist/SvComboBox.svelte +7 -3
- package/dist/SvCommand.svelte +28 -85
- package/dist/SvCountryInput.svelte +1 -1
- package/dist/SvDateRangeInput.svelte +1 -1
- package/dist/SvDateTimePicker.svelte +9 -3
- package/dist/SvDateTimePicker.svelte.d.ts +3 -0
- package/dist/SvDockLayout.svelte +244 -0
- package/dist/SvDockLayout.svelte.d.ts +77 -0
- package/dist/SvDockManager.svelte +922 -0
- package/dist/SvDockManager.svelte.d.ts +106 -0
- package/dist/SvDrawer.svelte +69 -47
- package/dist/SvDrawer.svelte.d.ts +4 -0
- package/dist/SvDropDownList.svelte +85 -13
- package/dist/SvDropDownList.svelte.d.ts +3 -0
- package/dist/SvField.svelte +21 -4
- package/dist/SvField.svelte.d.ts +1 -0
- package/dist/SvForm.svelte +201 -91
- package/dist/SvForm.svelte.d.ts +15 -22
- package/dist/SvGrid.controller.svelte.d.ts +32 -1
- package/dist/SvGrid.controller.svelte.js +156 -27
- package/dist/SvGrid.css +193 -5
- package/dist/SvGrid.svelte +357 -12
- package/dist/SvGrid.types.d.ts +452 -1
- package/dist/SvGridDropdown.svelte +8 -1
- package/dist/SvGridSelect.svelte +1 -0
- package/dist/SvListBox.svelte +36 -14
- package/dist/SvMaskedInput.svelte +4 -3
- package/dist/SvMaskedInput.svelte.d.ts +1 -1
- package/dist/SvMenu.svelte +1 -1
- package/dist/SvMenuList.svelte +23 -71
- package/dist/SvMenuList.svelte.d.ts +2 -13
- package/dist/SvModal.svelte +12 -29
- package/dist/SvMultiSelect.svelte +1 -0
- package/dist/SvNumberInput.svelte +4 -3
- package/dist/SvNumberInput.svelte.d.ts +1 -1
- package/dist/SvPagination.svelte +18 -18
- package/dist/SvPasswordInput.svelte +3 -2
- package/dist/SvPasswordInput.svelte.d.ts +1 -1
- package/dist/SvPhoneInput.svelte +3 -2
- package/dist/SvPhoneInput.svelte.d.ts +1 -1
- package/dist/SvPopover.svelte +1 -1
- package/dist/SvStepper.svelte +6 -9
- package/dist/SvTagsInput.svelte +3 -2
- package/dist/SvTagsInput.svelte.d.ts +1 -1
- package/dist/SvTooltip.svelte +12 -23
- package/dist/SvTour.svelte +32 -4
- package/dist/SvTreeSelect.svelte +1 -0
- package/dist/build-api.js +76 -54
- package/dist/cdn/svgrid.js +10907 -6444
- package/dist/cdn/svgrid.svelte-external.js +9622 -5475
- package/dist/columns.js +6 -0
- package/dist/conditional-formatting.d.ts +67 -3
- package/dist/conditional-formatting.js +124 -17
- package/dist/createCarousel.svelte.d.ts +72 -0
- package/dist/createCarousel.svelte.js +80 -0
- package/dist/createCommand.svelte.d.ts +62 -0
- package/dist/createCommand.svelte.js +109 -0
- package/dist/createForm.svelte.d.ts +62 -0
- package/dist/createForm.svelte.js +374 -0
- package/dist/createMenu.svelte.d.ts +88 -0
- package/dist/createMenu.svelte.js +153 -0
- package/dist/createOverlay.svelte.d.ts +38 -0
- package/dist/createOverlay.svelte.js +68 -0
- package/dist/createPagination.d.ts +74 -0
- package/dist/createPagination.js +71 -0
- package/dist/createPopoverSelect.svelte.js +6 -12
- package/dist/createStepper.d.ts +44 -0
- package/dist/createStepper.js +26 -0
- package/dist/createTooltip.svelte.d.ts +32 -0
- package/dist/createTooltip.svelte.js +61 -0
- package/dist/datetime/timezone.d.ts +36 -0
- package/dist/datetime/timezone.js +110 -0
- package/dist/dock-context.d.ts +52 -0
- package/dist/dock-context.js +1 -0
- package/dist/dock-manager-model.d.ts +136 -0
- package/dist/dock-manager-model.js +514 -0
- package/dist/dock-model.d.ts +105 -0
- package/dist/dock-model.js +288 -0
- package/dist/dock-popout.d.ts +30 -0
- package/dist/dock-popout.js +81 -0
- package/dist/editor-contract.d.ts +2 -0
- package/dist/filter-operators.d.ts +6 -0
- package/dist/filter-operators.js +35 -2
- package/dist/filtering/excel-filters.d.ts +18 -1
- package/dist/filtering/excel-filters.js +56 -0
- package/dist/form-field.d.ts +73 -0
- package/dist/form-field.js +15 -0
- package/dist/index.d.ts +25 -5
- package/dist/index.js +40 -4
- package/dist/list-nav.d.ts +18 -0
- package/dist/list-nav.js +30 -0
- package/dist/list-option.d.ts +2 -0
- package/dist/menu-item.d.ts +20 -0
- package/dist/menu-item.js +1 -0
- package/dist/menus.d.ts +5 -0
- package/dist/menus.js +68 -16
- package/dist/panel-resize.d.ts +25 -0
- package/dist/panel-resize.js +40 -0
- package/dist/popover.d.ts +29 -3
- package/dist/popover.js +22 -6
- package/dist/recurrence.d.ts +33 -7
- package/dist/recurrence.js +151 -13
- package/dist/row-drag.d.ts +8 -0
- package/dist/row-drag.js +11 -0
- package/dist/scheduler-ical.d.ts +30 -0
- package/dist/scheduler-ical.js +180 -0
- package/dist/scheduler-model.d.ts +269 -0
- package/dist/scheduler-model.js +602 -0
- package/dist/scheduler-view.svelte.d.ts +22 -0
- package/dist/scheduler-view.svelte.js +13 -0
- package/dist/spreadsheet.js +20 -12
- package/dist/ui-app.types.d.ts +3 -0
- package/package.json +5 -1
- package/src/DockNodeView.svelte +306 -0
- package/src/DockPopoutHost.svelte +17 -0
- package/src/GridMenus.svelte +83 -3
- package/src/SvAutoComplete.svelte +5 -3
- package/src/SvCarousel.svelte +20 -42
- package/src/SvCarousel.test.ts +68 -0
- package/src/SvColorInput.svelte +4 -3
- package/src/SvComboBox.svelte +7 -3
- package/src/SvCommand.svelte +28 -85
- package/src/SvCommand.test.ts +78 -0
- package/src/SvCountryInput.svelte +1 -1
- package/src/SvDateRangeInput.svelte +1 -1
- package/src/SvDateTimePicker.svelte +9 -3
- package/src/SvDockLayout.svelte +244 -0
- package/src/SvDockManager.svelte +922 -0
- package/src/SvDrawer.svelte +69 -47
- package/src/SvDropDownList.svelte +85 -13
- package/src/SvField.svelte +21 -4
- package/src/SvForm.svelte +201 -91
- package/src/SvForm.test.ts +299 -0
- package/src/SvGrid.controller.svelte.ts +152 -28
- package/src/SvGrid.css +193 -5
- package/src/SvGrid.svelte +357 -12
- package/src/SvGrid.types.ts +463 -1
- package/src/SvGridDropdown.svelte +8 -1
- package/src/SvGridSelect.svelte +1 -0
- package/src/SvListBox.svelte +36 -14
- package/src/SvMaskedInput.svelte +4 -3
- package/src/SvMenu.svelte +1 -1
- package/src/SvMenu.test.ts +24 -0
- package/src/SvMenuList.svelte +23 -71
- package/src/SvModal.svelte +12 -29
- package/src/SvModal.test.ts +82 -0
- package/src/SvMultiSelect.svelte +1 -0
- package/src/SvNumberInput.svelte +4 -3
- package/src/SvPagination.svelte +18 -18
- package/src/SvPasswordInput.svelte +3 -2
- package/src/SvPhoneInput.svelte +3 -2
- package/src/SvPopover.svelte +1 -1
- package/src/SvStepper.svelte +6 -9
- package/src/SvTagsInput.svelte +3 -2
- package/src/SvTooltip.svelte +12 -23
- package/src/SvTooltip.test.ts +73 -0
- package/src/SvTour.svelte +32 -4
- package/src/SvTreeSelect.svelte +1 -0
- package/src/build-api.ts +74 -51
- package/src/columns.ts +5 -0
- package/src/conditional-formatting.test.ts +104 -0
- package/src/conditional-formatting.ts +176 -19
- package/src/createCarousel.svelte.ts +104 -0
- package/src/createCommand.svelte.ts +125 -0
- package/src/createForm.svelte.ts +333 -0
- package/src/createMenu.svelte.ts +202 -0
- package/src/createOverlay.svelte.ts +101 -0
- package/src/createPagination.test.ts +65 -0
- package/src/createPagination.ts +84 -0
- package/src/createPopoverSelect.svelte.ts +5 -9
- package/src/createStepper.test.ts +62 -0
- package/src/createStepper.ts +61 -0
- package/src/createTooltip.svelte.ts +86 -0
- package/src/datetime/timezone.test.ts +69 -0
- package/src/datetime/timezone.ts +134 -0
- package/src/dock-context.ts +47 -0
- package/src/dock-manager-model.test.ts +275 -0
- package/src/dock-manager-model.ts +593 -0
- package/src/dock-model.test.ts +163 -0
- package/src/dock-model.ts +368 -0
- package/src/dock-popout.ts +94 -0
- package/src/editor-contract.ts +2 -0
- package/src/filter-operators.test.ts +59 -0
- package/src/filter-operators.ts +35 -2
- package/src/filtering/excel-filters.test.ts +86 -1
- package/src/filtering/excel-filters.ts +65 -0
- package/src/form-field.ts +87 -0
- package/src/index.ts +158 -4
- package/src/list-nav.test.ts +49 -0
- package/src/list-nav.ts +29 -0
- package/src/list-option.ts +2 -0
- package/src/menu-item.ts +21 -0
- package/src/menus.test.ts +48 -0
- package/src/menus.ts +73 -16
- package/src/panel-resize.ts +52 -0
- package/src/popover.bounds.test.ts +93 -0
- package/src/popover.ts +51 -7
- package/src/recurrence.test.ts +88 -1
- package/src/recurrence.ts +166 -15
- package/src/row-drag.ts +10 -0
- package/src/scheduler-ical.test.ts +68 -0
- package/src/scheduler-ical.ts +180 -0
- package/src/scheduler-model.test.ts +546 -0
- package/src/scheduler-model.ts +853 -0
- package/src/scheduler-view.svelte.ts +33 -0
- package/src/spreadsheet.ts +20 -12
- package/src/svgrid.effect-driven-filter.svelte.test.ts +116 -0
- package/src/ui-app.types.ts +3 -0
- package/src/virtual.test.ts +6 -4
package/dist/SvMenuList.svelte
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
separator?: boolean
|
|
7
|
-
icon?: Snippet
|
|
8
|
-
/** Right-aligned hint, e.g. a keyboard shortcut. */
|
|
9
|
-
shortcut?: string
|
|
10
|
-
disabled?: boolean
|
|
11
|
-
/** Nested submenu items (renders a flyout). */
|
|
12
|
-
children?: MenuItem[]
|
|
13
|
-
onSelect?: () => void
|
|
14
|
-
}
|
|
2
|
+
// MenuItem lives in a plain `.ts` module (breaks a Vite dev import cycle); it is
|
|
3
|
+
// re-exported here so existing `import { type MenuItem } from './SvMenuList.svelte'`
|
|
4
|
+
// sites keep working.
|
|
5
|
+
export type { MenuItem } from './menu-item'
|
|
15
6
|
</script>
|
|
16
7
|
|
|
17
8
|
<script lang="ts">
|
|
@@ -22,6 +13,8 @@
|
|
|
22
13
|
*/
|
|
23
14
|
import Self from './SvMenuList.svelte'
|
|
24
15
|
import { type EditorDir } from './editor-contract'
|
|
16
|
+
import type { MenuItem } from './menu-item'
|
|
17
|
+
import { createMenu } from './createMenu.svelte'
|
|
25
18
|
|
|
26
19
|
let {
|
|
27
20
|
items,
|
|
@@ -46,56 +39,24 @@
|
|
|
46
39
|
dir?: EditorDir
|
|
47
40
|
} = $props()
|
|
48
41
|
|
|
49
|
-
// Indexes of real (non-separator) items, for roving focus.
|
|
50
|
-
const focusable = $derived(items.map((it, i) => (it.separator || it.disabled ? -1 : i)).filter((i) => i >= 0))
|
|
51
|
-
let active = $state(-1)
|
|
52
|
-
let openSub = $state(-1)
|
|
53
42
|
let listEl = $state<HTMLDivElement | null>(null)
|
|
54
43
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (item.children?.length) { openSub = items.indexOf(item); return }
|
|
68
|
-
item.onSelect?.()
|
|
69
|
-
onselect(item)
|
|
70
|
-
onclose()
|
|
71
|
-
}
|
|
72
|
-
function onKeydown(e: KeyboardEvent, i: number) {
|
|
73
|
-
const item = items[i]
|
|
74
|
-
const rtl = dir === 'rtl'
|
|
75
|
-
// Open this item's submenu (opposite key under rtl).
|
|
76
|
-
const openThis = () => { if (item?.children?.length) { e.preventDefault(); openSub = i } }
|
|
77
|
-
// Collapse just THIS list back into its parent item (opposite key under
|
|
78
|
-
// rtl) - `oncollapse` is local to this nesting level, unlike `onclose`.
|
|
79
|
-
const collapseThis = () => { if (submenu) { e.preventDefault(); oncollapse?.() } }
|
|
80
|
-
switch (e.key) {
|
|
81
|
-
case 'ArrowDown': e.preventDefault(); move(1); break
|
|
82
|
-
case 'ArrowUp': e.preventDefault(); move(-1); break
|
|
83
|
-
case 'Home': e.preventDefault(); focusItem(focusable[0] ?? 0); break
|
|
84
|
-
case 'End': e.preventDefault(); focusItem(focusable.at(-1) ?? 0); break
|
|
85
|
-
case 'ArrowRight': (rtl ? collapseThis : openThis)(); break
|
|
86
|
-
case 'ArrowLeft': (rtl ? openThis : collapseThis)(); break
|
|
87
|
-
case 'Enter':
|
|
88
|
-
case ' ': e.preventDefault(); if (item) choose(item); break
|
|
89
|
-
case 'Escape': e.preventDefault(); onclose(); break
|
|
90
|
-
// Tab leaves the menu entirely (WAI-ARIA menu button pattern): close the
|
|
91
|
-
// whole tree rather than trapping focus inside a portalled panel.
|
|
92
|
-
case 'Tab': onclose(); break
|
|
93
|
-
}
|
|
94
|
-
}
|
|
44
|
+
// Roving focus, submenu open/collapse and the full keyboard contract live in
|
|
45
|
+
// the shared headless core; this component only renders + drives real DOM
|
|
46
|
+
// focus (the core stays DOM-free). See createMenu.svelte.ts.
|
|
47
|
+
const menu = createMenu({
|
|
48
|
+
items: () => items,
|
|
49
|
+
onSelect: (item) => onselect(item),
|
|
50
|
+
onClose: () => onclose(),
|
|
51
|
+
submenu: () => submenu,
|
|
52
|
+
onCollapse: () => oncollapse?.(),
|
|
53
|
+
dir: () => dir,
|
|
54
|
+
focusItem: (i) => queueMicrotask(() => listEl?.querySelector<HTMLElement>(`[data-mi="${i}"]`)?.focus()),
|
|
55
|
+
})
|
|
95
56
|
</script>
|
|
96
57
|
|
|
97
58
|
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
98
|
-
<div bind:this={listEl} class="sv-menu__list" class:is-submenu={submenu}
|
|
59
|
+
<div bind:this={listEl} class="sv-menu__list" class:is-submenu={submenu} {...menu.listProps()}>
|
|
99
60
|
{#each items as item, i (i)}
|
|
100
61
|
{#if item.separator}
|
|
101
62
|
<div class="sv-menu__sep" role="separator"></div>
|
|
@@ -104,30 +65,21 @@
|
|
|
104
65
|
<button
|
|
105
66
|
type="button"
|
|
106
67
|
class="sv-menu__item"
|
|
107
|
-
class:is-active={
|
|
108
|
-
|
|
109
|
-
role="menuitem"
|
|
110
|
-
aria-haspopup={item.children?.length ? 'menu' : undefined}
|
|
111
|
-
aria-expanded={item.children?.length ? openSub === i : undefined}
|
|
112
|
-
aria-disabled={item.disabled || undefined}
|
|
113
|
-
disabled={item.disabled}
|
|
114
|
-
tabindex={active === i || (active === -1 && i === focusable[0]) ? 0 : -1}
|
|
115
|
-
onpointerenter={() => { if (!item.disabled) { active = i; openSub = item.children?.length ? i : -1 } }}
|
|
116
|
-
onkeydown={(e) => onKeydown(e, i)}
|
|
117
|
-
onclick={() => choose(item)}
|
|
68
|
+
class:is-active={menu.isActive(i)}
|
|
69
|
+
{...menu.itemProps(i)}
|
|
118
70
|
>
|
|
119
71
|
<span class="sv-menu__icon">{#if item.icon}{@render item.icon()}{/if}</span>
|
|
120
72
|
<span class="sv-menu__label">{item.label}</span>
|
|
121
73
|
{#if item.shortcut}<span class="sv-menu__shortcut">{item.shortcut}</span>{/if}
|
|
122
74
|
{#if item.children?.length}<svg class="sv-menu__chev" viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6" /></svg>{/if}
|
|
123
75
|
</button>
|
|
124
|
-
{#if item.children?.length &&
|
|
76
|
+
{#if item.children?.length && menu.isSubOpen(i)}
|
|
125
77
|
<div class="sv-menu__flyout">
|
|
126
78
|
<Self
|
|
127
79
|
items={item.children}
|
|
128
80
|
{onclose}
|
|
129
81
|
{onselect}
|
|
130
|
-
oncollapse={() =>
|
|
82
|
+
oncollapse={() => menu.focus(i)}
|
|
131
83
|
{dir}
|
|
132
84
|
submenu
|
|
133
85
|
/>
|
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
export type MenuItem = {
|
|
3
|
-
/** Item text. Omit + set `separator` for a divider. */
|
|
4
|
-
label?: string;
|
|
5
|
-
separator?: boolean;
|
|
6
|
-
icon?: Snippet;
|
|
7
|
-
/** Right-aligned hint, e.g. a keyboard shortcut. */
|
|
8
|
-
shortcut?: string;
|
|
9
|
-
disabled?: boolean;
|
|
10
|
-
/** Nested submenu items (renders a flyout). */
|
|
11
|
-
children?: MenuItem[];
|
|
12
|
-
onSelect?: () => void;
|
|
13
|
-
};
|
|
1
|
+
export type { MenuItem } from './menu-item';
|
|
14
2
|
import { type EditorDir } from './editor-contract';
|
|
3
|
+
import type { MenuItem } from './menu-item';
|
|
15
4
|
type $$ComponentProps = {
|
|
16
5
|
items: ReadonlyArray<MenuItem>;
|
|
17
6
|
/** Close the WHOLE menu (a leaf was chosen, Escape, or outside-click). */
|
package/dist/SvModal.svelte
CHANGED
|
@@ -15,9 +15,7 @@
|
|
|
15
15
|
import type { Snippet } from 'svelte'
|
|
16
16
|
import { portalToBody } from './popover'
|
|
17
17
|
import { nextEditorId } from './editor-contract'
|
|
18
|
-
import {
|
|
19
|
-
import { lockScroll } from './a11y/scroll-lock'
|
|
20
|
-
import { createDismissableLayer } from './a11y/dismissable'
|
|
18
|
+
import { createOverlay } from './createOverlay.svelte'
|
|
21
19
|
|
|
22
20
|
type Props = {
|
|
23
21
|
open?: boolean
|
|
@@ -55,28 +53,16 @@
|
|
|
55
53
|
|
|
56
54
|
function close() { open = false; onClose?.() }
|
|
57
55
|
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const layer = createDismissableLayer({
|
|
69
|
-
element: () => dialogEl,
|
|
70
|
-
onDismiss: (reason) => {
|
|
71
|
-
if (reason === 'escape' ? closeOnEsc : closeOnBackdrop) close()
|
|
72
|
-
},
|
|
73
|
-
})
|
|
74
|
-
layer.activate()
|
|
75
|
-
return () => {
|
|
76
|
-
layer.release()
|
|
77
|
-
unlockScroll()
|
|
78
|
-
trap.release()
|
|
79
|
-
}
|
|
56
|
+
// Focus trap + scroll lock + Escape/backdrop dismissal come from the shared
|
|
57
|
+
// headless overlay core; this component resets its transient drag/resize on
|
|
58
|
+
// open and renders the panel. See createOverlay.svelte.ts.
|
|
59
|
+
const overlay = createOverlay({
|
|
60
|
+
open: () => open,
|
|
61
|
+
getDialog: () => dialogEl,
|
|
62
|
+
onClose: close,
|
|
63
|
+
closeOnEsc: () => closeOnEsc,
|
|
64
|
+
closeOnBackdrop: () => closeOnBackdrop,
|
|
65
|
+
onOpen: () => { drag = { x: 0, y: 0 }; box = null },
|
|
80
66
|
})
|
|
81
67
|
|
|
82
68
|
// --- Drag (header) ---------------------------------------------------------
|
|
@@ -130,10 +116,7 @@
|
|
|
130
116
|
bind:this={dialogEl}
|
|
131
117
|
class="sv-modal sv-modal--{size}"
|
|
132
118
|
class:is-resized={!!box}
|
|
133
|
-
|
|
134
|
-
aria-modal="true"
|
|
135
|
-
aria-labelledby={title ? titleId : undefined}
|
|
136
|
-
tabindex="-1"
|
|
119
|
+
{...overlay.dialogProps({ labelledBy: title ? titleId : undefined })}
|
|
137
120
|
style:transform={`translate(calc(-50% + ${drag.x}px), calc(-50% + ${drag.y}px))`}
|
|
138
121
|
style:width={box ? `${box.w}px` : undefined}
|
|
139
122
|
style:height={box ? `${box.h}px` : undefined}
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
let {
|
|
34
|
-
value = null,
|
|
34
|
+
value = $bindable<number | null>(null),
|
|
35
35
|
onChange,
|
|
36
36
|
min = -Infinity,
|
|
37
37
|
max = Infinity,
|
|
@@ -62,7 +62,8 @@
|
|
|
62
62
|
|
|
63
63
|
const num = createNumberInput({
|
|
64
64
|
value: () => value,
|
|
65
|
-
|
|
65
|
+
// Write `value` (so `bind:value` works) AND fire `onChange` (callback users).
|
|
66
|
+
onChange: (v) => { value = v; onChange?.(v) },
|
|
66
67
|
min: () => min,
|
|
67
68
|
max: () => max,
|
|
68
69
|
step: () => step,
|
|
@@ -86,7 +87,7 @@
|
|
|
86
87
|
<div class="sv-num sv-num--{size}" class:is-disabled={disabled} class:is-invalid={invalid}>
|
|
87
88
|
<input class="sv-num__input" {...num.inputProps()} />
|
|
88
89
|
{#if clearable && value != null && !disabled && !readonly}
|
|
89
|
-
<button type="button" class="sv-num__clear" aria-label="Clear" tabindex="-1" onclick={() => onChange?.(null)}>
|
|
90
|
+
<button type="button" class="sv-num__clear" aria-label="Clear" tabindex="-1" onclick={() => { value = null; onChange?.(null) }}>
|
|
90
91
|
<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18" /></svg>
|
|
91
92
|
</button>
|
|
92
93
|
{/if}
|
|
@@ -15,6 +15,6 @@ type Props = SvEditorProps & {
|
|
|
15
15
|
/** Show a clear (x) button when there is a value. */
|
|
16
16
|
clearable?: boolean;
|
|
17
17
|
};
|
|
18
|
-
declare const SvNumberInput: import("svelte").Component<Props, {}, "">;
|
|
18
|
+
declare const SvNumberInput: import("svelte").Component<Props, {}, "value">;
|
|
19
19
|
type SvNumberInput = ReturnType<typeof SvNumberInput>;
|
|
20
20
|
export default SvNumberInput;
|
package/dist/SvPagination.svelte
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* <SvPagination page={p} pageCount={20} onChange={(n) => (p = n)} />
|
|
10
10
|
* ```
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
12
|
+
import { createPagination } from './createPagination'
|
|
13
13
|
|
|
14
14
|
type Props = {
|
|
15
15
|
/** 1-based current page. */
|
|
@@ -38,23 +38,26 @@
|
|
|
38
38
|
ariaLabel = 'Pagination',
|
|
39
39
|
}: Props = $props()
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
// Page-range math + navigation live in the shared headless core.
|
|
42
|
+
const pg = createPagination({
|
|
43
|
+
page: () => page,
|
|
44
|
+
pageCount: () => pageCount,
|
|
45
|
+
onChange,
|
|
46
|
+
siblingCount: () => siblingCount,
|
|
47
|
+
boundaryCount: () => boundaryCount,
|
|
48
|
+
disabled: () => disabled,
|
|
49
|
+
})
|
|
47
50
|
</script>
|
|
48
51
|
|
|
49
|
-
<nav class="sv-pg sv-pg--{size}"
|
|
52
|
+
<nav class="sv-pg sv-pg--{size}" {...pg.navProps(ariaLabel)}>
|
|
50
53
|
<ul class="sv-pg__list">
|
|
51
54
|
{#if showFirstLast}
|
|
52
|
-
<li><button type="button" class="sv-pg__btn"
|
|
55
|
+
<li><button type="button" class="sv-pg__btn" {...pg.firstProps()}>«</button></li>
|
|
53
56
|
{/if}
|
|
54
57
|
{#if showPrevNext}
|
|
55
|
-
<li><button type="button" class="sv-pg__btn"
|
|
58
|
+
<li><button type="button" class="sv-pg__btn" {...pg.prevProps()}>‹</button></li>
|
|
56
59
|
{/if}
|
|
57
|
-
{#each items as it, i (typeof it === 'number' ? `p${it}` : `${it}-${i}`)}
|
|
60
|
+
{#each pg.items as it, i (typeof it === 'number' ? `p${it}` : `${it}-${i}`)}
|
|
58
61
|
{#if it === 'ellipsis-left' || it === 'ellipsis-right'}
|
|
59
62
|
<li><span class="sv-pg__ellipsis" aria-hidden="true">…</span></li>
|
|
60
63
|
{:else}
|
|
@@ -62,20 +65,17 @@
|
|
|
62
65
|
<button
|
|
63
66
|
type="button"
|
|
64
67
|
class="sv-pg__btn"
|
|
65
|
-
class:is-active={it
|
|
66
|
-
|
|
67
|
-
aria-label={`Page ${it}`}
|
|
68
|
-
{disabled}
|
|
69
|
-
onclick={() => go(it)}
|
|
68
|
+
class:is-active={pg.isActive(it)}
|
|
69
|
+
{...pg.pageButtonProps(it)}
|
|
70
70
|
>{it}</button>
|
|
71
71
|
</li>
|
|
72
72
|
{/if}
|
|
73
73
|
{/each}
|
|
74
74
|
{#if showPrevNext}
|
|
75
|
-
<li><button type="button" class="sv-pg__btn"
|
|
75
|
+
<li><button type="button" class="sv-pg__btn" {...pg.nextProps()}>›</button></li>
|
|
76
76
|
{/if}
|
|
77
77
|
{#if showFirstLast}
|
|
78
|
-
<li><button type="button" class="sv-pg__btn"
|
|
78
|
+
<li><button type="button" class="sv-pg__btn" {...pg.lastProps()}>»</button></li>
|
|
79
79
|
{/if}
|
|
80
80
|
</ul>
|
|
81
81
|
</nav>
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
let {
|
|
34
|
-
value = '',
|
|
34
|
+
value = $bindable(''),
|
|
35
35
|
onChange,
|
|
36
36
|
placeholder,
|
|
37
37
|
disabled = false,
|
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
|
|
59
59
|
const pw = createPasswordInput({
|
|
60
60
|
value: () => value,
|
|
61
|
-
|
|
61
|
+
// Write `value` (so `bind:value` works) AND fire `onChange` (callback users).
|
|
62
|
+
onChange: (v) => { value = v; onChange?.(v) },
|
|
62
63
|
placeholder: () => placeholder,
|
|
63
64
|
disabled: () => disabled,
|
|
64
65
|
readonly: () => readonly,
|
|
@@ -20,6 +20,6 @@ type Props = SvEditorProps & {
|
|
|
20
20
|
/** Override the built-in strings (reveal toggle + strength labels). */
|
|
21
21
|
messages?: Partial<PasswordMessages>;
|
|
22
22
|
};
|
|
23
|
-
declare const SvPasswordInput: import("svelte").Component<Props, {}, "">;
|
|
23
|
+
declare const SvPasswordInput: import("svelte").Component<Props, {}, "value">;
|
|
24
24
|
type SvPasswordInput = ReturnType<typeof SvPasswordInput>;
|
|
25
25
|
export default SvPasswordInput;
|
package/dist/SvPhoneInput.svelte
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
let {
|
|
30
|
-
value = '',
|
|
30
|
+
value = $bindable(''),
|
|
31
31
|
onChange,
|
|
32
32
|
country = 'US',
|
|
33
33
|
disabled = false,
|
|
@@ -52,7 +52,8 @@
|
|
|
52
52
|
|
|
53
53
|
const ph = createPhoneInput({
|
|
54
54
|
value: () => value,
|
|
55
|
-
|
|
55
|
+
// Write `value` (so `bind:value` works) AND fire `onChange` (callback users).
|
|
56
|
+
onChange: (v, parts) => { value = v; onChange?.(v, parts) },
|
|
56
57
|
country: () => country,
|
|
57
58
|
disabled: () => disabled,
|
|
58
59
|
readonly: () => readonly,
|
|
@@ -13,6 +13,6 @@ type Props = SvEditorProps & {
|
|
|
13
13
|
/** Override the built-in strings (e.g. the country selector aria-label). */
|
|
14
14
|
messages?: Partial<PhoneMessages>;
|
|
15
15
|
};
|
|
16
|
-
declare const SvPhoneInput: import("svelte").Component<Props, {}, "">;
|
|
16
|
+
declare const SvPhoneInput: import("svelte").Component<Props, {}, "value">;
|
|
17
17
|
type SvPhoneInput = ReturnType<typeof SvPhoneInput>;
|
|
18
18
|
export default SvPhoneInput;
|
package/dist/SvPopover.svelte
CHANGED
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
|
|
50
50
|
let anchorEl = $state<HTMLSpanElement | null>(null)
|
|
51
51
|
let panelEl = $state<HTMLDivElement | null>(null)
|
|
52
|
-
let rect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false })
|
|
52
|
+
let rect = $state<AnchoredRect>({ top: 0, left: 0, width: 0, openUpward: false, maxHeight: 0, availHeight: 0 })
|
|
53
53
|
|
|
54
54
|
function setOpen(v: boolean) { if (v !== open) { open = v; onOpenChange?.(v) } }
|
|
55
55
|
function toggle() { setOpen(!open) }
|
package/dist/SvStepper.svelte
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* ```
|
|
15
15
|
*/
|
|
16
16
|
import type { StepItem } from './ui-app.types'
|
|
17
|
+
import { createStepper } from './createStepper'
|
|
17
18
|
|
|
18
19
|
type Props = {
|
|
19
20
|
steps: ReadonlyArray<StepItem>
|
|
@@ -27,22 +28,18 @@
|
|
|
27
28
|
|
|
28
29
|
let { steps, current, onChange, linear = true, orientation = 'horizontal' }: Props = $props()
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const clickable = (i: number) => (linear ? i <= current : true)
|
|
33
|
-
const go = (i: number) => { if (clickable(i) && i !== current) onChange?.(i) }
|
|
31
|
+
// Status, clickability and navigation live in the shared headless core.
|
|
32
|
+
const st = createStepper({ count: () => steps.length, current: () => current, onChange, linear: () => linear })
|
|
34
33
|
</script>
|
|
35
34
|
|
|
36
35
|
<ol class="sv-step sv-step--{orientation}">
|
|
37
36
|
{#each steps as step, i (i)}
|
|
38
|
-
{@const status = statusOf(i)}
|
|
39
|
-
<li class="sv-step__item is-{status}" class:is-clickable={clickable(i)}>
|
|
37
|
+
{@const status = st.statusOf(i)}
|
|
38
|
+
<li class="sv-step__item is-{status}" class:is-clickable={st.clickable(i)}>
|
|
40
39
|
<button
|
|
41
40
|
type="button"
|
|
42
41
|
class="sv-step__btn"
|
|
43
|
-
|
|
44
|
-
disabled={!clickable(i)}
|
|
45
|
-
onclick={() => go(i)}
|
|
42
|
+
{...st.stepButtonProps(i)}
|
|
46
43
|
>
|
|
47
44
|
<span class="sv-step__marker" aria-hidden="true">
|
|
48
45
|
{#if status === 'complete'}✓{:else}{i + 1}{/if}
|
package/dist/SvTagsInput.svelte
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
let {
|
|
30
|
-
value = [],
|
|
30
|
+
value = $bindable<string[]>([]),
|
|
31
31
|
onChange,
|
|
32
32
|
placeholder = 'Add tag…',
|
|
33
33
|
disabled = false,
|
|
@@ -51,7 +51,8 @@
|
|
|
51
51
|
|
|
52
52
|
const ti = createTagsInput({
|
|
53
53
|
value: () => value,
|
|
54
|
-
|
|
54
|
+
// Write `value` (so `bind:value` works) AND fire `onChange` (callback users).
|
|
55
|
+
onChange: (t) => { value = t; onChange?.(t) },
|
|
55
56
|
disabled: () => disabled,
|
|
56
57
|
unique: () => unique,
|
|
57
58
|
max: () => max,
|
|
@@ -14,6 +14,6 @@ type Props = SvEditorProps & {
|
|
|
14
14
|
/** Override the built-in strings (draft + remove aria-labels). */
|
|
15
15
|
messages?: Partial<TagsMessages>;
|
|
16
16
|
};
|
|
17
|
-
declare const SvTagsInput: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
declare const SvTagsInput: import("svelte").Component<Props, {}, "value">;
|
|
18
18
|
type SvTagsInput = ReturnType<typeof SvTagsInput>;
|
|
19
19
|
export default SvTagsInput;
|
package/dist/SvTooltip.svelte
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { Snippet } from 'svelte'
|
|
14
14
|
import { portalToBody, popIn } from './popover'
|
|
15
|
-
import { nextEditorId } from './editor-contract'
|
|
16
15
|
import { getFocusable } from './a11y/focus-trap'
|
|
16
|
+
import { createTooltip } from './createTooltip.svelte'
|
|
17
17
|
|
|
18
18
|
type Props = {
|
|
19
19
|
text: string
|
|
@@ -28,10 +28,12 @@
|
|
|
28
28
|
|
|
29
29
|
let anchorEl = $state<HTMLSpanElement | null>(null)
|
|
30
30
|
let tipEl = $state<HTMLDivElement | null>(null)
|
|
31
|
-
let open = $state(false)
|
|
32
31
|
let pos = $state({ top: 0, left: 0, arrowX: 12 })
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
|
|
33
|
+
// Open state, show-delay and Escape-to-hide live in the shared headless core;
|
|
34
|
+
// this component owns only the DOM concerns (positioning + describedby merge).
|
|
35
|
+
const tip = createTooltip({ text: () => text, disabled: () => disabled, delay: () => delay })
|
|
36
|
+
const tid = tip.tipId
|
|
35
37
|
|
|
36
38
|
function place() {
|
|
37
39
|
if (!anchorEl) return
|
|
@@ -46,25 +48,16 @@
|
|
|
46
48
|
pos = { top, left, arrowX }
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
if (disabled || !text) return
|
|
51
|
-
clearTimeout(showTimer)
|
|
52
|
-
showTimer = setTimeout(() => { open = true; queueMicrotask(place) }, delay)
|
|
53
|
-
}
|
|
54
|
-
function hide() { clearTimeout(showTimer); open = false }
|
|
55
|
-
|
|
51
|
+
// Reposition while open (and on scroll/resize). Escape-to-hide is the core's.
|
|
56
52
|
$effect(() => {
|
|
57
|
-
if (!open) return
|
|
58
|
-
place
|
|
53
|
+
if (!tip.open) return
|
|
54
|
+
queueMicrotask(place)
|
|
59
55
|
const rp = () => place()
|
|
60
56
|
window.addEventListener('scroll', rp, true)
|
|
61
57
|
window.addEventListener('resize', rp)
|
|
62
|
-
const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') hide() }
|
|
63
|
-
document.addEventListener('keydown', onKey)
|
|
64
58
|
return () => {
|
|
65
59
|
window.removeEventListener('scroll', rp, true)
|
|
66
60
|
window.removeEventListener('resize', rp)
|
|
67
|
-
document.removeEventListener('keydown', onKey)
|
|
68
61
|
}
|
|
69
62
|
})
|
|
70
63
|
|
|
@@ -75,7 +68,7 @@
|
|
|
75
68
|
// announced. Mirror the id onto that child too, merging with (and later
|
|
76
69
|
// restoring) any aria-describedby it already carries.
|
|
77
70
|
$effect(() => {
|
|
78
|
-
if (!open || !anchorEl) return
|
|
71
|
+
if (!tip.open || !anchorEl) return
|
|
79
72
|
const child = getFocusable(anchorEl)[0]
|
|
80
73
|
if (!child) return
|
|
81
74
|
const before = child.getAttribute('aria-describedby')
|
|
@@ -96,16 +89,12 @@
|
|
|
96
89
|
<span
|
|
97
90
|
bind:this={anchorEl}
|
|
98
91
|
class="sv-tip__anchor"
|
|
99
|
-
|
|
100
|
-
onpointerenter={show}
|
|
101
|
-
onpointerleave={hide}
|
|
102
|
-
onfocusin={show}
|
|
103
|
-
onfocusout={hide}
|
|
92
|
+
{...tip.anchorProps()}
|
|
104
93
|
>
|
|
105
94
|
{@render children?.()}
|
|
106
95
|
</span>
|
|
107
96
|
|
|
108
|
-
{#if open}
|
|
97
|
+
{#if tip.open}
|
|
109
98
|
<div
|
|
110
99
|
bind:this={tipEl}
|
|
111
100
|
id={tid}
|
package/dist/SvTour.svelte
CHANGED
|
@@ -98,14 +98,34 @@
|
|
|
98
98
|
})
|
|
99
99
|
|
|
100
100
|
const POP_W = 300
|
|
101
|
+
// Measured popover height, so placement can keep the WHOLE popover on-screen
|
|
102
|
+
// (long content + tall targets used to push it off the top).
|
|
103
|
+
let popH = $state(200)
|
|
101
104
|
const pop = $derived.by(() => {
|
|
102
105
|
if (typeof window === 'undefined' || !rect) {
|
|
103
106
|
return { top: '50%', left: '50%', transform: 'translate(-50%, -50%)', place: 'center' as const }
|
|
104
107
|
}
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
const vw = window.innerWidth
|
|
109
|
+
const vh = window.innerHeight
|
|
110
|
+
const gap = 12
|
|
111
|
+
const h = popH || 200
|
|
112
|
+
const clampTop = (t: number) => Math.max(gap, Math.min(t, vh - h - gap))
|
|
113
|
+
const clampLeft = (l: number) => Math.max(gap, Math.min(l, vw - POP_W - gap))
|
|
114
|
+
// Prefer directly below the target, then directly above - but only when the
|
|
115
|
+
// target is short enough to leave room for the whole popover.
|
|
116
|
+
if (vh - rect.bottom >= h + gap) {
|
|
117
|
+
return { top: `${rect.bottom + 10}px`, left: `${clampLeft(rect.left)}px`, transform: 'none', place: 'bottom' as const }
|
|
118
|
+
}
|
|
119
|
+
if (rect.top >= h + gap) {
|
|
120
|
+
return { top: `${clampTop(rect.top - 10 - h)}px`, left: `${clampLeft(rect.left)}px`, transform: 'none', place: 'top' as const }
|
|
121
|
+
}
|
|
122
|
+
// Tall target (a full-height rail / canvas / inspector): sit BESIDE it,
|
|
123
|
+
// vertically centred on the target, on whichever side has room.
|
|
124
|
+
const top = clampTop(rect.top + rect.height / 2 - h / 2)
|
|
125
|
+
if (vw - rect.right >= POP_W + gap) return { top: `${top}px`, left: `${rect.right + 10}px`, transform: 'none', place: 'center' as const }
|
|
126
|
+
if (rect.left >= POP_W + gap) return { top: `${top}px`, left: `${rect.left - POP_W - 10}px`, transform: 'none', place: 'center' as const }
|
|
127
|
+
// No side room either - centre horizontally (still fully visible).
|
|
128
|
+
return { top: `${top}px`, left: `${clampLeft((vw - POP_W) / 2)}px`, transform: 'none', place: 'center' as const }
|
|
109
129
|
})
|
|
110
130
|
</script>
|
|
111
131
|
|
|
@@ -125,6 +145,7 @@
|
|
|
125
145
|
|
|
126
146
|
<div
|
|
127
147
|
bind:this={popEl}
|
|
148
|
+
bind:clientHeight={popH}
|
|
128
149
|
class="sv-tour__pop sv-tour__pop--{pop.place}"
|
|
129
150
|
style:top={pop.top}
|
|
130
151
|
style:left={pop.left}
|
|
@@ -133,6 +154,11 @@
|
|
|
133
154
|
aria-modal="true"
|
|
134
155
|
aria-label={step.title ?? 'Tour step'}
|
|
135
156
|
>
|
|
157
|
+
{#if step.image}
|
|
158
|
+
<div class="sv-tour__media">
|
|
159
|
+
{#if /^(https?:|data:|\/)/.test(step.image)}<img class="sv-tour__img" src={step.image} alt="" />{:else}{@html step.image}{/if}
|
|
160
|
+
</div>
|
|
161
|
+
{/if}
|
|
136
162
|
{#if step.title}<div class="sv-tour__title">{step.title}</div>{/if}
|
|
137
163
|
<div class="sv-tour__body">{step.content}</div>
|
|
138
164
|
<div class="sv-tour__foot">
|
|
@@ -163,6 +189,8 @@
|
|
|
163
189
|
}
|
|
164
190
|
@keyframes sv-tour-in { from { opacity: 0; transform: translateY(4px) } }
|
|
165
191
|
:global(.sv-tour__pop--top) { animation: none; }
|
|
192
|
+
:global(.sv-tour__media) { margin: -2px -4px 12px; border-radius: 9px; overflow: hidden; line-height: 0; }
|
|
193
|
+
:global(.sv-tour__media svg), :global(.sv-tour__img) { display: block; width: 100%; height: auto; }
|
|
166
194
|
:global(.sv-tour__title) { font-size: 14.5px; font-weight: 650; margin-bottom: 4px; }
|
|
167
195
|
:global(.sv-tour__body) { font-size: 13px; line-height: 1.55; color: var(--sg-fg, #334155); }
|
|
168
196
|
:global(.sv-tour__foot) { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 14px; }
|
package/dist/SvTreeSelect.svelte
CHANGED