@llui/components 0.0.30 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/accordion.d.ts +27 -9
- package/dist/components/accordion.d.ts.map +1 -1
- package/dist/components/accordion.js +4 -3
- package/dist/components/accordion.js.map +1 -1
- package/dist/components/angle-slider.d.ts +15 -5
- package/dist/components/angle-slider.d.ts.map +1 -1
- package/dist/components/angle-slider.js +3 -2
- package/dist/components/angle-slider.js.map +1 -1
- package/dist/components/async-list.d.ts +18 -6
- package/dist/components/async-list.d.ts.map +1 -1
- package/dist/components/async-list.js +3 -2
- package/dist/components/async-list.js.map +1 -1
- package/dist/components/avatar.d.ts +12 -4
- package/dist/components/avatar.d.ts.map +1 -1
- package/dist/components/avatar.js +4 -3
- package/dist/components/avatar.js.map +1 -1
- package/dist/components/carousel.d.ts +21 -7
- package/dist/components/carousel.d.ts.map +1 -1
- package/dist/components/carousel.js +8 -8
- package/dist/components/carousel.js.map +1 -1
- package/dist/components/cascade-select.d.ts +9 -3
- package/dist/components/cascade-select.d.ts.map +1 -1
- package/dist/components/cascade-select.js +4 -4
- package/dist/components/cascade-select.js.map +1 -1
- package/dist/components/checkbox.d.ts +9 -3
- package/dist/components/checkbox.d.ts.map +1 -1
- package/dist/components/checkbox.js +4 -3
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/clipboard.d.ts +12 -4
- package/dist/components/clipboard.d.ts.map +1 -1
- package/dist/components/clipboard.js +3 -3
- package/dist/components/clipboard.js.map +1 -1
- package/dist/components/collapsible.d.ts +12 -4
- package/dist/components/collapsible.d.ts.map +1 -1
- package/dist/components/collapsible.js +2 -1
- package/dist/components/collapsible.js.map +1 -1
- package/dist/components/color-picker.d.ts +18 -6
- package/dist/components/color-picker.d.ts.map +1 -1
- package/dist/components/color-picker.js +5 -5
- package/dist/components/color-picker.js.map +1 -1
- package/dist/components/combobox.d.ts +39 -13
- package/dist/components/combobox.d.ts.map +1 -1
- package/dist/components/combobox.js +17 -9
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/context-menu.d.ts +24 -8
- package/dist/components/context-menu.d.ts.map +1 -1
- package/dist/components/context-menu.js +8 -8
- package/dist/components/context-menu.js.map +1 -1
- package/dist/components/date-input.d.ts +21 -6
- package/dist/components/date-input.d.ts.map +1 -1
- package/dist/components/date-input.js +4 -4
- package/dist/components/date-input.js.map +1 -1
- package/dist/components/date-picker.d.ts +36 -12
- package/dist/components/date-picker.d.ts.map +1 -1
- package/dist/components/date-picker.js +15 -8
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/dialog.d.ts +12 -4
- package/dist/components/dialog.d.ts.map +1 -1
- package/dist/components/dialog.js +3 -3
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/drawer.d.ts +12 -4
- package/dist/components/drawer.d.ts.map +1 -1
- package/dist/components/drawer.js +3 -3
- package/dist/components/drawer.js.map +1 -1
- package/dist/components/editable.d.ts +15 -5
- package/dist/components/editable.d.ts.map +1 -1
- package/dist/components/editable.js +14 -13
- package/dist/components/editable.js.map +1 -1
- package/dist/components/file-upload.d.ts +30 -10
- package/dist/components/file-upload.d.ts.map +1 -1
- package/dist/components/file-upload.js +10 -10
- package/dist/components/file-upload.js.map +1 -1
- package/dist/components/floating-panel.d.ts +48 -16
- package/dist/components/floating-panel.d.ts.map +1 -1
- package/dist/components/floating-panel.js +6 -6
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/form.d.ts +18 -6
- package/dist/components/form.d.ts.map +1 -1
- package/dist/components/form.js +2 -1
- package/dist/components/form.js.map +1 -1
- package/dist/components/image-cropper.d.ts +33 -11
- package/dist/components/image-cropper.d.ts.map +1 -1
- package/dist/components/image-cropper.js +6 -6
- package/dist/components/image-cropper.js.map +1 -1
- package/dist/components/listbox.d.ts +33 -11
- package/dist/components/listbox.d.ts.map +1 -1
- package/dist/components/listbox.js +12 -4
- package/dist/components/listbox.js.map +1 -1
- package/dist/components/marquee.d.ts +21 -7
- package/dist/components/marquee.d.ts.map +1 -1
- package/dist/components/marquee.js +3 -2
- package/dist/components/marquee.js.map +1 -1
- package/dist/components/menu.d.ts +36 -12
- package/dist/components/menu.d.ts.map +1 -1
- package/dist/components/menu.js +23 -9
- package/dist/components/menu.js.map +1 -1
- package/dist/components/navigation-menu.d.ts +15 -5
- package/dist/components/navigation-menu.d.ts.map +1 -1
- package/dist/components/navigation-menu.js +8 -8
- package/dist/components/navigation-menu.js.map +1 -1
- package/dist/components/number-input.d.ts +24 -8
- package/dist/components/number-input.d.ts.map +1 -1
- package/dist/components/number-input.js +8 -8
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/pagination.d.ts +21 -7
- package/dist/components/pagination.d.ts.map +1 -1
- package/dist/components/pagination.js +4 -4
- package/dist/components/pagination.js.map +1 -1
- package/dist/components/password-input.d.ts +9 -3
- package/dist/components/password-input.d.ts.map +1 -1
- package/dist/components/password-input.js +3 -3
- package/dist/components/password-input.js.map +1 -1
- package/dist/components/pin-input.d.ts +15 -5
- package/dist/components/pin-input.d.ts.map +1 -1
- package/dist/components/pin-input.js +8 -7
- package/dist/components/pin-input.js.map +1 -1
- package/dist/components/popover.d.ts +12 -4
- package/dist/components/popover.d.ts.map +1 -1
- package/dist/components/popover.js +3 -3
- package/dist/components/popover.js.map +1 -1
- package/dist/components/presence.d.ts +15 -5
- package/dist/components/presence.d.ts.map +1 -1
- package/dist/components/presence.js.map +1 -1
- package/dist/components/progress.d.ts +6 -2
- package/dist/components/progress.d.ts.map +1 -1
- package/dist/components/progress.js.map +1 -1
- package/dist/components/qr-code.d.ts +9 -3
- package/dist/components/qr-code.d.ts.map +1 -1
- package/dist/components/qr-code.js.map +1 -1
- package/dist/components/radio-group.d.ts +18 -6
- package/dist/components/radio-group.d.ts.map +1 -1
- package/dist/components/radio-group.js +4 -3
- package/dist/components/radio-group.js.map +1 -1
- package/dist/components/rating-group.d.ts +21 -7
- package/dist/components/rating-group.d.ts.map +1 -1
- package/dist/components/rating-group.js +8 -7
- package/dist/components/rating-group.js.map +1 -1
- package/dist/components/scroll-area.d.ts +9 -3
- package/dist/components/scroll-area.d.ts.map +1 -1
- package/dist/components/scroll-area.js +5 -4
- package/dist/components/scroll-area.js.map +1 -1
- package/dist/components/select.d.ts +42 -14
- package/dist/components/select.d.ts.map +1 -1
- package/dist/components/select.js +4 -4
- package/dist/components/select.js.map +1 -1
- package/dist/components/signature-pad.d.ts +24 -8
- package/dist/components/signature-pad.d.ts.map +1 -1
- package/dist/components/signature-pad.js +3 -3
- package/dist/components/signature-pad.js.map +1 -1
- package/dist/components/slider.d.ts +21 -7
- package/dist/components/slider.d.ts.map +1 -1
- package/dist/components/slider.js.map +1 -1
- package/dist/components/sortable.d.ts +18 -6
- package/dist/components/sortable.d.ts.map +1 -1
- package/dist/components/sortable.js +11 -10
- package/dist/components/sortable.js.map +1 -1
- package/dist/components/splitter.d.ts +21 -7
- package/dist/components/splitter.d.ts.map +1 -1
- package/dist/components/splitter.js +5 -4
- package/dist/components/splitter.js.map +1 -1
- package/dist/components/steps.d.ts +21 -7
- package/dist/components/steps.d.ts.map +1 -1
- package/dist/components/steps.js +4 -4
- package/dist/components/steps.js.map +1 -1
- package/dist/components/switch.d.ts +9 -3
- package/dist/components/switch.d.ts.map +1 -1
- package/dist/components/switch.js +4 -3
- package/dist/components/switch.js.map +1 -1
- package/dist/components/tabs.d.ts +24 -8
- package/dist/components/tabs.d.ts.map +1 -1
- package/dist/components/tabs.js +7 -6
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/tags-input.d.ts +27 -9
- package/dist/components/tags-input.d.ts.map +1 -1
- package/dist/components/tags-input.js +10 -10
- package/dist/components/tags-input.js.map +1 -1
- package/dist/components/theme-switch.d.ts.map +1 -1
- package/dist/components/theme-switch.js +3 -2
- package/dist/components/theme-switch.js.map +1 -1
- package/dist/components/time-picker.d.ts +27 -9
- package/dist/components/time-picker.d.ts.map +1 -1
- package/dist/components/time-picker.js +10 -10
- package/dist/components/time-picker.js.map +1 -1
- package/dist/components/timer.d.ts +15 -5
- package/dist/components/timer.d.ts.map +1 -1
- package/dist/components/timer.js +4 -4
- package/dist/components/timer.js.map +1 -1
- package/dist/components/toast.d.ts +24 -8
- package/dist/components/toast.d.ts.map +1 -1
- package/dist/components/toast.js +6 -6
- package/dist/components/toast.js.map +1 -1
- package/dist/components/toc.d.ts +15 -5
- package/dist/components/toc.d.ts.map +1 -1
- package/dist/components/toc.js +2 -2
- package/dist/components/toc.js.map +1 -1
- package/dist/components/toggle-group.d.ts +15 -5
- package/dist/components/toggle-group.d.ts.map +1 -1
- package/dist/components/toggle-group.js +4 -3
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/toggle.d.ts +9 -3
- package/dist/components/toggle.d.ts.map +1 -1
- package/dist/components/toggle.js +4 -3
- package/dist/components/toggle.js.map +1 -1
- package/dist/components/tooltip.d.ts +12 -4
- package/dist/components/tooltip.d.ts.map +1 -1
- package/dist/components/tooltip.js +3 -3
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/tour.d.ts +18 -6
- package/dist/components/tour.d.ts.map +1 -1
- package/dist/components/tour.js +6 -6
- package/dist/components/tour.js.map +1 -1
- package/dist/components/tree-view.d.ts +75 -25
- package/dist/components/tree-view.d.ts.map +1 -1
- package/dist/components/tree-view.js +18 -7
- package/dist/components/tree-view.js.map +1 -1
- package/package.json +3 -3
package/dist/components/menu.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { show, portal, onMount, div } from '@llui/dom';
|
|
1
|
+
import { show, portal, onMount, div, tagSend } from '@llui/dom';
|
|
2
2
|
import { pushDismissable } from '../utils/dismissable.js';
|
|
3
3
|
import { attachFloating } from '../utils/floating.js';
|
|
4
4
|
import { typeaheadAccumulate, typeaheadMatchByItems, isTypeaheadKey, TYPEAHEAD_TIMEOUT_MS, } from '../utils/typeahead.js';
|
|
@@ -114,7 +114,21 @@ export function connect(get, send, opts) {
|
|
|
114
114
|
const triggerId = `${base}:trigger`;
|
|
115
115
|
const contentId = `${base}:content`;
|
|
116
116
|
const itemId = (v) => `${base}:item:${v}`;
|
|
117
|
-
|
|
117
|
+
// Keyboard navigation dispatches a fixed vocabulary of MenuMsg
|
|
118
|
+
// variants. `tagSend` propagates the user's translator tag (when
|
|
119
|
+
// `send` is a tagged dispatch translator) onto this handler so the
|
|
120
|
+
// agent's `list_actions` surfaces the user-side variants the
|
|
121
|
+
// translator forwards. Without a translator, the library variants
|
|
122
|
+
// listed here are what `update()` actually receives.
|
|
123
|
+
const handleMenuKey = tagSend(send, [
|
|
124
|
+
'highlightNext',
|
|
125
|
+
'highlightPrev',
|
|
126
|
+
'highlightFirst',
|
|
127
|
+
'highlightLast',
|
|
128
|
+
'selectHighlighted',
|
|
129
|
+
'close',
|
|
130
|
+
'typeahead',
|
|
131
|
+
], (e) => {
|
|
118
132
|
switch (e.key) {
|
|
119
133
|
case 'ArrowDown':
|
|
120
134
|
e.preventDefault();
|
|
@@ -146,7 +160,7 @@ export function connect(get, send, opts) {
|
|
|
146
160
|
send({ type: 'typeahead', char: e.key, now: Date.now() });
|
|
147
161
|
}
|
|
148
162
|
}
|
|
149
|
-
};
|
|
163
|
+
});
|
|
150
164
|
return {
|
|
151
165
|
trigger: {
|
|
152
166
|
type: 'button',
|
|
@@ -157,8 +171,8 @@ export function connect(get, send, opts) {
|
|
|
157
171
|
'data-state': (s) => (get(s).open ? 'open' : 'closed'),
|
|
158
172
|
'data-scope': 'menu',
|
|
159
173
|
'data-part': 'trigger',
|
|
160
|
-
onClick: () => send({ type: 'toggle' }),
|
|
161
|
-
onKeyDown: (e) => {
|
|
174
|
+
onClick: tagSend(send, ['toggle'], () => send({ type: 'toggle' })),
|
|
175
|
+
onKeyDown: tagSend(send, ['open', 'highlightLast'], (e) => {
|
|
162
176
|
if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {
|
|
163
177
|
e.preventDefault();
|
|
164
178
|
send({ type: 'open' });
|
|
@@ -168,7 +182,7 @@ export function connect(get, send, opts) {
|
|
|
168
182
|
send({ type: 'open' });
|
|
169
183
|
send({ type: 'highlightLast' });
|
|
170
184
|
}
|
|
171
|
-
},
|
|
185
|
+
}),
|
|
172
186
|
},
|
|
173
187
|
positioner: {
|
|
174
188
|
'data-scope': 'menu',
|
|
@@ -196,11 +210,11 @@ export function connect(get, send, opts) {
|
|
|
196
210
|
'data-part': 'item',
|
|
197
211
|
'data-value': value,
|
|
198
212
|
tabIndex: -1,
|
|
199
|
-
onClick: () => {
|
|
213
|
+
onClick: tagSend(send, ['select'], () => {
|
|
200
214
|
send({ type: 'select', value });
|
|
201
215
|
opts.onSelect?.(value);
|
|
202
|
-
},
|
|
203
|
-
onPointerMove: () => send({ type: 'highlight', value }),
|
|
216
|
+
}),
|
|
217
|
+
onPointerMove: tagSend(send, ['highlight'], () => send({ type: 'highlight', value })),
|
|
204
218
|
},
|
|
205
219
|
}),
|
|
206
220
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"menu.js","sourceRoot":"","sources":["../../src/components/menu.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send, TransitionOptions } from '@llui/dom'\nimport { show, portal, onMount, div } from '@llui/dom'\nimport { pushDismissable } from '../utils/dismissable.js'\nimport { attachFloating, type Placement } from '../utils/floating.js'\nimport {\n typeaheadAccumulate,\n typeaheadMatchByItems,\n isTypeaheadKey,\n TYPEAHEAD_TIMEOUT_MS,\n} from '../utils/typeahead.js'\n\n/**\n * Menu — a dropdown list of items triggered by a button. Keyboard navigation\n * (arrows, Home, End), typeahead (first-letter matching), Enter/Space to\n * activate the focused item, Escape to close.\n *\n * Items are opaque string values (keys); the user's view renders the\n * label/icon/etc. The machine tracks which item is currently \"highlighted\"\n * (= the one that will activate on Enter). On open, the first item is\n * highlighted by default unless `defaultHighlighted` is provided.\n */\n\nexport interface MenuState {\n open: boolean\n items: string[]\n disabledItems: string[]\n highlighted: string | null\n /** Accumulator for typeahead search. */\n typeahead: string\n typeaheadExpiresAt: number\n}\n\nexport type MenuMsg =\n | { type: 'open' }\n | { type: 'close' }\n | { type: 'toggle' }\n | { type: 'highlight'; value: string | null }\n | { type: 'highlightNext' }\n | { type: 'highlightPrev' }\n | { type: 'highlightFirst' }\n | { type: 'highlightLast' }\n | { type: 'selectHighlighted' }\n | { type: 'select'; value: string }\n | { type: 'setItems'; items: string[]; disabled?: string[] }\n | { type: 'typeahead'; char: string; now: number }\n\nexport interface MenuInit {\n open?: boolean\n items?: string[]\n disabledItems?: string[]\n highlighted?: string | null\n}\n\nexport function init(opts: MenuInit = {}): MenuState {\n return {\n open: opts.open ?? false,\n items: opts.items ?? [],\n disabledItems: opts.disabledItems ?? [],\n highlighted: opts.highlighted ?? null,\n typeahead: '',\n typeaheadExpiresAt: 0,\n }\n}\n\nfunction firstEnabled(items: string[], disabled: string[]): string | null {\n for (const v of items) if (!disabled.includes(v)) return v\n return null\n}\n\nfunction lastEnabled(items: string[], disabled: string[]): string | null {\n for (let i = items.length - 1; i >= 0; i--) {\n const v = items[i]!\n if (!disabled.includes(v)) return v\n }\n return null\n}\n\nfunction nextEnabled(\n items: string[],\n disabled: string[],\n from: string | null,\n delta: 1 | -1,\n): string | null {\n if (items.length === 0) return null\n const start = from === null ? -1 : items.indexOf(from)\n const n = items.length\n for (let i = 1; i <= n; i++) {\n const idx = start === -1 && delta === 1 ? i - 1 : (start + delta * i + n * n) % n\n const v = items[idx]!\n if (!disabled.includes(v)) return v\n }\n return null\n}\n\nexport function update(state: MenuState, msg: MenuMsg): [MenuState, never[]] {\n switch (msg.type) {\n case 'open': {\n const highlighted = state.highlighted ?? firstEnabled(state.items, state.disabledItems)\n return [{ ...state, open: true, highlighted }, []]\n }\n case 'close':\n return [{ ...state, open: false, highlighted: null, typeahead: '' }, []]\n case 'toggle':\n if (state.open) {\n return [{ ...state, open: false, highlighted: null, typeahead: '' }, []]\n }\n return [\n {\n ...state,\n open: true,\n highlighted: state.highlighted ?? firstEnabled(state.items, state.disabledItems),\n },\n [],\n ]\n case 'highlight':\n if (msg.value !== null && state.disabledItems.includes(msg.value)) return [state, []]\n return [{ ...state, highlighted: msg.value }, []]\n case 'highlightNext': {\n const to = nextEnabled(state.items, state.disabledItems, state.highlighted, 1)\n return [{ ...state, highlighted: to }, []]\n }\n case 'highlightPrev': {\n const to = nextEnabled(state.items, state.disabledItems, state.highlighted, -1)\n return [{ ...state, highlighted: to }, []]\n }\n case 'highlightFirst':\n return [{ ...state, highlighted: firstEnabled(state.items, state.disabledItems) }, []]\n case 'highlightLast':\n return [{ ...state, highlighted: lastEnabled(state.items, state.disabledItems) }, []]\n case 'selectHighlighted':\n if (state.highlighted === null) return [state, []]\n return [{ ...state, open: false, highlighted: null, typeahead: '' }, []]\n case 'select':\n if (state.disabledItems.includes(msg.value)) return [state, []]\n return [{ ...state, open: false, highlighted: null, typeahead: '' }, []]\n case 'setItems': {\n const disabled = msg.disabled ?? state.disabledItems\n const highlighted =\n state.highlighted &&\n msg.items.includes(state.highlighted) &&\n !disabled.includes(state.highlighted)\n ? state.highlighted\n : null\n return [{ ...state, items: msg.items, disabledItems: disabled, highlighted }, []]\n }\n case 'typeahead': {\n const acc = typeaheadAccumulate(state.typeahead, msg.char, msg.now, state.typeaheadExpiresAt)\n const startIdx = state.highlighted ? state.items.indexOf(state.highlighted) : null\n const matchIdx = typeaheadMatchByItems(state.items, state.disabledItems, acc, startIdx)\n const match = matchIdx === null ? null : state.items[matchIdx]!\n return [\n {\n ...state,\n typeahead: acc,\n typeaheadExpiresAt: msg.now + TYPEAHEAD_TIMEOUT_MS,\n highlighted: match ?? state.highlighted,\n },\n [],\n ]\n }\n }\n}\n\nexport interface MenuItemParts<S> {\n item: {\n role: 'menuitem'\n id: string\n 'aria-disabled': (s: S) => 'true' | undefined\n 'data-state': (s: S) => 'highlighted' | undefined\n 'data-disabled': (s: S) => '' | undefined\n 'data-scope': 'menu'\n 'data-part': 'item'\n 'data-value': string\n tabIndex: -1\n onClick: (e: MouseEvent) => void\n onPointerMove: (e: PointerEvent) => void\n }\n}\n\nexport interface MenuParts<S> {\n trigger: {\n type: 'button'\n 'aria-haspopup': 'menu'\n 'aria-expanded': (s: S) => boolean\n 'aria-controls': string\n id: string\n 'data-state': (s: S) => 'open' | 'closed'\n 'data-scope': 'menu'\n 'data-part': 'trigger'\n onClick: (e: MouseEvent) => void\n onKeyDown: (e: KeyboardEvent) => void\n }\n positioner: {\n 'data-scope': 'menu'\n 'data-part': 'positioner'\n style: string\n }\n content: {\n role: 'menu'\n id: string\n 'aria-labelledby': string\n tabIndex: -1\n 'data-state': (s: S) => 'open' | 'closed'\n 'data-scope': 'menu'\n 'data-part': 'content'\n onKeyDown: (e: KeyboardEvent) => void\n }\n item: (value: string) => MenuItemParts<S>\n}\n\nexport interface ConnectOptions {\n id: string\n /** Called when an item is activated (Enter/Space/click). */\n onSelect?: (value: string) => void\n}\n\nexport function connect<S>(\n get: (s: S) => MenuState,\n send: Send<MenuMsg>,\n opts: ConnectOptions,\n): MenuParts<S> {\n const base = opts.id\n const triggerId = `${base}:trigger`\n const contentId = `${base}:content`\n const itemId = (v: string): string => `${base}:item:${v}`\n\n const handleMenuKey = (e: KeyboardEvent): void => {\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault()\n send({ type: 'highlightNext' })\n return\n case 'ArrowUp':\n e.preventDefault()\n send({ type: 'highlightPrev' })\n return\n case 'Home':\n e.preventDefault()\n send({ type: 'highlightFirst' })\n return\n case 'End':\n e.preventDefault()\n send({ type: 'highlightLast' })\n return\n case 'Enter':\n case ' ':\n e.preventDefault()\n send({ type: 'selectHighlighted' })\n return\n case 'Escape':\n e.preventDefault()\n send({ type: 'close' })\n return\n default:\n if (isTypeaheadKey(e)) {\n send({ type: 'typeahead', char: e.key, now: Date.now() })\n }\n }\n }\n\n return {\n trigger: {\n type: 'button',\n 'aria-haspopup': 'menu',\n 'aria-expanded': (s) => get(s).open,\n 'aria-controls': contentId,\n id: triggerId,\n 'data-state': (s) => (get(s).open ? 'open' : 'closed'),\n 'data-scope': 'menu',\n 'data-part': 'trigger',\n onClick: () => send({ type: 'toggle' }),\n onKeyDown: (e: KeyboardEvent) => {\n if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n send({ type: 'open' })\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n send({ type: 'open' })\n send({ type: 'highlightLast' })\n }\n },\n },\n positioner: {\n 'data-scope': 'menu',\n 'data-part': 'positioner',\n style: 'position:absolute;top:0;left:0;',\n },\n content: {\n role: 'menu',\n id: contentId,\n 'aria-labelledby': triggerId,\n tabIndex: -1,\n 'data-state': (s) => (get(s).open ? 'open' : 'closed'),\n 'data-scope': 'menu',\n 'data-part': 'content',\n onKeyDown: handleMenuKey,\n },\n item: (value: string): MenuItemParts<S> => ({\n item: {\n role: 'menuitem',\n id: itemId(value),\n 'aria-disabled': (s) => (get(s).disabledItems.includes(value) ? 'true' : undefined),\n 'data-state': (s) => (get(s).highlighted === value ? 'highlighted' : undefined),\n 'data-disabled': (s) => (get(s).disabledItems.includes(value) ? '' : undefined),\n 'data-scope': 'menu',\n 'data-part': 'item',\n 'data-value': value,\n tabIndex: -1,\n onClick: () => {\n send({ type: 'select', value })\n opts.onSelect?.(value)\n },\n onPointerMove: () => send({ type: 'highlight', value }),\n },\n }),\n }\n}\n\nexport interface OverlayOptions<S> {\n get: (s: S) => MenuState\n send: Send<MenuMsg>\n parts: MenuParts<S>\n content: () => Node[]\n placement?: Placement\n offset?: number\n flip?: boolean\n shift?: boolean\n transition?: TransitionOptions\n target?: string | HTMLElement\n}\n\nexport function overlay<S>(opts: OverlayOptions<S>): Node[] {\n const target = opts.target ?? 'body'\n const placement = opts.placement ?? 'bottom-start'\n const offset = opts.offset ?? 4\n const flip = opts.flip !== false\n const shift = opts.shift !== false\n const parts = opts.parts\n const contentId = parts.content.id\n const triggerId = parts.trigger.id\n\n return show<S, MenuMsg>({\n when: (s) => opts.get(s).open,\n render: () =>\n portal({\n target,\n render: () => {\n onMount(() => {\n const contentEl = document.getElementById(contentId)\n const triggerEl = document.getElementById(triggerId)\n if (!contentEl || !triggerEl) return\n\n const cleanups: Array<() => void> = []\n\n const positioner = contentEl.closest('[data-part=\"positioner\"]') as HTMLElement | null\n const floatingEl = positioner ?? contentEl\n cleanups.push(\n attachFloating({\n anchor: triggerEl,\n floating: floatingEl,\n placement,\n offset,\n flip,\n shift,\n }),\n )\n\n cleanups.push(\n pushDismissable({\n element: contentEl,\n ignore: () => [triggerEl],\n onDismiss: () => {\n opts.send({ type: 'close' })\n triggerEl.focus()\n },\n }),\n )\n\n // Auto-focus content so keyboard navigation works immediately.\n // preventScroll avoids a page jump when the portaled content\n // is briefly at position (0,0) before floating-ui positions it.\n contentEl.focus({ preventScroll: true })\n\n return () => {\n for (let i = cleanups.length - 1; i >= 0; i--) cleanups[i]!()\n }\n })\n return [div(parts.positioner, opts.content())]\n },\n }),\n enter: opts.transition?.enter,\n leave: opts.transition?.leave,\n })\n}\n\nexport const menu = { init, update, connect, overlay }\n"]}
|
|
1
|
+
{"version":3,"file":"menu.js","sourceRoot":"","sources":["../../src/components/menu.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send, TransitionOptions } from '@llui/dom'\nimport { show, portal, onMount, div, tagSend } from '@llui/dom'\nimport { pushDismissable } from '../utils/dismissable.js'\nimport { attachFloating, type Placement } from '../utils/floating.js'\nimport {\n typeaheadAccumulate,\n typeaheadMatchByItems,\n isTypeaheadKey,\n TYPEAHEAD_TIMEOUT_MS,\n} from '../utils/typeahead.js'\n\n/**\n * Menu — a dropdown list of items triggered by a button. Keyboard navigation\n * (arrows, Home, End), typeahead (first-letter matching), Enter/Space to\n * activate the focused item, Escape to close.\n *\n * Items are opaque string values (keys); the user's view renders the\n * label/icon/etc. The machine tracks which item is currently \"highlighted\"\n * (= the one that will activate on Enter). On open, the first item is\n * highlighted by default unless `defaultHighlighted` is provided.\n */\n\nexport interface MenuState {\n open: boolean\n items: string[]\n disabledItems: string[]\n highlighted: string | null\n /** Accumulator for typeahead search. */\n typeahead: string\n typeaheadExpiresAt: number\n}\n\nexport type MenuMsg =\n /** @intent(\"Open the menu\") */\n | { type: 'open' }\n /** @intent(\"Close the menu\") */\n | { type: 'close' }\n /** @intent(\"Toggle the menu open/closed\") */\n | { type: 'toggle' }\n /** @humanOnly */\n | { type: 'highlight'; value: string | null }\n /** @humanOnly */\n | { type: 'highlightNext' }\n /** @humanOnly */\n | { type: 'highlightPrev' }\n /** @humanOnly */\n | { type: 'highlightFirst' }\n /** @humanOnly */\n | { type: 'highlightLast' }\n /** @intent(\"Activate the currently-highlighted menu item\") */\n | { type: 'selectHighlighted' }\n /** @intent(\"Activate the menu item with the given value\") */\n | { type: 'select'; value: string }\n /** @humanOnly */\n | { type: 'setItems'; items: string[]; disabled?: string[] }\n /** @humanOnly */\n | { type: 'typeahead'; char: string; now: number }\n\nexport interface MenuInit {\n open?: boolean\n items?: string[]\n disabledItems?: string[]\n highlighted?: string | null\n}\n\nexport function init(opts: MenuInit = {}): MenuState {\n return {\n open: opts.open ?? false,\n items: opts.items ?? [],\n disabledItems: opts.disabledItems ?? [],\n highlighted: opts.highlighted ?? null,\n typeahead: '',\n typeaheadExpiresAt: 0,\n }\n}\n\nfunction firstEnabled(items: string[], disabled: string[]): string | null {\n for (const v of items) if (!disabled.includes(v)) return v\n return null\n}\n\nfunction lastEnabled(items: string[], disabled: string[]): string | null {\n for (let i = items.length - 1; i >= 0; i--) {\n const v = items[i]!\n if (!disabled.includes(v)) return v\n }\n return null\n}\n\nfunction nextEnabled(\n items: string[],\n disabled: string[],\n from: string | null,\n delta: 1 | -1,\n): string | null {\n if (items.length === 0) return null\n const start = from === null ? -1 : items.indexOf(from)\n const n = items.length\n for (let i = 1; i <= n; i++) {\n const idx = start === -1 && delta === 1 ? i - 1 : (start + delta * i + n * n) % n\n const v = items[idx]!\n if (!disabled.includes(v)) return v\n }\n return null\n}\n\nexport function update(state: MenuState, msg: MenuMsg): [MenuState, never[]] {\n switch (msg.type) {\n case 'open': {\n const highlighted = state.highlighted ?? firstEnabled(state.items, state.disabledItems)\n return [{ ...state, open: true, highlighted }, []]\n }\n case 'close':\n return [{ ...state, open: false, highlighted: null, typeahead: '' }, []]\n case 'toggle':\n if (state.open) {\n return [{ ...state, open: false, highlighted: null, typeahead: '' }, []]\n }\n return [\n {\n ...state,\n open: true,\n highlighted: state.highlighted ?? firstEnabled(state.items, state.disabledItems),\n },\n [],\n ]\n case 'highlight':\n if (msg.value !== null && state.disabledItems.includes(msg.value)) return [state, []]\n return [{ ...state, highlighted: msg.value }, []]\n case 'highlightNext': {\n const to = nextEnabled(state.items, state.disabledItems, state.highlighted, 1)\n return [{ ...state, highlighted: to }, []]\n }\n case 'highlightPrev': {\n const to = nextEnabled(state.items, state.disabledItems, state.highlighted, -1)\n return [{ ...state, highlighted: to }, []]\n }\n case 'highlightFirst':\n return [{ ...state, highlighted: firstEnabled(state.items, state.disabledItems) }, []]\n case 'highlightLast':\n return [{ ...state, highlighted: lastEnabled(state.items, state.disabledItems) }, []]\n case 'selectHighlighted':\n if (state.highlighted === null) return [state, []]\n return [{ ...state, open: false, highlighted: null, typeahead: '' }, []]\n case 'select':\n if (state.disabledItems.includes(msg.value)) return [state, []]\n return [{ ...state, open: false, highlighted: null, typeahead: '' }, []]\n case 'setItems': {\n const disabled = msg.disabled ?? state.disabledItems\n const highlighted =\n state.highlighted &&\n msg.items.includes(state.highlighted) &&\n !disabled.includes(state.highlighted)\n ? state.highlighted\n : null\n return [{ ...state, items: msg.items, disabledItems: disabled, highlighted }, []]\n }\n case 'typeahead': {\n const acc = typeaheadAccumulate(state.typeahead, msg.char, msg.now, state.typeaheadExpiresAt)\n const startIdx = state.highlighted ? state.items.indexOf(state.highlighted) : null\n const matchIdx = typeaheadMatchByItems(state.items, state.disabledItems, acc, startIdx)\n const match = matchIdx === null ? null : state.items[matchIdx]!\n return [\n {\n ...state,\n typeahead: acc,\n typeaheadExpiresAt: msg.now + TYPEAHEAD_TIMEOUT_MS,\n highlighted: match ?? state.highlighted,\n },\n [],\n ]\n }\n }\n}\n\nexport interface MenuItemParts<S> {\n item: {\n role: 'menuitem'\n id: string\n 'aria-disabled': (s: S) => 'true' | undefined\n 'data-state': (s: S) => 'highlighted' | undefined\n 'data-disabled': (s: S) => '' | undefined\n 'data-scope': 'menu'\n 'data-part': 'item'\n 'data-value': string\n tabIndex: -1\n onClick: (e: MouseEvent) => void\n onPointerMove: (e: PointerEvent) => void\n }\n}\n\nexport interface MenuParts<S> {\n trigger: {\n type: 'button'\n 'aria-haspopup': 'menu'\n 'aria-expanded': (s: S) => boolean\n 'aria-controls': string\n id: string\n 'data-state': (s: S) => 'open' | 'closed'\n 'data-scope': 'menu'\n 'data-part': 'trigger'\n onClick: (e: MouseEvent) => void\n onKeyDown: (e: KeyboardEvent) => void\n }\n positioner: {\n 'data-scope': 'menu'\n 'data-part': 'positioner'\n style: string\n }\n content: {\n role: 'menu'\n id: string\n 'aria-labelledby': string\n tabIndex: -1\n 'data-state': (s: S) => 'open' | 'closed'\n 'data-scope': 'menu'\n 'data-part': 'content'\n onKeyDown: (e: KeyboardEvent) => void\n }\n item: (value: string) => MenuItemParts<S>\n}\n\nexport interface ConnectOptions {\n id: string\n /** Called when an item is activated (Enter/Space/click). */\n onSelect?: (value: string) => void\n}\n\nexport function connect<S>(\n get: (s: S) => MenuState,\n send: Send<MenuMsg>,\n opts: ConnectOptions,\n): MenuParts<S> {\n const base = opts.id\n const triggerId = `${base}:trigger`\n const contentId = `${base}:content`\n const itemId = (v: string): string => `${base}:item:${v}`\n\n // Keyboard navigation dispatches a fixed vocabulary of MenuMsg\n // variants. `tagSend` propagates the user's translator tag (when\n // `send` is a tagged dispatch translator) onto this handler so the\n // agent's `list_actions` surfaces the user-side variants the\n // translator forwards. Without a translator, the library variants\n // listed here are what `update()` actually receives.\n const handleMenuKey = tagSend(\n send,\n [\n 'highlightNext',\n 'highlightPrev',\n 'highlightFirst',\n 'highlightLast',\n 'selectHighlighted',\n 'close',\n 'typeahead',\n ],\n (e: KeyboardEvent): void => {\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault()\n send({ type: 'highlightNext' })\n return\n case 'ArrowUp':\n e.preventDefault()\n send({ type: 'highlightPrev' })\n return\n case 'Home':\n e.preventDefault()\n send({ type: 'highlightFirst' })\n return\n case 'End':\n e.preventDefault()\n send({ type: 'highlightLast' })\n return\n case 'Enter':\n case ' ':\n e.preventDefault()\n send({ type: 'selectHighlighted' })\n return\n case 'Escape':\n e.preventDefault()\n send({ type: 'close' })\n return\n default:\n if (isTypeaheadKey(e)) {\n send({ type: 'typeahead', char: e.key, now: Date.now() })\n }\n }\n },\n )\n\n return {\n trigger: {\n type: 'button',\n 'aria-haspopup': 'menu',\n 'aria-expanded': (s) => get(s).open,\n 'aria-controls': contentId,\n id: triggerId,\n 'data-state': (s) => (get(s).open ? 'open' : 'closed'),\n 'data-scope': 'menu',\n 'data-part': 'trigger',\n onClick: tagSend(send, ['toggle'], () => send({ type: 'toggle' })),\n onKeyDown: tagSend(send, ['open', 'highlightLast'], (e: KeyboardEvent) => {\n if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n send({ type: 'open' })\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n send({ type: 'open' })\n send({ type: 'highlightLast' })\n }\n }),\n },\n positioner: {\n 'data-scope': 'menu',\n 'data-part': 'positioner',\n style: 'position:absolute;top:0;left:0;',\n },\n content: {\n role: 'menu',\n id: contentId,\n 'aria-labelledby': triggerId,\n tabIndex: -1,\n 'data-state': (s) => (get(s).open ? 'open' : 'closed'),\n 'data-scope': 'menu',\n 'data-part': 'content',\n onKeyDown: handleMenuKey,\n },\n item: (value: string): MenuItemParts<S> => ({\n item: {\n role: 'menuitem',\n id: itemId(value),\n 'aria-disabled': (s) => (get(s).disabledItems.includes(value) ? 'true' : undefined),\n 'data-state': (s) => (get(s).highlighted === value ? 'highlighted' : undefined),\n 'data-disabled': (s) => (get(s).disabledItems.includes(value) ? '' : undefined),\n 'data-scope': 'menu',\n 'data-part': 'item',\n 'data-value': value,\n tabIndex: -1,\n onClick: tagSend(send, ['select'], () => {\n send({ type: 'select', value })\n opts.onSelect?.(value)\n }),\n onPointerMove: tagSend(send, ['highlight'], () => send({ type: 'highlight', value })),\n },\n }),\n }\n}\n\nexport interface OverlayOptions<S> {\n get: (s: S) => MenuState\n send: Send<MenuMsg>\n parts: MenuParts<S>\n content: () => Node[]\n placement?: Placement\n offset?: number\n flip?: boolean\n shift?: boolean\n transition?: TransitionOptions\n target?: string | HTMLElement\n}\n\nexport function overlay<S>(opts: OverlayOptions<S>): Node[] {\n const target = opts.target ?? 'body'\n const placement = opts.placement ?? 'bottom-start'\n const offset = opts.offset ?? 4\n const flip = opts.flip !== false\n const shift = opts.shift !== false\n const parts = opts.parts\n const contentId = parts.content.id\n const triggerId = parts.trigger.id\n\n return show<S, MenuMsg>({\n when: (s) => opts.get(s).open,\n render: () =>\n portal({\n target,\n render: () => {\n onMount(() => {\n const contentEl = document.getElementById(contentId)\n const triggerEl = document.getElementById(triggerId)\n if (!contentEl || !triggerEl) return\n\n const cleanups: Array<() => void> = []\n\n const positioner = contentEl.closest('[data-part=\"positioner\"]') as HTMLElement | null\n const floatingEl = positioner ?? contentEl\n cleanups.push(\n attachFloating({\n anchor: triggerEl,\n floating: floatingEl,\n placement,\n offset,\n flip,\n shift,\n }),\n )\n\n cleanups.push(\n pushDismissable({\n element: contentEl,\n ignore: () => [triggerEl],\n onDismiss: () => {\n opts.send({ type: 'close' })\n triggerEl.focus()\n },\n }),\n )\n\n // Auto-focus content so keyboard navigation works immediately.\n // preventScroll avoids a page jump when the portaled content\n // is briefly at position (0,0) before floating-ui positions it.\n contentEl.focus({ preventScroll: true })\n\n return () => {\n for (let i = cleanups.length - 1; i >= 0; i--) cleanups[i]!()\n }\n })\n return [div(parts.positioner, opts.content())]\n },\n }),\n enter: opts.transition?.enter,\n leave: opts.transition?.leave,\n })\n}\n\nexport const menu = { init, update, connect, overlay }\n"]}
|
|
@@ -26,20 +26,30 @@ export interface NavMenuState {
|
|
|
26
26
|
focused: string | null;
|
|
27
27
|
disabled: boolean;
|
|
28
28
|
}
|
|
29
|
-
export type NavMenuMsg =
|
|
29
|
+
export type NavMenuMsg =
|
|
30
|
+
/** @intent("Open the submenu identified by id, closing any open siblings") */
|
|
31
|
+
{
|
|
30
32
|
type: 'openBranch';
|
|
31
33
|
id: string;
|
|
32
34
|
ancestorIds: string[];
|
|
33
|
-
}
|
|
35
|
+
}
|
|
36
|
+
/** @intent("Close the submenu identified by id (also closes its descendants)") */
|
|
37
|
+
| {
|
|
34
38
|
type: 'closeBranch';
|
|
35
39
|
id: string;
|
|
36
|
-
}
|
|
40
|
+
}
|
|
41
|
+
/** @intent("Toggle the submenu identified by id open/closed") */
|
|
42
|
+
| {
|
|
37
43
|
type: 'toggleBranch';
|
|
38
44
|
id: string;
|
|
39
45
|
ancestorIds: string[];
|
|
40
|
-
}
|
|
46
|
+
}
|
|
47
|
+
/** @intent("Close every open submenu") */
|
|
48
|
+
| {
|
|
41
49
|
type: 'closeAll';
|
|
42
|
-
}
|
|
50
|
+
}
|
|
51
|
+
/** @humanOnly */
|
|
52
|
+
| {
|
|
43
53
|
type: 'focus';
|
|
44
54
|
id: string | null;
|
|
45
55
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation-menu.d.ts","sourceRoot":"","sources":["../../src/components/navigation-menu.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAKrC;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,YAAY;IAC3B;yDACqD;IACrD,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"navigation-menu.d.ts","sourceRoot":"","sources":["../../src/components/navigation-menu.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAKrC;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,YAAY;IAC3B;yDACqD;IACrD,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,MAAM,UAAU;AACpB,8EAA8E;AAC5E;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE;AAC3D,kFAAkF;GAChF;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE;AACrC,iEAAiE;GAC/D;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE;AAC7D,0CAA0C;GACxC;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE;AACtB,iBAAiB;GACf;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAA;AAExC,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,wBAAgB,IAAI,CAAC,IAAI,GAAE,WAAgB,GAAG,YAAY,CAMzD;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,UAAU,GAAG,CAAC,YAAY,EAAE,KAAK,EAAE,CAAC,CAgCpF;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,OAAO,EAAE;QACP,IAAI,EAAE,QAAQ,CAAA;QACd,IAAI,EAAE,UAAU,CAAA;QAChB,EAAE,EAAE,MAAM,CAAA;QACV,eAAe,EAAE,MAAM,GAAG,SAAS,CAAA;QACnC,eAAe,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,GAAG,SAAS,CAAA;QAC9C,YAAY,EAAE,iBAAiB,CAAA;QAC/B,WAAW,EAAE,SAAS,CAAA;QACtB,YAAY,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,QAAQ,CAAA;QACzC,YAAY,EAAE,MAAM,CAAA;QACpB,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;QAC1B,OAAO,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAA;QAChC,cAAc,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,CAAA;QACzC,OAAO,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAA;KACjC,CAAA;IACD,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAA;QACZ,EAAE,EAAE,MAAM,CAAA;QACV,iBAAiB,EAAE,MAAM,CAAA;QACzB,YAAY,EAAE,iBAAiB,CAAA;QAC/B,WAAW,EAAE,SAAS,CAAA;QACtB,YAAY,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,QAAQ,CAAA;QACzC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAA;QACzB,cAAc,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,CAAA;KAC1C,CAAA;CACF;AAED,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS,CAAA;QACf,YAAY,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,CAAA;QACzC,YAAY,EAAE,iBAAiB,CAAA;QAC/B,WAAW,EAAE,MAAM,CAAA;QACnB,eAAe,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,SAAS,CAAA;QACzC,cAAc,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,CAAA;QACzC,cAAc,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,CAAA;KAC1C,CAAA;IACD,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,KAAK,YAAY,CAAC,CAAC,CAAC,CAAA;CAC9F;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,wBAAgB,OAAO,CAAC,CAAC,EACvB,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,YAAY,EAC3B,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,EACtB,IAAI,EAAE,cAAc,GACnB,YAAY,CAAC,CAAC,CAAC,CA8EjB;AAED,eAAO,MAAM,cAAc;;;;;CAAoC,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useContext } from '@llui/dom';
|
|
1
|
+
import { useContext, tagSend } from '@llui/dom';
|
|
2
2
|
import { LocaleContext } from '../locale.js';
|
|
3
3
|
export function init(opts = {}) {
|
|
4
4
|
return {
|
|
@@ -92,18 +92,18 @@ export function connect(get, send, opts) {
|
|
|
92
92
|
'data-state': (s) => (isOpen(get(s), id) ? 'open' : 'closed'),
|
|
93
93
|
'data-value': id,
|
|
94
94
|
tabIndex: (s) => (get(s).focused === id ? 0 : -1),
|
|
95
|
-
onClick: () => {
|
|
95
|
+
onClick: tagSend(send, ['toggleBranch'], () => {
|
|
96
96
|
if (options.isBranch) {
|
|
97
97
|
send({ type: 'toggleBranch', id, ancestorIds });
|
|
98
98
|
}
|
|
99
|
-
},
|
|
100
|
-
onPointerEnter: () => {
|
|
99
|
+
}),
|
|
100
|
+
onPointerEnter: tagSend(send, ['openBranch'], () => {
|
|
101
101
|
cancelClose();
|
|
102
102
|
if (options.isBranch) {
|
|
103
103
|
send({ type: 'openBranch', id, ancestorIds });
|
|
104
104
|
}
|
|
105
|
-
},
|
|
106
|
-
onFocus: () => send({ type: 'focus', id }),
|
|
105
|
+
}),
|
|
106
|
+
onFocus: tagSend(send, ['focus'], () => send({ type: 'focus', id })),
|
|
107
107
|
},
|
|
108
108
|
content: {
|
|
109
109
|
role: 'menu',
|
|
@@ -113,12 +113,12 @@ export function connect(get, send, opts) {
|
|
|
113
113
|
'data-part': 'content',
|
|
114
114
|
'data-state': (s) => (isOpen(get(s), id) ? 'open' : 'closed'),
|
|
115
115
|
hidden: (s) => !isOpen(get(s), id),
|
|
116
|
-
onPointerEnter: () => {
|
|
116
|
+
onPointerEnter: tagSend(send, ['openBranch'], () => {
|
|
117
117
|
cancelClose();
|
|
118
118
|
if (options.isBranch) {
|
|
119
119
|
send({ type: 'openBranch', id, ancestorIds });
|
|
120
120
|
}
|
|
121
|
-
},
|
|
121
|
+
}),
|
|
122
122
|
},
|
|
123
123
|
};
|
|
124
124
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation-menu.js","sourceRoot":"","sources":["../../src/components/navigation-menu.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send } from '@llui/dom'\nimport { useContext } from '@llui/dom'\nimport { LocaleContext } from '../locale.js'\nimport type { Locale } from '../locale.js'\n\n/**\n * Navigation menu — multi-level menu bar with hover/focus-triggered\n * submenus. Unlike `menu` (a single dropdown), navigation-menu supports\n * nested submenus arbitrarily deep and is typically used for primary\n * site navigation.\n *\n * State tracks the currently focused item id and the ids of all\n * currently-open branches. The consumer provides the tree structure\n * (items with optional children); the machine doesn't index the\n * hierarchy itself — it just maintains open-paths and lets the view\n * handle traversal.\n *\n * Typical interaction model (delay-based):\n * - Pointer enter on a branch → openBranch after openDelay\n * - Pointer leave of the whole tree → closeAll after closeDelay\n * - Click/keyboard activation → toggleBranch immediately\n *\n * The consumer is responsible for debouncing via setTimeout; the machine\n * just responds to the dispatched messages.\n */\n\nexport interface NavMenuState {\n /** Ids of open branches, in open order (root-first). Closing an\n * ancestor automatically closes its descendants. */\n open: string[]\n focused: string | null\n disabled: boolean\n}\n\nexport type NavMenuMsg =\n | { type: 'openBranch'; id: string; ancestorIds: string[] }\n | { type: 'closeBranch'; id: string }\n | { type: 'toggleBranch'; id: string; ancestorIds: string[] }\n | { type: 'closeAll' }\n | { type: 'focus'; id: string | null }\n\nexport interface NavMenuInit {\n open?: string[]\n focused?: string | null\n disabled?: boolean\n}\n\nexport function init(opts: NavMenuInit = {}): NavMenuState {\n return {\n open: opts.open ?? [],\n focused: opts.focused ?? null,\n disabled: opts.disabled ?? false,\n }\n}\n\nexport function update(state: NavMenuState, msg: NavMenuMsg): [NavMenuState, never[]] {\n if (state.disabled) return [state, []]\n switch (msg.type) {\n case 'openBranch': {\n // Close any siblings of `id` at the same ancestor path, then add it.\n // Sibling detection: an entry is a sibling if its ancestor set matches\n // msg.ancestorIds and it isn't msg.id. We don't track ancestors in\n // state, so: filter `open` to keep only entries that are themselves\n // an ancestor of msg.id, plus msg.id.\n const keep = new Set([...msg.ancestorIds, msg.id])\n const open = state.open.filter((o) => keep.has(o))\n if (!open.includes(msg.id)) open.push(msg.id)\n return [{ ...state, open }, []]\n }\n case 'closeBranch': {\n // Close this branch and any descendants that follow it in the open\n // list. Since open is ordered root-first, descendants come after.\n const idx = state.open.indexOf(msg.id)\n if (idx === -1) return [state, []]\n const open = state.open.slice(0, idx)\n return [{ ...state, open }, []]\n }\n case 'toggleBranch':\n if (state.open.includes(msg.id)) {\n return update(state, { type: 'closeBranch', id: msg.id })\n }\n return update(state, { type: 'openBranch', id: msg.id, ancestorIds: msg.ancestorIds })\n case 'closeAll':\n return [{ ...state, open: [] }, []]\n case 'focus':\n return [{ ...state, focused: msg.id }, []]\n }\n}\n\nexport function isOpen(state: NavMenuState, id: string): boolean {\n return state.open.includes(id)\n}\n\nexport interface NavItemParts<S> {\n trigger: {\n type: 'button'\n role: 'menuitem'\n id: string\n 'aria-haspopup': 'menu' | undefined\n 'aria-expanded': (s: S) => boolean | undefined\n 'data-scope': 'navigation-menu'\n 'data-part': 'trigger'\n 'data-state': (s: S) => 'open' | 'closed'\n 'data-value': string\n tabIndex: (s: S) => number\n onClick: (e: MouseEvent) => void\n onPointerEnter: (e: PointerEvent) => void\n onFocus: (e: FocusEvent) => void\n }\n content: {\n role: 'menu'\n id: string\n 'aria-labelledby': string\n 'data-scope': 'navigation-menu'\n 'data-part': 'content'\n 'data-state': (s: S) => 'open' | 'closed'\n hidden: (s: S) => boolean\n onPointerEnter: (e: PointerEvent) => void\n }\n}\n\nexport interface NavMenuParts<S> {\n root: {\n role: 'menubar'\n 'aria-label': string | ((s: S) => string)\n 'data-scope': 'navigation-menu'\n 'data-part': 'root'\n 'data-disabled': (s: S) => '' | undefined\n onPointerLeave: (e: PointerEvent) => void\n onPointerEnter: (e: PointerEvent) => void\n }\n item: (id: string, options: { isBranch: boolean; ancestorIds?: string[] }) => NavItemParts<S>\n}\n\nexport interface ConnectOptions {\n id: string\n label?: string\n /**\n * Whether pointer-leaving the whole menu closes everything. Default: true.\n * The consumer can inject their own close delay by intercepting\n * onPointerLeave + calling setTimeout + dispatching closeAll.\n */\n closeOnLeave?: boolean\n}\n\nexport function connect<S>(\n get: (s: S) => NavMenuState,\n send: Send<NavMenuMsg>,\n opts: ConnectOptions,\n): NavMenuParts<S> {\n const locale = useContext<S, Locale>(LocaleContext)\n const triggerId = (v: string): string => `${opts.id}:trigger:${v}`\n const contentId = (v: string): string => `${opts.id}:content:${v}`\n const closeOnLeave = opts.closeOnLeave !== false\n let closeTimer: ReturnType<typeof setTimeout> | null = null\n\n const scheduleClose = (): void => {\n if (!closeOnLeave) return\n if (closeTimer) clearTimeout(closeTimer)\n closeTimer = setTimeout(() => {\n send({ type: 'closeAll' })\n closeTimer = null\n }, 150)\n }\n\n const cancelClose = (): void => {\n if (closeTimer) {\n clearTimeout(closeTimer)\n closeTimer = null\n }\n }\n\n return {\n root: {\n role: 'menubar',\n 'aria-label': opts.label ?? ((s: S) => locale(s).navigationMenu.label),\n 'data-scope': 'navigation-menu',\n 'data-part': 'root',\n 'data-disabled': (s) => (get(s).disabled ? '' : undefined),\n onPointerLeave: () => scheduleClose(),\n onPointerEnter: () => cancelClose(),\n },\n item: (id: string, options: { isBranch: boolean; ancestorIds?: string[] }): NavItemParts<S> => {\n const ancestorIds = options.ancestorIds ?? []\n return {\n trigger: {\n type: 'button',\n role: 'menuitem',\n id: triggerId(id),\n 'aria-haspopup': options.isBranch ? 'menu' : undefined,\n 'aria-expanded': (s) => (options.isBranch ? isOpen(get(s), id) : undefined),\n 'data-scope': 'navigation-menu',\n 'data-part': 'trigger',\n 'data-state': (s) => (isOpen(get(s), id) ? 'open' : 'closed'),\n 'data-value': id,\n tabIndex: (s) => (get(s).focused === id ? 0 : -1),\n onClick: () => {\n if (options.isBranch) {\n send({ type: 'toggleBranch', id, ancestorIds })\n }\n },\n onPointerEnter: () => {\n cancelClose()\n if (options.isBranch) {\n send({ type: 'openBranch', id, ancestorIds })\n }\n },\n onFocus: () => send({ type: 'focus', id }),\n },\n content: {\n role: 'menu',\n id: contentId(id),\n 'aria-labelledby': triggerId(id),\n 'data-scope': 'navigation-menu',\n 'data-part': 'content',\n 'data-state': (s) => (isOpen(get(s), id) ? 'open' : 'closed'),\n hidden: (s) => !isOpen(get(s), id),\n onPointerEnter: () => {\n cancelClose()\n if (options.isBranch) {\n send({ type: 'openBranch', id, ancestorIds })\n }\n },\n },\n }\n },\n }\n}\n\nexport const navigationMenu = { init, update, connect, isOpen }\n"]}
|
|
1
|
+
{"version":3,"file":"navigation-menu.js","sourceRoot":"","sources":["../../src/components/navigation-menu.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send } from '@llui/dom'\nimport { useContext, tagSend } from '@llui/dom'\nimport { LocaleContext } from '../locale.js'\nimport type { Locale } from '../locale.js'\n\n/**\n * Navigation menu — multi-level menu bar with hover/focus-triggered\n * submenus. Unlike `menu` (a single dropdown), navigation-menu supports\n * nested submenus arbitrarily deep and is typically used for primary\n * site navigation.\n *\n * State tracks the currently focused item id and the ids of all\n * currently-open branches. The consumer provides the tree structure\n * (items with optional children); the machine doesn't index the\n * hierarchy itself — it just maintains open-paths and lets the view\n * handle traversal.\n *\n * Typical interaction model (delay-based):\n * - Pointer enter on a branch → openBranch after openDelay\n * - Pointer leave of the whole tree → closeAll after closeDelay\n * - Click/keyboard activation → toggleBranch immediately\n *\n * The consumer is responsible for debouncing via setTimeout; the machine\n * just responds to the dispatched messages.\n */\n\nexport interface NavMenuState {\n /** Ids of open branches, in open order (root-first). Closing an\n * ancestor automatically closes its descendants. */\n open: string[]\n focused: string | null\n disabled: boolean\n}\n\nexport type NavMenuMsg =\n /** @intent(\"Open the submenu identified by id, closing any open siblings\") */\n | { type: 'openBranch'; id: string; ancestorIds: string[] }\n /** @intent(\"Close the submenu identified by id (also closes its descendants)\") */\n | { type: 'closeBranch'; id: string }\n /** @intent(\"Toggle the submenu identified by id open/closed\") */\n | { type: 'toggleBranch'; id: string; ancestorIds: string[] }\n /** @intent(\"Close every open submenu\") */\n | { type: 'closeAll' }\n /** @humanOnly */\n | { type: 'focus'; id: string | null }\n\nexport interface NavMenuInit {\n open?: string[]\n focused?: string | null\n disabled?: boolean\n}\n\nexport function init(opts: NavMenuInit = {}): NavMenuState {\n return {\n open: opts.open ?? [],\n focused: opts.focused ?? null,\n disabled: opts.disabled ?? false,\n }\n}\n\nexport function update(state: NavMenuState, msg: NavMenuMsg): [NavMenuState, never[]] {\n if (state.disabled) return [state, []]\n switch (msg.type) {\n case 'openBranch': {\n // Close any siblings of `id` at the same ancestor path, then add it.\n // Sibling detection: an entry is a sibling if its ancestor set matches\n // msg.ancestorIds and it isn't msg.id. We don't track ancestors in\n // state, so: filter `open` to keep only entries that are themselves\n // an ancestor of msg.id, plus msg.id.\n const keep = new Set([...msg.ancestorIds, msg.id])\n const open = state.open.filter((o) => keep.has(o))\n if (!open.includes(msg.id)) open.push(msg.id)\n return [{ ...state, open }, []]\n }\n case 'closeBranch': {\n // Close this branch and any descendants that follow it in the open\n // list. Since open is ordered root-first, descendants come after.\n const idx = state.open.indexOf(msg.id)\n if (idx === -1) return [state, []]\n const open = state.open.slice(0, idx)\n return [{ ...state, open }, []]\n }\n case 'toggleBranch':\n if (state.open.includes(msg.id)) {\n return update(state, { type: 'closeBranch', id: msg.id })\n }\n return update(state, { type: 'openBranch', id: msg.id, ancestorIds: msg.ancestorIds })\n case 'closeAll':\n return [{ ...state, open: [] }, []]\n case 'focus':\n return [{ ...state, focused: msg.id }, []]\n }\n}\n\nexport function isOpen(state: NavMenuState, id: string): boolean {\n return state.open.includes(id)\n}\n\nexport interface NavItemParts<S> {\n trigger: {\n type: 'button'\n role: 'menuitem'\n id: string\n 'aria-haspopup': 'menu' | undefined\n 'aria-expanded': (s: S) => boolean | undefined\n 'data-scope': 'navigation-menu'\n 'data-part': 'trigger'\n 'data-state': (s: S) => 'open' | 'closed'\n 'data-value': string\n tabIndex: (s: S) => number\n onClick: (e: MouseEvent) => void\n onPointerEnter: (e: PointerEvent) => void\n onFocus: (e: FocusEvent) => void\n }\n content: {\n role: 'menu'\n id: string\n 'aria-labelledby': string\n 'data-scope': 'navigation-menu'\n 'data-part': 'content'\n 'data-state': (s: S) => 'open' | 'closed'\n hidden: (s: S) => boolean\n onPointerEnter: (e: PointerEvent) => void\n }\n}\n\nexport interface NavMenuParts<S> {\n root: {\n role: 'menubar'\n 'aria-label': string | ((s: S) => string)\n 'data-scope': 'navigation-menu'\n 'data-part': 'root'\n 'data-disabled': (s: S) => '' | undefined\n onPointerLeave: (e: PointerEvent) => void\n onPointerEnter: (e: PointerEvent) => void\n }\n item: (id: string, options: { isBranch: boolean; ancestorIds?: string[] }) => NavItemParts<S>\n}\n\nexport interface ConnectOptions {\n id: string\n label?: string\n /**\n * Whether pointer-leaving the whole menu closes everything. Default: true.\n * The consumer can inject their own close delay by intercepting\n * onPointerLeave + calling setTimeout + dispatching closeAll.\n */\n closeOnLeave?: boolean\n}\n\nexport function connect<S>(\n get: (s: S) => NavMenuState,\n send: Send<NavMenuMsg>,\n opts: ConnectOptions,\n): NavMenuParts<S> {\n const locale = useContext<S, Locale>(LocaleContext)\n const triggerId = (v: string): string => `${opts.id}:trigger:${v}`\n const contentId = (v: string): string => `${opts.id}:content:${v}`\n const closeOnLeave = opts.closeOnLeave !== false\n let closeTimer: ReturnType<typeof setTimeout> | null = null\n\n const scheduleClose = (): void => {\n if (!closeOnLeave) return\n if (closeTimer) clearTimeout(closeTimer)\n closeTimer = setTimeout(() => {\n send({ type: 'closeAll' })\n closeTimer = null\n }, 150)\n }\n\n const cancelClose = (): void => {\n if (closeTimer) {\n clearTimeout(closeTimer)\n closeTimer = null\n }\n }\n\n return {\n root: {\n role: 'menubar',\n 'aria-label': opts.label ?? ((s: S) => locale(s).navigationMenu.label),\n 'data-scope': 'navigation-menu',\n 'data-part': 'root',\n 'data-disabled': (s) => (get(s).disabled ? '' : undefined),\n onPointerLeave: () => scheduleClose(),\n onPointerEnter: () => cancelClose(),\n },\n item: (id: string, options: { isBranch: boolean; ancestorIds?: string[] }): NavItemParts<S> => {\n const ancestorIds = options.ancestorIds ?? []\n return {\n trigger: {\n type: 'button',\n role: 'menuitem',\n id: triggerId(id),\n 'aria-haspopup': options.isBranch ? 'menu' : undefined,\n 'aria-expanded': (s) => (options.isBranch ? isOpen(get(s), id) : undefined),\n 'data-scope': 'navigation-menu',\n 'data-part': 'trigger',\n 'data-state': (s) => (isOpen(get(s), id) ? 'open' : 'closed'),\n 'data-value': id,\n tabIndex: (s) => (get(s).focused === id ? 0 : -1),\n onClick: tagSend(send, ['toggleBranch'], () => {\n if (options.isBranch) {\n send({ type: 'toggleBranch', id, ancestorIds })\n }\n }),\n onPointerEnter: tagSend(send, ['openBranch'], () => {\n cancelClose()\n if (options.isBranch) {\n send({ type: 'openBranch', id, ancestorIds })\n }\n }),\n onFocus: tagSend(send, ['focus'], () => send({ type: 'focus', id })),\n },\n content: {\n role: 'menu',\n id: contentId(id),\n 'aria-labelledby': triggerId(id),\n 'data-scope': 'navigation-menu',\n 'data-part': 'content',\n 'data-state': (s) => (isOpen(get(s), id) ? 'open' : 'closed'),\n hidden: (s) => !isOpen(get(s), id),\n onPointerEnter: tagSend(send, ['openBranch'], () => {\n cancelClose()\n if (options.isBranch) {\n send({ type: 'openBranch', id, ancestorIds })\n }\n }),\n },\n }\n },\n }\n}\n\nexport const navigationMenu = { init, update, connect, isOpen }\n"]}
|
|
@@ -14,25 +14,41 @@ export interface NumberInputState {
|
|
|
14
14
|
/** Allow a free-text input value while the user is typing. */
|
|
15
15
|
rawText: string;
|
|
16
16
|
}
|
|
17
|
-
export type NumberInputMsg =
|
|
17
|
+
export type NumberInputMsg =
|
|
18
|
+
/** @intent("Set the numeric value (clamped to min/max, snapped to step)") */
|
|
19
|
+
{
|
|
18
20
|
type: 'setValue';
|
|
19
21
|
value: number | null;
|
|
20
|
-
}
|
|
22
|
+
}
|
|
23
|
+
/** @humanOnly */
|
|
24
|
+
| {
|
|
21
25
|
type: 'setRawText';
|
|
22
26
|
text: string;
|
|
23
|
-
}
|
|
27
|
+
}
|
|
28
|
+
/** @intent("Commit the in-progress text input — parse, clamp, snap, and update value") */
|
|
29
|
+
| {
|
|
24
30
|
type: 'commit';
|
|
25
|
-
}
|
|
31
|
+
}
|
|
32
|
+
/** @intent("Increase value by step (or step × multiplier)") */
|
|
33
|
+
| {
|
|
26
34
|
type: 'increment';
|
|
27
35
|
multiplier?: number;
|
|
28
|
-
}
|
|
36
|
+
}
|
|
37
|
+
/** @intent("Decrease value by step (or step × multiplier)") */
|
|
38
|
+
| {
|
|
29
39
|
type: 'decrement';
|
|
30
40
|
multiplier?: number;
|
|
31
|
-
}
|
|
41
|
+
}
|
|
42
|
+
/** @intent("Snap value to the configured minimum") */
|
|
43
|
+
| {
|
|
32
44
|
type: 'toMin';
|
|
33
|
-
}
|
|
45
|
+
}
|
|
46
|
+
/** @intent("Snap value to the configured maximum") */
|
|
47
|
+
| {
|
|
34
48
|
type: 'toMax';
|
|
35
|
-
}
|
|
49
|
+
}
|
|
50
|
+
/** @humanOnly */
|
|
51
|
+
| {
|
|
36
52
|
type: 'setDisabled';
|
|
37
53
|
disabled: boolean;
|
|
38
54
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"number-input.d.ts","sourceRoot":"","sources":["../../src/components/number-input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAKrC;;;;GAIG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"number-input.d.ts","sourceRoot":"","sources":["../../src/components/number-input.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useContext } from '@llui/dom';
|
|
1
|
+
import { useContext, tagSend } from '@llui/dom';
|
|
2
2
|
import { LocaleContext } from '../locale.js';
|
|
3
3
|
export function init(opts = {}) {
|
|
4
4
|
const value = opts.value ?? null;
|
|
@@ -111,15 +111,15 @@ export function connect(get, send, opts = {}) {
|
|
|
111
111
|
value: (s) => get(s).rawText,
|
|
112
112
|
'data-scope': 'number-input',
|
|
113
113
|
'data-part': 'input',
|
|
114
|
-
onInput: (e) => {
|
|
114
|
+
onInput: tagSend(send, ['setRawText'], (e) => {
|
|
115
115
|
const text = e.target.value;
|
|
116
116
|
send({ type: 'setRawText', text });
|
|
117
117
|
const parsed = parseFloat(text);
|
|
118
118
|
if (!isNaN(parsed))
|
|
119
119
|
trySetValue(parsed);
|
|
120
|
-
},
|
|
121
|
-
onBlur: () => send({ type: 'commit' }),
|
|
122
|
-
onKeyDown: (e) => {
|
|
120
|
+
}),
|
|
121
|
+
onBlur: tagSend(send, ['commit'], () => send({ type: 'commit' })),
|
|
122
|
+
onKeyDown: tagSend(send, ['increment', 'decrement', 'toMin', 'toMax', 'commit'], (e) => {
|
|
123
123
|
switch (e.key) {
|
|
124
124
|
case 'ArrowUp':
|
|
125
125
|
e.preventDefault();
|
|
@@ -150,7 +150,7 @@ export function connect(get, send, opts = {}) {
|
|
|
150
150
|
send({ type: 'commit' });
|
|
151
151
|
return;
|
|
152
152
|
}
|
|
153
|
-
},
|
|
153
|
+
}),
|
|
154
154
|
},
|
|
155
155
|
increment: {
|
|
156
156
|
type: 'button',
|
|
@@ -162,7 +162,7 @@ export function connect(get, send, opts = {}) {
|
|
|
162
162
|
'data-scope': 'number-input',
|
|
163
163
|
'data-part': 'increment',
|
|
164
164
|
tabIndex: -1,
|
|
165
|
-
onClick: () => send({ type: 'increment' }),
|
|
165
|
+
onClick: tagSend(send, ['increment'], () => send({ type: 'increment' })),
|
|
166
166
|
},
|
|
167
167
|
decrement: {
|
|
168
168
|
type: 'button',
|
|
@@ -174,7 +174,7 @@ export function connect(get, send, opts = {}) {
|
|
|
174
174
|
'data-scope': 'number-input',
|
|
175
175
|
'data-part': 'decrement',
|
|
176
176
|
tabIndex: -1,
|
|
177
|
-
onClick: () => send({ type: 'decrement' }),
|
|
177
|
+
onClick: tagSend(send, ['decrement'], () => send({ type: 'decrement' })),
|
|
178
178
|
},
|
|
179
179
|
};
|
|
180
180
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"number-input.js","sourceRoot":"","sources":["../../src/components/number-input.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send } from '@llui/dom'\nimport { useContext } from '@llui/dom'\nimport { LocaleContext } from '../locale.js'\nimport type { Locale } from '../locale.js'\n\n/**\n * Number input — numeric field with increment/decrement buttons. Clamps\n * to min/max and snaps to step. Keyboard: Arrow Up/Down, PageUp/PageDown,\n * Home/End.\n */\n\nexport interface NumberInputState {\n value: number | null\n min: number\n max: number\n step: number\n disabled: boolean\n readOnly: boolean\n /** Allow a free-text input value while the user is typing. */\n rawText: string\n}\n\nexport type NumberInputMsg =\n | { type: 'setValue'; value: number | null }\n | { type: 'setRawText'; text: string }\n | { type: 'commit' }\n | { type: 'increment'; multiplier?: number }\n | { type: 'decrement'; multiplier?: number }\n | { type: 'toMin' }\n | { type: 'toMax' }\n | { type: 'setDisabled'; disabled: boolean }\n\nexport interface NumberInputInit {\n value?: number | null\n min?: number\n max?: number\n step?: number\n disabled?: boolean\n readOnly?: boolean\n}\n\nexport function init(opts: NumberInputInit = {}): NumberInputState {\n const value = opts.value ?? null\n return {\n value,\n min: opts.min ?? -Infinity,\n max: opts.max ?? Infinity,\n step: opts.step ?? 1,\n disabled: opts.disabled ?? false,\n readOnly: opts.readOnly ?? false,\n rawText: value === null ? '' : String(value),\n }\n}\n\nfunction clamp(n: number, min: number, max: number): number {\n if (n < min) return min\n if (n > max) return max\n return n\n}\n\nfunction snap(n: number, step: number, anchor = 0): number {\n if (step <= 0) return n\n const origin = isFinite(anchor) ? anchor : 0\n const decimals = decimalPlaces(step)\n const steps = Math.round((n - origin) / step)\n const snapped = origin + steps * step\n return Number(snapped.toFixed(decimals))\n}\n\nfunction decimalPlaces(n: number): number {\n if (Math.floor(n) === n) return 0\n const str = n.toString()\n const dot = str.indexOf('.')\n return dot === -1 ? 0 : str.length - dot - 1\n}\n\nexport function update(state: NumberInputState, msg: NumberInputMsg): [NumberInputState, never[]] {\n if (msg.type !== 'setDisabled' && (state.disabled || state.readOnly)) {\n // Allow setRawText for controlled typing? No — disabled means no interaction.\n return [state, []]\n }\n switch (msg.type) {\n case 'setValue': {\n const v =\n msg.value === null\n ? null\n : clamp(snap(msg.value, state.step, state.min), state.min, state.max)\n return [{ ...state, value: v, rawText: v === null ? '' : String(v) }, []]\n }\n case 'setRawText':\n return [{ ...state, rawText: msg.text }, []]\n case 'commit': {\n const parsed = parseFloat(state.rawText)\n if (isNaN(parsed))\n return [{ ...state, rawText: state.value === null ? '' : String(state.value) }, []]\n const v = clamp(snap(parsed, state.step, state.min), state.min, state.max)\n return [{ ...state, value: v, rawText: String(v) }, []]\n }\n case 'increment': {\n const base = state.value ?? 0\n const raw = base + state.step * (msg.multiplier ?? 1)\n const decimals = decimalPlaces(state.step)\n const v = clamp(Number(raw.toFixed(decimals)), state.min, state.max)\n return [{ ...state, value: v, rawText: String(v) }, []]\n }\n case 'decrement': {\n const base = state.value ?? 0\n const raw = base - state.step * (msg.multiplier ?? 1)\n const decimals = decimalPlaces(state.step)\n const v = clamp(Number(raw.toFixed(decimals)), state.min, state.max)\n return [{ ...state, value: v, rawText: String(v) }, []]\n }\n case 'toMin':\n return [{ ...state, value: state.min, rawText: String(state.min) }, []]\n case 'toMax':\n return [{ ...state, value: state.max, rawText: String(state.max) }, []]\n case 'setDisabled':\n return [{ ...state, disabled: msg.disabled }, []]\n }\n}\n\nexport interface NumberInputParts<S> {\n root: {\n 'data-scope': 'number-input'\n 'data-part': 'root'\n 'data-disabled': (s: S) => '' | undefined\n }\n input: {\n type: 'text'\n role: 'spinbutton'\n inputMode: 'decimal'\n 'aria-valuemin': (s: S) => number | undefined\n 'aria-valuemax': (s: S) => number | undefined\n 'aria-valuenow': (s: S) => number | undefined\n 'aria-disabled': (s: S) => 'true' | undefined\n 'aria-readonly': (s: S) => 'true' | undefined\n disabled: (s: S) => boolean\n readOnly: (s: S) => boolean\n value: (s: S) => string\n 'data-scope': 'number-input'\n 'data-part': 'input'\n onInput: (e: Event) => void\n onBlur: (e: FocusEvent) => void\n onKeyDown: (e: KeyboardEvent) => void\n }\n increment: {\n type: 'button'\n 'aria-label': string | ((s: S) => string)\n 'aria-disabled': (s: S) => 'true' | undefined\n disabled: (s: S) => boolean\n 'data-scope': 'number-input'\n 'data-part': 'increment'\n tabIndex: -1\n onClick: (e: MouseEvent) => void\n }\n decrement: {\n type: 'button'\n 'aria-label': string | ((s: S) => string)\n 'aria-disabled': (s: S) => 'true' | undefined\n disabled: (s: S) => boolean\n 'data-scope': 'number-input'\n 'data-part': 'decrement'\n tabIndex: -1\n onClick: (e: MouseEvent) => void\n }\n}\n\nexport interface ConnectOptions {\n incrementLabel?: string\n decrementLabel?: string\n /** Validate the numeric value before committing. Non-empty array blocks setValue. */\n validate?: (value: number) => string[] | null\n}\n\nexport function connect<S>(\n get: (s: S) => NumberInputState,\n send: Send<NumberInputMsg>,\n opts: ConnectOptions = {},\n): NumberInputParts<S> {\n const locale = useContext<S, Locale>(LocaleContext)\n const incrementLabel: string | ((s: S) => string) =\n opts.incrementLabel ?? ((s: S) => locale(s).numberInput.increment)\n const decrementLabel: string | ((s: S) => string) =\n opts.decrementLabel ?? ((s: S) => locale(s).numberInput.decrement)\n const validate = opts.validate\n\n const trySetValue = (value: number) => {\n if (validate) {\n const errors = validate(value)\n if (errors && errors.length > 0) return\n }\n send({ type: 'setValue', value })\n }\n\n return {\n root: {\n 'data-scope': 'number-input',\n 'data-part': 'root',\n 'data-disabled': (s) => (get(s).disabled ? '' : undefined),\n },\n input: {\n type: 'text',\n role: 'spinbutton',\n inputMode: 'decimal',\n 'aria-valuemin': (s) => (isFinite(get(s).min) ? get(s).min : undefined),\n 'aria-valuemax': (s) => (isFinite(get(s).max) ? get(s).max : undefined),\n 'aria-valuenow': (s) => get(s).value ?? undefined,\n 'aria-disabled': (s) => (get(s).disabled ? 'true' : undefined),\n 'aria-readonly': (s) => (get(s).readOnly ? 'true' : undefined),\n disabled: (s) => get(s).disabled,\n readOnly: (s) => get(s).readOnly,\n value: (s) => get(s).rawText,\n 'data-scope': 'number-input',\n 'data-part': 'input',\n onInput: (e) => {\n const text = (e.target as HTMLInputElement).value\n send({ type: 'setRawText', text })\n const parsed = parseFloat(text)\n if (!isNaN(parsed)) trySetValue(parsed)\n },\n onBlur: () => send({ type: 'commit' }),\n onKeyDown: (e) => {\n switch (e.key) {\n case 'ArrowUp':\n e.preventDefault()\n send({ type: 'increment' })\n return\n case 'ArrowDown':\n e.preventDefault()\n send({ type: 'decrement' })\n return\n case 'PageUp':\n e.preventDefault()\n send({ type: 'increment', multiplier: 10 })\n return\n case 'PageDown':\n e.preventDefault()\n send({ type: 'decrement', multiplier: 10 })\n return\n case 'Home':\n e.preventDefault()\n send({ type: 'toMin' })\n return\n case 'End':\n e.preventDefault()\n send({ type: 'toMax' })\n return\n case 'Enter':\n e.preventDefault()\n send({ type: 'commit' })\n return\n }\n },\n },\n increment: {\n type: 'button',\n 'aria-label': incrementLabel,\n 'aria-disabled': (s) =>\n get(s).disabled || get(s).readOnly || (get(s).value ?? 0) >= get(s).max\n ? 'true'\n : undefined,\n disabled: (s) => get(s).disabled || get(s).readOnly || (get(s).value ?? 0) >= get(s).max,\n 'data-scope': 'number-input',\n 'data-part': 'increment',\n tabIndex: -1,\n onClick: () => send({ type: 'increment' }),\n },\n decrement: {\n type: 'button',\n 'aria-label': decrementLabel,\n 'aria-disabled': (s) =>\n get(s).disabled || get(s).readOnly || (get(s).value ?? 0) <= get(s).min\n ? 'true'\n : undefined,\n disabled: (s) => get(s).disabled || get(s).readOnly || (get(s).value ?? 0) <= get(s).min,\n 'data-scope': 'number-input',\n 'data-part': 'decrement',\n tabIndex: -1,\n onClick: () => send({ type: 'decrement' }),\n },\n }\n}\n\nexport const numberInput = { init, update, connect }\n"]}
|
|
1
|
+
{"version":3,"file":"number-input.js","sourceRoot":"","sources":["../../src/components/number-input.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send } from '@llui/dom'\nimport { useContext, tagSend } from '@llui/dom'\nimport { LocaleContext } from '../locale.js'\nimport type { Locale } from '../locale.js'\n\n/**\n * Number input — numeric field with increment/decrement buttons. Clamps\n * to min/max and snaps to step. Keyboard: Arrow Up/Down, PageUp/PageDown,\n * Home/End.\n */\n\nexport interface NumberInputState {\n value: number | null\n min: number\n max: number\n step: number\n disabled: boolean\n readOnly: boolean\n /** Allow a free-text input value while the user is typing. */\n rawText: string\n}\n\nexport type NumberInputMsg =\n /** @intent(\"Set the numeric value (clamped to min/max, snapped to step)\") */\n | { type: 'setValue'; value: number | null }\n /** @humanOnly */\n | { type: 'setRawText'; text: string }\n /** @intent(\"Commit the in-progress text input — parse, clamp, snap, and update value\") */\n | { type: 'commit' }\n /** @intent(\"Increase value by step (or step × multiplier)\") */\n | { type: 'increment'; multiplier?: number }\n /** @intent(\"Decrease value by step (or step × multiplier)\") */\n | { type: 'decrement'; multiplier?: number }\n /** @intent(\"Snap value to the configured minimum\") */\n | { type: 'toMin' }\n /** @intent(\"Snap value to the configured maximum\") */\n | { type: 'toMax' }\n /** @humanOnly */\n | { type: 'setDisabled'; disabled: boolean }\n\nexport interface NumberInputInit {\n value?: number | null\n min?: number\n max?: number\n step?: number\n disabled?: boolean\n readOnly?: boolean\n}\n\nexport function init(opts: NumberInputInit = {}): NumberInputState {\n const value = opts.value ?? null\n return {\n value,\n min: opts.min ?? -Infinity,\n max: opts.max ?? Infinity,\n step: opts.step ?? 1,\n disabled: opts.disabled ?? false,\n readOnly: opts.readOnly ?? false,\n rawText: value === null ? '' : String(value),\n }\n}\n\nfunction clamp(n: number, min: number, max: number): number {\n if (n < min) return min\n if (n > max) return max\n return n\n}\n\nfunction snap(n: number, step: number, anchor = 0): number {\n if (step <= 0) return n\n const origin = isFinite(anchor) ? anchor : 0\n const decimals = decimalPlaces(step)\n const steps = Math.round((n - origin) / step)\n const snapped = origin + steps * step\n return Number(snapped.toFixed(decimals))\n}\n\nfunction decimalPlaces(n: number): number {\n if (Math.floor(n) === n) return 0\n const str = n.toString()\n const dot = str.indexOf('.')\n return dot === -1 ? 0 : str.length - dot - 1\n}\n\nexport function update(state: NumberInputState, msg: NumberInputMsg): [NumberInputState, never[]] {\n if (msg.type !== 'setDisabled' && (state.disabled || state.readOnly)) {\n // Allow setRawText for controlled typing? No — disabled means no interaction.\n return [state, []]\n }\n switch (msg.type) {\n case 'setValue': {\n const v =\n msg.value === null\n ? null\n : clamp(snap(msg.value, state.step, state.min), state.min, state.max)\n return [{ ...state, value: v, rawText: v === null ? '' : String(v) }, []]\n }\n case 'setRawText':\n return [{ ...state, rawText: msg.text }, []]\n case 'commit': {\n const parsed = parseFloat(state.rawText)\n if (isNaN(parsed))\n return [{ ...state, rawText: state.value === null ? '' : String(state.value) }, []]\n const v = clamp(snap(parsed, state.step, state.min), state.min, state.max)\n return [{ ...state, value: v, rawText: String(v) }, []]\n }\n case 'increment': {\n const base = state.value ?? 0\n const raw = base + state.step * (msg.multiplier ?? 1)\n const decimals = decimalPlaces(state.step)\n const v = clamp(Number(raw.toFixed(decimals)), state.min, state.max)\n return [{ ...state, value: v, rawText: String(v) }, []]\n }\n case 'decrement': {\n const base = state.value ?? 0\n const raw = base - state.step * (msg.multiplier ?? 1)\n const decimals = decimalPlaces(state.step)\n const v = clamp(Number(raw.toFixed(decimals)), state.min, state.max)\n return [{ ...state, value: v, rawText: String(v) }, []]\n }\n case 'toMin':\n return [{ ...state, value: state.min, rawText: String(state.min) }, []]\n case 'toMax':\n return [{ ...state, value: state.max, rawText: String(state.max) }, []]\n case 'setDisabled':\n return [{ ...state, disabled: msg.disabled }, []]\n }\n}\n\nexport interface NumberInputParts<S> {\n root: {\n 'data-scope': 'number-input'\n 'data-part': 'root'\n 'data-disabled': (s: S) => '' | undefined\n }\n input: {\n type: 'text'\n role: 'spinbutton'\n inputMode: 'decimal'\n 'aria-valuemin': (s: S) => number | undefined\n 'aria-valuemax': (s: S) => number | undefined\n 'aria-valuenow': (s: S) => number | undefined\n 'aria-disabled': (s: S) => 'true' | undefined\n 'aria-readonly': (s: S) => 'true' | undefined\n disabled: (s: S) => boolean\n readOnly: (s: S) => boolean\n value: (s: S) => string\n 'data-scope': 'number-input'\n 'data-part': 'input'\n onInput: (e: Event) => void\n onBlur: (e: FocusEvent) => void\n onKeyDown: (e: KeyboardEvent) => void\n }\n increment: {\n type: 'button'\n 'aria-label': string | ((s: S) => string)\n 'aria-disabled': (s: S) => 'true' | undefined\n disabled: (s: S) => boolean\n 'data-scope': 'number-input'\n 'data-part': 'increment'\n tabIndex: -1\n onClick: (e: MouseEvent) => void\n }\n decrement: {\n type: 'button'\n 'aria-label': string | ((s: S) => string)\n 'aria-disabled': (s: S) => 'true' | undefined\n disabled: (s: S) => boolean\n 'data-scope': 'number-input'\n 'data-part': 'decrement'\n tabIndex: -1\n onClick: (e: MouseEvent) => void\n }\n}\n\nexport interface ConnectOptions {\n incrementLabel?: string\n decrementLabel?: string\n /** Validate the numeric value before committing. Non-empty array blocks setValue. */\n validate?: (value: number) => string[] | null\n}\n\nexport function connect<S>(\n get: (s: S) => NumberInputState,\n send: Send<NumberInputMsg>,\n opts: ConnectOptions = {},\n): NumberInputParts<S> {\n const locale = useContext<S, Locale>(LocaleContext)\n const incrementLabel: string | ((s: S) => string) =\n opts.incrementLabel ?? ((s: S) => locale(s).numberInput.increment)\n const decrementLabel: string | ((s: S) => string) =\n opts.decrementLabel ?? ((s: S) => locale(s).numberInput.decrement)\n const validate = opts.validate\n\n const trySetValue = (value: number) => {\n if (validate) {\n const errors = validate(value)\n if (errors && errors.length > 0) return\n }\n send({ type: 'setValue', value })\n }\n\n return {\n root: {\n 'data-scope': 'number-input',\n 'data-part': 'root',\n 'data-disabled': (s) => (get(s).disabled ? '' : undefined),\n },\n input: {\n type: 'text',\n role: 'spinbutton',\n inputMode: 'decimal',\n 'aria-valuemin': (s) => (isFinite(get(s).min) ? get(s).min : undefined),\n 'aria-valuemax': (s) => (isFinite(get(s).max) ? get(s).max : undefined),\n 'aria-valuenow': (s) => get(s).value ?? undefined,\n 'aria-disabled': (s) => (get(s).disabled ? 'true' : undefined),\n 'aria-readonly': (s) => (get(s).readOnly ? 'true' : undefined),\n disabled: (s) => get(s).disabled,\n readOnly: (s) => get(s).readOnly,\n value: (s) => get(s).rawText,\n 'data-scope': 'number-input',\n 'data-part': 'input',\n onInput: tagSend(send, ['setRawText'], (e) => {\n const text = (e.target as HTMLInputElement).value\n send({ type: 'setRawText', text })\n const parsed = parseFloat(text)\n if (!isNaN(parsed)) trySetValue(parsed)\n }),\n onBlur: tagSend(send, ['commit'], () => send({ type: 'commit' })),\n onKeyDown: tagSend(send, ['increment', 'decrement', 'toMin', 'toMax', 'commit'], (e) => {\n switch (e.key) {\n case 'ArrowUp':\n e.preventDefault()\n send({ type: 'increment' })\n return\n case 'ArrowDown':\n e.preventDefault()\n send({ type: 'decrement' })\n return\n case 'PageUp':\n e.preventDefault()\n send({ type: 'increment', multiplier: 10 })\n return\n case 'PageDown':\n e.preventDefault()\n send({ type: 'decrement', multiplier: 10 })\n return\n case 'Home':\n e.preventDefault()\n send({ type: 'toMin' })\n return\n case 'End':\n e.preventDefault()\n send({ type: 'toMax' })\n return\n case 'Enter':\n e.preventDefault()\n send({ type: 'commit' })\n return\n }\n }),\n },\n increment: {\n type: 'button',\n 'aria-label': incrementLabel,\n 'aria-disabled': (s) =>\n get(s).disabled || get(s).readOnly || (get(s).value ?? 0) >= get(s).max\n ? 'true'\n : undefined,\n disabled: (s) => get(s).disabled || get(s).readOnly || (get(s).value ?? 0) >= get(s).max,\n 'data-scope': 'number-input',\n 'data-part': 'increment',\n tabIndex: -1,\n onClick: tagSend(send, ['increment'], () => send({ type: 'increment' })),\n },\n decrement: {\n type: 'button',\n 'aria-label': decrementLabel,\n 'aria-disabled': (s) =>\n get(s).disabled || get(s).readOnly || (get(s).value ?? 0) <= get(s).min\n ? 'true'\n : undefined,\n disabled: (s) => get(s).disabled || get(s).readOnly || (get(s).value ?? 0) <= get(s).min,\n 'data-scope': 'number-input',\n 'data-part': 'decrement',\n tabIndex: -1,\n onClick: tagSend(send, ['decrement'], () => send({ type: 'decrement' })),\n },\n }\n}\n\nexport const numberInput = { init, update, connect }\n"]}
|
|
@@ -12,21 +12,35 @@ export interface PaginationState {
|
|
|
12
12
|
boundaries: number;
|
|
13
13
|
disabled: boolean;
|
|
14
14
|
}
|
|
15
|
-
export type PaginationMsg =
|
|
15
|
+
export type PaginationMsg =
|
|
16
|
+
/** @intent("Jump to a specific 1-based page number") */
|
|
17
|
+
{
|
|
16
18
|
type: 'goTo';
|
|
17
19
|
page: number;
|
|
18
|
-
}
|
|
20
|
+
}
|
|
21
|
+
/** @intent("Advance to the next page") */
|
|
22
|
+
| {
|
|
19
23
|
type: 'next';
|
|
20
|
-
}
|
|
24
|
+
}
|
|
25
|
+
/** @intent("Go back to the previous page") */
|
|
26
|
+
| {
|
|
21
27
|
type: 'prev';
|
|
22
|
-
}
|
|
28
|
+
}
|
|
29
|
+
/** @intent("Jump to the first page") */
|
|
30
|
+
| {
|
|
23
31
|
type: 'first';
|
|
24
|
-
}
|
|
32
|
+
}
|
|
33
|
+
/** @intent("Jump to the last page") */
|
|
34
|
+
| {
|
|
25
35
|
type: 'last';
|
|
26
|
-
}
|
|
36
|
+
}
|
|
37
|
+
/** @intent("Change how many items each page contains") */
|
|
38
|
+
| {
|
|
27
39
|
type: 'setPageSize';
|
|
28
40
|
pageSize: number;
|
|
29
|
-
}
|
|
41
|
+
}
|
|
42
|
+
/** @humanOnly */
|
|
43
|
+
| {
|
|
30
44
|
type: 'setTotal';
|
|
31
45
|
total: number;
|
|
32
46
|
};
|