@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.
Files changed (216) hide show
  1. package/dist/components/accordion.d.ts +27 -9
  2. package/dist/components/accordion.d.ts.map +1 -1
  3. package/dist/components/accordion.js +4 -3
  4. package/dist/components/accordion.js.map +1 -1
  5. package/dist/components/angle-slider.d.ts +15 -5
  6. package/dist/components/angle-slider.d.ts.map +1 -1
  7. package/dist/components/angle-slider.js +3 -2
  8. package/dist/components/angle-slider.js.map +1 -1
  9. package/dist/components/async-list.d.ts +18 -6
  10. package/dist/components/async-list.d.ts.map +1 -1
  11. package/dist/components/async-list.js +3 -2
  12. package/dist/components/async-list.js.map +1 -1
  13. package/dist/components/avatar.d.ts +12 -4
  14. package/dist/components/avatar.d.ts.map +1 -1
  15. package/dist/components/avatar.js +4 -3
  16. package/dist/components/avatar.js.map +1 -1
  17. package/dist/components/carousel.d.ts +21 -7
  18. package/dist/components/carousel.d.ts.map +1 -1
  19. package/dist/components/carousel.js +8 -8
  20. package/dist/components/carousel.js.map +1 -1
  21. package/dist/components/cascade-select.d.ts +9 -3
  22. package/dist/components/cascade-select.d.ts.map +1 -1
  23. package/dist/components/cascade-select.js +4 -4
  24. package/dist/components/cascade-select.js.map +1 -1
  25. package/dist/components/checkbox.d.ts +9 -3
  26. package/dist/components/checkbox.d.ts.map +1 -1
  27. package/dist/components/checkbox.js +4 -3
  28. package/dist/components/checkbox.js.map +1 -1
  29. package/dist/components/clipboard.d.ts +12 -4
  30. package/dist/components/clipboard.d.ts.map +1 -1
  31. package/dist/components/clipboard.js +3 -3
  32. package/dist/components/clipboard.js.map +1 -1
  33. package/dist/components/collapsible.d.ts +12 -4
  34. package/dist/components/collapsible.d.ts.map +1 -1
  35. package/dist/components/collapsible.js +2 -1
  36. package/dist/components/collapsible.js.map +1 -1
  37. package/dist/components/color-picker.d.ts +18 -6
  38. package/dist/components/color-picker.d.ts.map +1 -1
  39. package/dist/components/color-picker.js +5 -5
  40. package/dist/components/color-picker.js.map +1 -1
  41. package/dist/components/combobox.d.ts +39 -13
  42. package/dist/components/combobox.d.ts.map +1 -1
  43. package/dist/components/combobox.js +17 -9
  44. package/dist/components/combobox.js.map +1 -1
  45. package/dist/components/context-menu.d.ts +24 -8
  46. package/dist/components/context-menu.d.ts.map +1 -1
  47. package/dist/components/context-menu.js +8 -8
  48. package/dist/components/context-menu.js.map +1 -1
  49. package/dist/components/date-input.d.ts +21 -6
  50. package/dist/components/date-input.d.ts.map +1 -1
  51. package/dist/components/date-input.js +4 -4
  52. package/dist/components/date-input.js.map +1 -1
  53. package/dist/components/date-picker.d.ts +36 -12
  54. package/dist/components/date-picker.d.ts.map +1 -1
  55. package/dist/components/date-picker.js +15 -8
  56. package/dist/components/date-picker.js.map +1 -1
  57. package/dist/components/dialog.d.ts +12 -4
  58. package/dist/components/dialog.d.ts.map +1 -1
  59. package/dist/components/dialog.js +3 -3
  60. package/dist/components/dialog.js.map +1 -1
  61. package/dist/components/drawer.d.ts +12 -4
  62. package/dist/components/drawer.d.ts.map +1 -1
  63. package/dist/components/drawer.js +3 -3
  64. package/dist/components/drawer.js.map +1 -1
  65. package/dist/components/editable.d.ts +15 -5
  66. package/dist/components/editable.d.ts.map +1 -1
  67. package/dist/components/editable.js +14 -13
  68. package/dist/components/editable.js.map +1 -1
  69. package/dist/components/file-upload.d.ts +30 -10
  70. package/dist/components/file-upload.d.ts.map +1 -1
  71. package/dist/components/file-upload.js +10 -10
  72. package/dist/components/file-upload.js.map +1 -1
  73. package/dist/components/floating-panel.d.ts +48 -16
  74. package/dist/components/floating-panel.d.ts.map +1 -1
  75. package/dist/components/floating-panel.js +6 -6
  76. package/dist/components/floating-panel.js.map +1 -1
  77. package/dist/components/form.d.ts +18 -6
  78. package/dist/components/form.d.ts.map +1 -1
  79. package/dist/components/form.js +2 -1
  80. package/dist/components/form.js.map +1 -1
  81. package/dist/components/image-cropper.d.ts +33 -11
  82. package/dist/components/image-cropper.d.ts.map +1 -1
  83. package/dist/components/image-cropper.js +6 -6
  84. package/dist/components/image-cropper.js.map +1 -1
  85. package/dist/components/listbox.d.ts +33 -11
  86. package/dist/components/listbox.d.ts.map +1 -1
  87. package/dist/components/listbox.js +12 -4
  88. package/dist/components/listbox.js.map +1 -1
  89. package/dist/components/marquee.d.ts +21 -7
  90. package/dist/components/marquee.d.ts.map +1 -1
  91. package/dist/components/marquee.js +3 -2
  92. package/dist/components/marquee.js.map +1 -1
  93. package/dist/components/menu.d.ts +36 -12
  94. package/dist/components/menu.d.ts.map +1 -1
  95. package/dist/components/menu.js +23 -9
  96. package/dist/components/menu.js.map +1 -1
  97. package/dist/components/navigation-menu.d.ts +15 -5
  98. package/dist/components/navigation-menu.d.ts.map +1 -1
  99. package/dist/components/navigation-menu.js +8 -8
  100. package/dist/components/navigation-menu.js.map +1 -1
  101. package/dist/components/number-input.d.ts +24 -8
  102. package/dist/components/number-input.d.ts.map +1 -1
  103. package/dist/components/number-input.js +8 -8
  104. package/dist/components/number-input.js.map +1 -1
  105. package/dist/components/pagination.d.ts +21 -7
  106. package/dist/components/pagination.d.ts.map +1 -1
  107. package/dist/components/pagination.js +4 -4
  108. package/dist/components/pagination.js.map +1 -1
  109. package/dist/components/password-input.d.ts +9 -3
  110. package/dist/components/password-input.d.ts.map +1 -1
  111. package/dist/components/password-input.js +3 -3
  112. package/dist/components/password-input.js.map +1 -1
  113. package/dist/components/pin-input.d.ts +15 -5
  114. package/dist/components/pin-input.d.ts.map +1 -1
  115. package/dist/components/pin-input.js +8 -7
  116. package/dist/components/pin-input.js.map +1 -1
  117. package/dist/components/popover.d.ts +12 -4
  118. package/dist/components/popover.d.ts.map +1 -1
  119. package/dist/components/popover.js +3 -3
  120. package/dist/components/popover.js.map +1 -1
  121. package/dist/components/presence.d.ts +15 -5
  122. package/dist/components/presence.d.ts.map +1 -1
  123. package/dist/components/presence.js.map +1 -1
  124. package/dist/components/progress.d.ts +6 -2
  125. package/dist/components/progress.d.ts.map +1 -1
  126. package/dist/components/progress.js.map +1 -1
  127. package/dist/components/qr-code.d.ts +9 -3
  128. package/dist/components/qr-code.d.ts.map +1 -1
  129. package/dist/components/qr-code.js.map +1 -1
  130. package/dist/components/radio-group.d.ts +18 -6
  131. package/dist/components/radio-group.d.ts.map +1 -1
  132. package/dist/components/radio-group.js +4 -3
  133. package/dist/components/radio-group.js.map +1 -1
  134. package/dist/components/rating-group.d.ts +21 -7
  135. package/dist/components/rating-group.d.ts.map +1 -1
  136. package/dist/components/rating-group.js +8 -7
  137. package/dist/components/rating-group.js.map +1 -1
  138. package/dist/components/scroll-area.d.ts +9 -3
  139. package/dist/components/scroll-area.d.ts.map +1 -1
  140. package/dist/components/scroll-area.js +5 -4
  141. package/dist/components/scroll-area.js.map +1 -1
  142. package/dist/components/select.d.ts +42 -14
  143. package/dist/components/select.d.ts.map +1 -1
  144. package/dist/components/select.js +4 -4
  145. package/dist/components/select.js.map +1 -1
  146. package/dist/components/signature-pad.d.ts +24 -8
  147. package/dist/components/signature-pad.d.ts.map +1 -1
  148. package/dist/components/signature-pad.js +3 -3
  149. package/dist/components/signature-pad.js.map +1 -1
  150. package/dist/components/slider.d.ts +21 -7
  151. package/dist/components/slider.d.ts.map +1 -1
  152. package/dist/components/slider.js.map +1 -1
  153. package/dist/components/sortable.d.ts +18 -6
  154. package/dist/components/sortable.d.ts.map +1 -1
  155. package/dist/components/sortable.js +11 -10
  156. package/dist/components/sortable.js.map +1 -1
  157. package/dist/components/splitter.d.ts +21 -7
  158. package/dist/components/splitter.d.ts.map +1 -1
  159. package/dist/components/splitter.js +5 -4
  160. package/dist/components/splitter.js.map +1 -1
  161. package/dist/components/steps.d.ts +21 -7
  162. package/dist/components/steps.d.ts.map +1 -1
  163. package/dist/components/steps.js +4 -4
  164. package/dist/components/steps.js.map +1 -1
  165. package/dist/components/switch.d.ts +9 -3
  166. package/dist/components/switch.d.ts.map +1 -1
  167. package/dist/components/switch.js +4 -3
  168. package/dist/components/switch.js.map +1 -1
  169. package/dist/components/tabs.d.ts +24 -8
  170. package/dist/components/tabs.d.ts.map +1 -1
  171. package/dist/components/tabs.js +7 -6
  172. package/dist/components/tabs.js.map +1 -1
  173. package/dist/components/tags-input.d.ts +27 -9
  174. package/dist/components/tags-input.d.ts.map +1 -1
  175. package/dist/components/tags-input.js +10 -10
  176. package/dist/components/tags-input.js.map +1 -1
  177. package/dist/components/theme-switch.d.ts.map +1 -1
  178. package/dist/components/theme-switch.js +3 -2
  179. package/dist/components/theme-switch.js.map +1 -1
  180. package/dist/components/time-picker.d.ts +27 -9
  181. package/dist/components/time-picker.d.ts.map +1 -1
  182. package/dist/components/time-picker.js +10 -10
  183. package/dist/components/time-picker.js.map +1 -1
  184. package/dist/components/timer.d.ts +15 -5
  185. package/dist/components/timer.d.ts.map +1 -1
  186. package/dist/components/timer.js +4 -4
  187. package/dist/components/timer.js.map +1 -1
  188. package/dist/components/toast.d.ts +24 -8
  189. package/dist/components/toast.d.ts.map +1 -1
  190. package/dist/components/toast.js +6 -6
  191. package/dist/components/toast.js.map +1 -1
  192. package/dist/components/toc.d.ts +15 -5
  193. package/dist/components/toc.d.ts.map +1 -1
  194. package/dist/components/toc.js +2 -2
  195. package/dist/components/toc.js.map +1 -1
  196. package/dist/components/toggle-group.d.ts +15 -5
  197. package/dist/components/toggle-group.d.ts.map +1 -1
  198. package/dist/components/toggle-group.js +4 -3
  199. package/dist/components/toggle-group.js.map +1 -1
  200. package/dist/components/toggle.d.ts +9 -3
  201. package/dist/components/toggle.d.ts.map +1 -1
  202. package/dist/components/toggle.js +4 -3
  203. package/dist/components/toggle.js.map +1 -1
  204. package/dist/components/tooltip.d.ts +12 -4
  205. package/dist/components/tooltip.d.ts.map +1 -1
  206. package/dist/components/tooltip.js +3 -3
  207. package/dist/components/tooltip.js.map +1 -1
  208. package/dist/components/tour.d.ts +18 -6
  209. package/dist/components/tour.d.ts.map +1 -1
  210. package/dist/components/tour.js +6 -6
  211. package/dist/components/tour.js.map +1 -1
  212. package/dist/components/tree-view.d.ts +75 -25
  213. package/dist/components/tree-view.d.ts.map +1 -1
  214. package/dist/components/tree-view.js +18 -7
  215. package/dist/components/tree-view.js.map +1 -1
  216. package/package.json +3 -3
@@ -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
- const handleMenuKey = (e) => {
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":"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"}
1
+ {"version":3,"file":"navigation-menu.d.ts","sourceRoot":"","sources":["../../src/components/navigation-menu.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
  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":"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"}
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
  };