ajo-ui 0.1.0

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 (103) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +126 -0
  3. package/dist/accordion.js +130 -0
  4. package/dist/avatar.js +54 -0
  5. package/dist/calendar.js +2 -0
  6. package/dist/carousel.js +239 -0
  7. package/dist/chart.js +790 -0
  8. package/dist/checkbox-group.js +70 -0
  9. package/dist/checkbox.js +77 -0
  10. package/dist/chunks/bar-CVafh6C1.js +99 -0
  11. package/dist/chunks/calendar-q8jZ8Cxr.js +1923 -0
  12. package/dist/chunks/collection-DtRB63U4.js +111 -0
  13. package/dist/chunks/data-table-DpkWv4y4.js +1039 -0
  14. package/dist/chunks/menu-B45IyHHC.js +704 -0
  15. package/dist/chunks/native-BJdhd9XJ.js +20 -0
  16. package/dist/chunks/popup-C8Bb3l_g.js +459 -0
  17. package/dist/chunks/popup-surface-BDCgtVU0.js +18 -0
  18. package/dist/chunks/position-D6_i_SRn.js +434 -0
  19. package/dist/chunks/virtual-list-B7hjGkjk.js +413 -0
  20. package/dist/collapsible.js +106 -0
  21. package/dist/command.js +263 -0
  22. package/dist/context-menu.js +112 -0
  23. package/dist/data-table.js +5 -0
  24. package/dist/dialog.js +207 -0
  25. package/dist/direction.js +22 -0
  26. package/dist/drawer.js +139 -0
  27. package/dist/field.js +26 -0
  28. package/dist/index.js +38 -0
  29. package/dist/input-date.js +994 -0
  30. package/dist/input-group.js +52 -0
  31. package/dist/input-otp.js +179 -0
  32. package/dist/menu.js +2 -0
  33. package/dist/menubar.js +236 -0
  34. package/dist/message-scroller.js +446 -0
  35. package/dist/navigation-menu.js +330 -0
  36. package/dist/popover.js +307 -0
  37. package/dist/progress.js +39 -0
  38. package/dist/radio-group.js +107 -0
  39. package/dist/resizable.js +172 -0
  40. package/dist/select.js +961 -0
  41. package/dist/sidebar.js +343 -0
  42. package/dist/slider.js +259 -0
  43. package/dist/switch.js +53 -0
  44. package/dist/tabs.js +182 -0
  45. package/dist/toast.js +492 -0
  46. package/dist/toggle-group.js +111 -0
  47. package/dist/toggle.js +52 -0
  48. package/dist/toolbar.js +127 -0
  49. package/dist/tooltip.js +196 -0
  50. package/dist/utils.js +104 -0
  51. package/dist/virtual-list.js +2 -0
  52. package/package.json +250 -0
  53. package/src/accordion.tsx +261 -0
  54. package/src/availability.ts +261 -0
  55. package/src/avatar.tsx +99 -0
  56. package/src/bar.ts +156 -0
  57. package/src/calendar.tsx +1441 -0
  58. package/src/carousel.tsx +424 -0
  59. package/src/chart.tsx +1194 -0
  60. package/src/checkbox-group.tsx +132 -0
  61. package/src/checkbox.tsx +130 -0
  62. package/src/collapsible.tsx +188 -0
  63. package/src/collection.ts +154 -0
  64. package/src/command.tsx +511 -0
  65. package/src/context-menu.tsx +233 -0
  66. package/src/data-table-contract.ts +143 -0
  67. package/src/data-table-model.ts +760 -0
  68. package/src/data-table.tsx +475 -0
  69. package/src/dialog.tsx +393 -0
  70. package/src/direction.tsx +45 -0
  71. package/src/drawer.tsx +251 -0
  72. package/src/field.tsx +61 -0
  73. package/src/index.ts +37 -0
  74. package/src/input-date.tsx +1539 -0
  75. package/src/input-group.tsx +142 -0
  76. package/src/input-otp.tsx +324 -0
  77. package/src/menu-cluster.ts +124 -0
  78. package/src/menu.tsx +1095 -0
  79. package/src/menubar.tsx +459 -0
  80. package/src/message-scroller.tsx +732 -0
  81. package/src/native.ts +26 -0
  82. package/src/navigation-menu.tsx +578 -0
  83. package/src/popover.tsx +519 -0
  84. package/src/popup-surface.tsx +31 -0
  85. package/src/popup.ts +569 -0
  86. package/src/position.ts +523 -0
  87. package/src/progress.tsx +70 -0
  88. package/src/radio-group.tsx +186 -0
  89. package/src/resizable.tsx +310 -0
  90. package/src/segments.ts +922 -0
  91. package/src/select.tsx +1501 -0
  92. package/src/sidebar.tsx +683 -0
  93. package/src/slider.tsx +424 -0
  94. package/src/switch.tsx +104 -0
  95. package/src/tabs.tsx +314 -0
  96. package/src/toast.tsx +923 -0
  97. package/src/toggle-group.tsx +249 -0
  98. package/src/toggle.tsx +91 -0
  99. package/src/toolbar.tsx +212 -0
  100. package/src/tooltip.tsx +359 -0
  101. package/src/utils.ts +204 -0
  102. package/src/virtual-list.tsx +205 -0
  103. package/src/virtual.ts +385 -0
package/dist/select.js ADDED
@@ -0,0 +1,961 @@
1
+ import { defaultResultsLabel, flag, matchesTokens, resolveFilter, text, triggerAttrs } from "./utils.js";
2
+ import { t as collection } from "./chunks/collection-DtRB63U4.js";
3
+ import { n as popup, t as contentAttrs } from "./chunks/popup-C8Bb3l_g.js";
4
+ import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "./input-group.js";
5
+ import { announce, callHandler, callRef, controlled, dom, id, listen, roving, statefulRootAttrs, typeahead } from "ajo-cloves";
6
+ import { context } from "ajo/context";
7
+ import { Fragment, jsx, jsxs } from "ajo/jsx-runtime";
8
+ //#region packages/ajo-ui/src/select.tsx
9
+ var SelectContext = context(null);
10
+ var fallbackString = (item) => {
11
+ if (item == null) return "";
12
+ if (typeof item === "string" || typeof item === "number" || typeof item === "bigint") return String(item);
13
+ if (typeof item === "object") {
14
+ const record = item;
15
+ if (record.label != null) return text(record.label);
16
+ if (record.value != null) return text(record.value);
17
+ }
18
+ return String(item);
19
+ };
20
+ var defaultFilter = (_item, search, label) => matchesTokens(search, label);
21
+ var array = (value) => Array.isArray(value) ? value : value == null ? [] : [value];
22
+ var CREATE_KEY = "\0create";
23
+ var selectItems = collection("select", { rendered: false });
24
+ var SelectRoot = function* ({ defaultInputValue, defaultOpen, defaultValue, inputValue, open }) {
25
+ const selectId = id("select");
26
+ const ownerDocument = dom(this) ? this.ownerDocument : null;
27
+ const fallbackInputId = `${selectId}-input`;
28
+ const labels = /* @__PURE__ */ new Map();
29
+ let activeKey = "";
30
+ let disabled = false;
31
+ let fieldReference = null;
32
+ let input = null;
33
+ let inputId = fallbackInputId;
34
+ let announceResults = false;
35
+ let closeFocus = null;
36
+ let lastResultCount = -1;
37
+ let onCreate;
38
+ let onInputValueChange;
39
+ let onOpenChange;
40
+ let onValueChange;
41
+ let pendingFocus = false;
42
+ let pendingReference = null;
43
+ let pendingSeed = "";
44
+ let pendingStep = 0;
45
+ let pop;
46
+ const searchState = controlled(this, {
47
+ fallback: String(inputValue ?? defaultInputValue ?? ""),
48
+ onChange: (next, event) => onInputValueChange?.(next, event)
49
+ });
50
+ const valueState = controlled(this, {
51
+ fallback: defaultValue ?? null,
52
+ onChange: (next, event) => onValueChange?.(next, event)
53
+ });
54
+ const live = announce(this);
55
+ const itemDomId = (key) => `${selectId}-item-${encodeURIComponent(key)}`;
56
+ const inPopup = (element) => Boolean(element && pop.content && pop.content.contains(element));
57
+ const clearFocusIntent = () => {
58
+ pendingFocus = false;
59
+ pendingReference = null;
60
+ pendingSeed = "";
61
+ pendingStep = 0;
62
+ };
63
+ pop = popup(this, {
64
+ prefix: "select",
65
+ profile: "select",
66
+ initialOpen: Boolean(open ?? defaultOpen),
67
+ disabled: () => disabled,
68
+ onOpenChange: (next, event) => onOpenChange?.(next, event),
69
+ reference: (view) => view.reference,
70
+ source: (view) => dom(view.reference) ? view.reference : null,
71
+ referenceHidden: "close",
72
+ dismiss: {
73
+ escape: false,
74
+ outside: true,
75
+ inside: (view) => [dom(view.reference) ? view.reference : null, view.content],
76
+ onDismiss: (event) => setOpen(false, event)
77
+ },
78
+ onPosition: () => {
79
+ if (!pendingFocus || pop.reference !== pendingReference) return;
80
+ pendingFocus = false;
81
+ const popupInput = inPopup(input) ? input : null;
82
+ if (popupInput) {
83
+ popupInput.focus();
84
+ if (pendingSeed) setSearch(pendingSeed);
85
+ } else {
86
+ const items = selectItems.items(pop.content);
87
+ let target = items.find((item) => selectedKeySet.has(item.dataset.value ?? "")) ?? items[0];
88
+ if (pendingStep && target) target = items[items.indexOf(target) + pendingStep] ?? target;
89
+ selectItems.focusItem(pop.content, target);
90
+ }
91
+ pendingReference = null;
92
+ pendingSeed = "";
93
+ pendingStep = 0;
94
+ },
95
+ onSync: (opened) => {
96
+ if (!opened) finishClose();
97
+ }
98
+ });
99
+ const restoreFocus = () => {
100
+ const active = ownerDocument?.activeElement;
101
+ const target = closeFocus ?? pop.trigger ?? input;
102
+ closeFocus = null;
103
+ if (!active || !pop.content?.contains(active)) return;
104
+ target?.focus();
105
+ };
106
+ const finishClose = () => {
107
+ restoreFocus();
108
+ clearFocusIntent();
109
+ if (!searchState.controlled && searchState.value) searchState.init("");
110
+ activeKey = "";
111
+ };
112
+ const setOpen = (next, event, focus) => {
113
+ if (disabled && next) return;
114
+ if (next === pop.open) return;
115
+ if (next) {
116
+ closeFocus = null;
117
+ if (inPopup(input)) pendingFocus = true;
118
+ }
119
+ if (!next) {
120
+ closeFocus = focus ?? null;
121
+ clearFocusIntent();
122
+ } else pendingReference = pop.reference;
123
+ pop.setOpen(next, event);
124
+ };
125
+ const effectiveReference = () => (inPopup(fieldReference) ? null : fieldReference) ?? pop.trigger ?? (inPopup(input) ? null : input);
126
+ const syncReference = () => {
127
+ const next = effectiveReference();
128
+ if (pop.reference === next) return;
129
+ clearFocusIntent();
130
+ pop.setReference(next);
131
+ };
132
+ const setReference = (element, previous) => {
133
+ if (!element && previous && fieldReference !== previous) return;
134
+ fieldReference = element;
135
+ syncReference();
136
+ };
137
+ const setInput = (element, previous) => {
138
+ if (!element && previous && input !== previous) return;
139
+ input = element;
140
+ const nextInputId = element?.id || fallbackInputId;
141
+ if (nextInputId !== inputId) {
142
+ inputId = nextInputId;
143
+ queueMicrotask(() => this.next());
144
+ }
145
+ syncReference();
146
+ };
147
+ const setTrigger = (element) => {
148
+ const had = Boolean(pop.trigger);
149
+ pop.setTrigger(element);
150
+ if (Boolean(element) !== had) queueMicrotask(() => this.next());
151
+ syncReference();
152
+ };
153
+ const setContent = (element) => {
154
+ pop.setContent(element);
155
+ syncReference();
156
+ };
157
+ const setSearch = (next, event) => {
158
+ searchState.set(next, event);
159
+ announceResults = true;
160
+ if (!pop.open) setOpen(true, event);
161
+ };
162
+ const setActive = (key) => {
163
+ if (key === activeKey) return;
164
+ if (!input) {
165
+ const target = selectItems.items(this).find((item) => item.dataset.value === key);
166
+ if (target && target !== ownerDocument?.activeElement) selectItems.focusItem(this, target);
167
+ }
168
+ this.next(() => activeKey = key);
169
+ };
170
+ const nav = roving(this, {
171
+ items: () => selectItems.items(this),
172
+ loop: () => false,
173
+ current: () => selectItems.items(this).find((item) => item.dataset.value === activeKey),
174
+ onMove: (target) => {
175
+ activeKey = target.dataset.value ?? "";
176
+ selectItems.highlight(this, target);
177
+ this.next();
178
+ queueMicrotask(() => target.scrollIntoView({ block: "nearest" }));
179
+ }
180
+ });
181
+ const focusNav = roving(this, {
182
+ items: () => selectItems.items(this),
183
+ onMove: (target, event) => {
184
+ selectItems.focusItem(this, target);
185
+ }
186
+ });
187
+ const ta = typeahead(this, {
188
+ items: () => selectItems.items(this).filter((item) => item.dataset.value !== CREATE_KEY),
189
+ onMatch: (target, event) => {
190
+ if (pop.open) selectItems.focusItem(this, target);
191
+ else {
192
+ target.click();
193
+ event.preventDefault();
194
+ }
195
+ }
196
+ });
197
+ let selectedKeySet = /* @__PURE__ */ new Set();
198
+ const chipsOf = () => Array.from(this.querySelectorAll("[data-slot=\"select-chip\"]"));
199
+ const rtl = () => getComputedStyle(this).direction === "rtl";
200
+ listen(this, "keydown", (event) => {
201
+ const target = event.target;
202
+ if (!target?.closest("[data-slot^=\"select\"]")) return;
203
+ if (target.dataset.slot === "select-chip") {
204
+ const chips = chipsOf();
205
+ const index = chips.indexOf(target);
206
+ const forward = rtl() ? "ArrowLeft" : "ArrowRight";
207
+ const backward = rtl() ? "ArrowRight" : "ArrowLeft";
208
+ if (event.key === "Escape") {
209
+ if (!pop.open) return;
210
+ event.preventDefault();
211
+ setOpen(false, event, input);
212
+ return;
213
+ }
214
+ if (event.key === backward) {
215
+ event.preventDefault();
216
+ if (index > 0) chips[index - 1].focus();
217
+ else input?.focus();
218
+ return;
219
+ }
220
+ if (event.key === forward) {
221
+ event.preventDefault();
222
+ if (index < chips.length - 1) chips[index + 1].focus();
223
+ else input?.focus();
224
+ return;
225
+ }
226
+ if (event.key === "Backspace" || event.key === "Delete") {
227
+ event.preventDefault();
228
+ const neighbor = chips[index + 1] ?? chips[index - 1];
229
+ const key = target.dataset.value ?? "";
230
+ const item = array(valueState.value).find((candidate) => itemKeyOf(candidate) === key);
231
+ if (item !== void 0) removeItem(item, event);
232
+ if (neighbor && neighbor !== target) neighbor.focus();
233
+ else input?.focus();
234
+ return;
235
+ }
236
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
237
+ event.preventDefault();
238
+ input?.focus();
239
+ setOpen(true, event);
240
+ return;
241
+ }
242
+ if (event.key.length === 1 || event.key === "Enter") input?.focus();
243
+ return;
244
+ }
245
+ if (target.closest("[data-slot=\"select-trigger\"]")) {
246
+ if (event.key === "Escape") {
247
+ if (!pop.open) return;
248
+ event.preventDefault();
249
+ setOpen(false, event, pop.trigger);
250
+ return;
251
+ }
252
+ if (event.key === "Tab") {
253
+ if (pop.open) setOpen(false, event);
254
+ return;
255
+ }
256
+ if (event.key === "ArrowDown" || event.key === "ArrowUp" || event.key === "Enter" || event.key === " ") {
257
+ event.preventDefault();
258
+ if (pop.open) {
259
+ if (event.key === "Enter" || event.key === " ") {
260
+ setOpen(false, event);
261
+ return;
262
+ }
263
+ if (inPopup(input)) {
264
+ input?.focus();
265
+ return;
266
+ }
267
+ const items = selectItems.items(this);
268
+ let target = items.find((item) => selectedKeySet.has(item.dataset.value ?? "")) ?? items[0];
269
+ if (target) target = items[items.indexOf(target) + (event.key === "ArrowDown" ? 1 : -1)] ?? target;
270
+ selectItems.focusItem(this, target);
271
+ return;
272
+ }
273
+ pendingFocus = true;
274
+ pendingStep = event.key === "ArrowDown" ? 1 : event.key === "ArrowUp" ? -1 : 0;
275
+ setOpen(true, event);
276
+ return;
277
+ }
278
+ if (inPopup(input)) {
279
+ if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey && event.key !== " ") {
280
+ event.preventDefault();
281
+ pendingFocus = true;
282
+ pendingSeed = event.key;
283
+ setOpen(true, event);
284
+ }
285
+ return;
286
+ }
287
+ if (!input) ta.handle(event);
288
+ return;
289
+ }
290
+ if (target === input || target.dataset.slot === "select-chips-input" || target.dataset.slot === "select-input") {
291
+ if (event.key === "Tab") {
292
+ if (pop.open) setOpen(false, event);
293
+ return;
294
+ }
295
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
296
+ if (!pop.open) setOpen(true, event);
297
+ if (!selectItems.items(this).some((item) => item.dataset.value === activeKey)) {
298
+ event.preventDefault();
299
+ nav.move(event.key === "ArrowDown" ? "first" : "last", event);
300
+ return;
301
+ }
302
+ nav.handle(event);
303
+ return;
304
+ }
305
+ if ((event.key === "Home" || event.key === "End") && pop.open) {
306
+ nav.handle(event);
307
+ return;
308
+ }
309
+ if (event.key === "Enter" && pop.open && activeKey) {
310
+ const item = selectItems.items(this).find((candidate) => candidate.dataset.value === activeKey);
311
+ if (!item) return;
312
+ event.preventDefault();
313
+ item.click();
314
+ return;
315
+ }
316
+ if (event.key === "Escape") {
317
+ if (!pop.open) return;
318
+ event.preventDefault();
319
+ setOpen(false, event, inPopup(target) ? pop.trigger : null);
320
+ return;
321
+ }
322
+ if (event.key === "Backspace" && target.dataset.slot === "select-chips-input" && !target.value) {
323
+ const selected = array(valueState.value);
324
+ if (selected.length) {
325
+ event.preventDefault();
326
+ removeItem(selected[selected.length - 1], event);
327
+ }
328
+ return;
329
+ }
330
+ const backward = rtl() ? "ArrowRight" : "ArrowLeft";
331
+ if (event.key === backward && target.dataset.slot === "select-chips-input" && target.selectionStart === 0) {
332
+ const chips = chipsOf();
333
+ if (chips.length) {
334
+ event.preventDefault();
335
+ chips[chips.length - 1].focus();
336
+ }
337
+ }
338
+ return;
339
+ }
340
+ if (target.closest("[data-slot=\"select-content\"]")) {
341
+ if (event.key === "Tab") {
342
+ setOpen(false, event);
343
+ return;
344
+ }
345
+ if (event.key === "Escape") {
346
+ event.preventDefault();
347
+ setOpen(false, event, pop.trigger);
348
+ return;
349
+ }
350
+ if (focusNav.handle(event)) return;
351
+ if (event.key === "Enter" || event.key === " ") {
352
+ const item = selectItems.item(event);
353
+ if (!item) return;
354
+ event.preventDefault();
355
+ item.click();
356
+ return;
357
+ }
358
+ if (ta.handle(event)) event.preventDefault();
359
+ }
360
+ });
361
+ let itemKeyOf = (item) => fallbackString(item);
362
+ let removeItem = (_item, _event) => {};
363
+ for (const args of this) {
364
+ disabled = Boolean(args.disabled);
365
+ onCreate = args.onCreate;
366
+ onInputValueChange = args.onInputValueChange;
367
+ onOpenChange = args.onOpenChange;
368
+ onValueChange = args.onValueChange;
369
+ const wasOpen = pop.open;
370
+ const opened = pop.sync(args.open == null ? void 0 : Boolean(args.open), {
371
+ placement: args.placement,
372
+ gap: args.gap
373
+ });
374
+ if (wasOpen && !opened) finishClose();
375
+ else if (!opened) clearFocusIntent();
376
+ else closeFocus = null;
377
+ searchState.sync(args.inputValue === void 0 ? void 0 : String(args.inputValue));
378
+ valueState.sync(args.value);
379
+ const multiple = Boolean(args.multiple);
380
+ const stringValue = (item) => args.itemToStringValue?.(item) ?? fallbackString(item);
381
+ const itemKey = (item) => stringValue(item);
382
+ itemKeyOf = itemKey;
383
+ const rawValue = valueState.value;
384
+ const selectedItems = multiple ? array(rawValue) : rawValue == null || rawValue === "" ? [] : [rawValue];
385
+ const selectedKeys = new Set(selectedItems.map(itemKey));
386
+ selectedKeySet = selectedKeys;
387
+ const search = searchState.value;
388
+ const filter = resolveFilter(args.filter, defaultFilter);
389
+ const filtering = Boolean(input) && args.filter !== null;
390
+ const filteredItems = filtering ? (args.items ?? []).filter((item) => filter(item, search, stringValue(item))) : args.items ?? [];
391
+ const matches = (item, keywords = []) => filtering ? filter(item, search, [stringValue(item), ...keywords].join(" ")) : true;
392
+ const label = (item) => {
393
+ const key = itemKey(item);
394
+ return labels.get(key) ?? stringValue(item);
395
+ };
396
+ const selectedLabels = selectedItems.map(label);
397
+ const query = search.trim().toLowerCase();
398
+ const createVisible = Boolean(onCreate) && Boolean(query) && ![...labels.values(), ...(args.items ?? []).map(stringValue)].some((candidate) => candidate.trim().toLowerCase() === query);
399
+ if (args.autoHighlight && pop.open && filteredItems.length && !filteredItems.some((item) => itemKey(item) === activeKey)) activeKey = itemKey(filteredItems[0]);
400
+ const commitValue = (next, event) => {
401
+ valueState.set(next, event);
402
+ };
403
+ const select = (item, event) => {
404
+ if (disabled) return;
405
+ const key = itemKey(item);
406
+ if (multiple) {
407
+ const current = array(valueState.value);
408
+ const exists = current.some((candidate) => itemKey(candidate) === key);
409
+ const next = exists ? current.filter((candidate) => itemKey(candidate) !== key) : [...current, item];
410
+ commitValue(next, event);
411
+ live.polite(`${label(item)} ${exists ? "deselected" : "selected"}, ${next.length} selected`);
412
+ if (!searchState.controlled) searchState.init("");
413
+ this.next();
414
+ return;
415
+ }
416
+ if (selectedKeys.has(key) && !args.required) {
417
+ commitValue(null, event);
418
+ live.polite(`${label(item)} deselected`);
419
+ } else commitValue(item, event);
420
+ if (!searchState.controlled) searchState.init("");
421
+ setOpen(false, event);
422
+ };
423
+ const remove = (item, event) => {
424
+ if (disabled) return;
425
+ const key = itemKey(item);
426
+ const next = array(valueState.value).filter((candidate) => itemKey(candidate) !== key);
427
+ commitValue(multiple ? next : null, event);
428
+ live.polite(`${label(item)} removed, ${next.length} selected`);
429
+ this.next();
430
+ };
431
+ removeItem = remove;
432
+ const clear = (event) => {
433
+ commitValue(multiple ? [] : null, event);
434
+ if (!searchState.controlled) searchState.init("");
435
+ this.next();
436
+ input?.focus();
437
+ };
438
+ const create = (event) => {
439
+ if (disabled) return;
440
+ const value = search.trim();
441
+ if (!value) return;
442
+ onCreate?.(value, event);
443
+ if (!searchState.controlled) searchState.init("");
444
+ this.next();
445
+ };
446
+ SelectContext({
447
+ activeId: activeKey ? itemDomId(activeKey) : "",
448
+ activeKey,
449
+ clear,
450
+ contentId: pop.contentId,
451
+ contentStyle: pop.contentStyle,
452
+ create,
453
+ createVisible,
454
+ disabled,
455
+ filteredItems,
456
+ hasTrigger: Boolean(pop.trigger),
457
+ inputId,
458
+ itemId: itemDomId,
459
+ listId: `${selectId}-list`,
460
+ matches,
461
+ multiple,
462
+ open: pop.open,
463
+ searchInPopup: inPopup(input),
464
+ registerLabel: (key, itemLabel) => {
465
+ if (labels.get(key) === itemLabel) return;
466
+ labels.set(key, itemLabel);
467
+ if (selectedKeys.has(key)) queueMicrotask(() => this.next());
468
+ },
469
+ remove,
470
+ required: Boolean(args.required),
471
+ search,
472
+ selectedKeys,
473
+ selectedLabels,
474
+ select,
475
+ setActive,
476
+ setReference,
477
+ setContent,
478
+ setInput,
479
+ setOpen,
480
+ setSearch,
481
+ setTrigger,
482
+ stringValue,
483
+ triggerId: pop.triggerId
484
+ });
485
+ if (dom(this)) queueMicrotask(() => {
486
+ const visible = selectItems.sweep(this);
487
+ pop.content?.toggleAttribute("data-empty", visible.length === 0);
488
+ if (input && !search) for (const empty of this.querySelectorAll("[data-slot=\"select-empty\"]")) empty.hidden = true;
489
+ if (activeKey && !visible.some((item) => item.dataset.value === activeKey)) this.next(() => activeKey = "");
490
+ const mounted = new Set(selectItems.all(this).map((item) => item.dataset.value ?? ""));
491
+ for (const key of labels.keys()) if (!mounted.has(key) && !selectedKeySet.has(key)) labels.delete(key);
492
+ const status = this.querySelector("[data-slot=\"select-status\"]");
493
+ if (pop.open && announceResults && !status && visible.length !== lastResultCount) live.polite((args.resultsLabel ?? defaultResultsLabel)(visible.length));
494
+ lastResultCount = pop.open ? visible.length : -1;
495
+ announceResults = false;
496
+ });
497
+ yield /* @__PURE__ */ jsxs(Fragment, { children: [
498
+ args.name && !multiple ? /* @__PURE__ */ jsx("input", {
499
+ disabled,
500
+ name: args.name,
501
+ "set:value": selectedItems.length ? itemKey(selectedItems[0]) : "",
502
+ type: "hidden",
503
+ value: selectedItems.length ? itemKey(selectedItems[0]) : ""
504
+ }) : null,
505
+ args.name && multiple ? selectedItems.map((item) => /* @__PURE__ */ jsx("input", {
506
+ disabled,
507
+ name: args.name,
508
+ "set:value": itemKey(item),
509
+ type: "hidden",
510
+ value: itemKey(item)
511
+ }, itemKey(item))) : null,
512
+ args.children
513
+ ] });
514
+ }
515
+ };
516
+ /** Unified select: single, multiple, searchable, editable, chips, and tagging by composition. */
517
+ var Select = ({ autoHighlight, children, class: classes, defaultInputValue, defaultOpen, defaultValue, disabled, filter, gap, inputValue, itemToStringValue, items, multiple, name, onCreate, onInputValueChange, onOpenChange, onValueChange, open, placement, required, resultsLabel, value, ...attrs }) => /* @__PURE__ */ jsx(SelectRoot, {
518
+ ...statefulRootAttrs(attrs),
519
+ autoHighlight,
520
+ defaultInputValue,
521
+ defaultOpen,
522
+ defaultValue,
523
+ disabled,
524
+ filter,
525
+ gap,
526
+ inputValue,
527
+ itemToStringValue,
528
+ items,
529
+ multiple,
530
+ name,
531
+ onCreate,
532
+ onInputValueChange,
533
+ onOpenChange,
534
+ onValueChange,
535
+ open,
536
+ placement,
537
+ required,
538
+ resultsLabel,
539
+ value,
540
+ "attr:class": classes,
541
+ "attr:data-slot": "select",
542
+ children
543
+ });
544
+ /** Button field for a Select; the closed-state combobox. */
545
+ var SelectTrigger = ({ children, class: classes, disabled, iconClass, id: idArg, ref, size = "default", type = "button", "set:onclick": onClick, ...attrs }) => {
546
+ const select = SelectContext();
547
+ const disabledFlag = Boolean(disabled ?? select?.disabled);
548
+ const empty = !select?.selectedKeys.size;
549
+ return /* @__PURE__ */ jsxs("button", {
550
+ ...attrs,
551
+ ...triggerAttrs({
552
+ controls: select?.listId,
553
+ expanded: Boolean(select?.open),
554
+ haspopup: "listbox",
555
+ id: idArg,
556
+ open: Boolean(select?.open),
557
+ ref,
558
+ setTrigger: select?.setTrigger,
559
+ triggerId: select?.triggerId
560
+ }),
561
+ "aria-required": flag(select?.required),
562
+ class: classes,
563
+ "data-placeholder": flag(empty),
564
+ "data-size": size,
565
+ "data-slot": "select-trigger",
566
+ disabled: disabledFlag,
567
+ role: "combobox",
568
+ "set:onclick": (event) => {
569
+ callHandler(onClick, event);
570
+ if (event.defaultPrevented) return;
571
+ select?.setOpen(!select.open, event);
572
+ },
573
+ type,
574
+ children: [children, /* @__PURE__ */ jsx("span", {
575
+ "aria-hidden": "true",
576
+ class: iconClass,
577
+ "data-slot": "select-icon"
578
+ })]
579
+ });
580
+ };
581
+ /** Selected value text for a SelectTrigger. */
582
+ var SelectValue = ({ children, class: classes, placeholder, ...attrs }) => {
583
+ const selected = SelectContext()?.selectedLabels ?? [];
584
+ const empty = !selected.length;
585
+ const content = children ?? (empty ? placeholder : selected.join(", "));
586
+ return /* @__PURE__ */ jsx("span", {
587
+ ...attrs,
588
+ class: classes,
589
+ "data-placeholder": flag(empty),
590
+ "data-slot": "select-value",
591
+ children: content
592
+ });
593
+ };
594
+ /** Input field or in-popup search box for a Select. */
595
+ var SelectInput = ({ children, addonClass, buttonClass, buttonIconClass, class: classes, clearButtonClass, clearIconClass, clearLabel = "Clear selection", disabled, id: idArg, inputClass, onValueChange, placeholder, ref, showClear, showTrigger = true, triggerLabel = "Show options", value, "set:oninput": onInput, ...attrs }) => {
596
+ const select = SelectContext();
597
+ const disabledFlag = Boolean(disabled ?? select?.disabled);
598
+ const clearable = Boolean(select && (select.selectedKeys.size > 0 || select.search));
599
+ const searchbox = Boolean(select?.searchInPopup);
600
+ const shown = value == null ? select?.open || select?.search ? select?.search ?? "" : select?.multiple ? "" : select?.selectedLabels[0] ?? "" : String(value);
601
+ let mounted = null;
602
+ let group = null;
603
+ const reference = (element) => {
604
+ const previous = mounted;
605
+ mounted = element;
606
+ select?.setInput(element, previous);
607
+ callRef(ref, element);
608
+ };
609
+ return /* @__PURE__ */ jsxs(InputGroup, {
610
+ class: classes,
611
+ disabled: disabledFlag,
612
+ ref: (element) => {
613
+ const previous = group;
614
+ group = element;
615
+ if (!element || !element.closest("[data-slot=\"select-content\"]")) select?.setReference(element, previous);
616
+ },
617
+ children: [
618
+ /* @__PURE__ */ jsx(InputGroupInput, {
619
+ ...attrs,
620
+ "aria-activedescendant": select?.activeId || void 0,
621
+ "aria-autocomplete": "list",
622
+ "aria-controls": select?.listId,
623
+ "aria-expanded": searchbox ? void 0 : select?.open ? "true" : "false",
624
+ "aria-haspopup": searchbox ? void 0 : "listbox",
625
+ "aria-required": flag(select?.required),
626
+ class: inputClass,
627
+ "data-slot": "select-input",
628
+ disabled: disabledFlag,
629
+ id: idArg ?? select?.inputId,
630
+ placeholder,
631
+ ref: reference,
632
+ role: searchbox ? "searchbox" : "combobox",
633
+ "set:onclick": (event) => select?.setOpen(true, event),
634
+ "set:onfocus": (event) => select?.setOpen(true, event),
635
+ "set:oninput": (event) => {
636
+ const next = event.target.value;
637
+ callHandler(onInput, event);
638
+ if (event.defaultPrevented) return;
639
+ onValueChange?.(next, event);
640
+ select?.setSearch(next, event);
641
+ },
642
+ "set:value": shown,
643
+ type: "text"
644
+ }),
645
+ children,
646
+ /* @__PURE__ */ jsxs(InputGroupAddon, {
647
+ align: "inline-end",
648
+ class: addonClass,
649
+ children: [showTrigger && !(showClear && clearable) ? /* @__PURE__ */ jsx(InputGroupButton, {
650
+ "aria-controls": select?.listId,
651
+ "aria-expanded": select?.open ? "true" : "false",
652
+ "aria-label": triggerLabel,
653
+ class: buttonClass,
654
+ "data-slot": "select-input-trigger",
655
+ disabled: disabledFlag,
656
+ "set:onclick": (event) => select?.setOpen(!select?.open, event),
657
+ type: "button",
658
+ children: /* @__PURE__ */ jsx("span", {
659
+ "aria-hidden": "true",
660
+ class: buttonIconClass
661
+ })
662
+ }) : null, showClear && clearable ? /* @__PURE__ */ jsx(InputGroupButton, {
663
+ "aria-label": clearLabel,
664
+ class: clearButtonClass,
665
+ "data-slot": "select-clear",
666
+ disabled: disabledFlag,
667
+ "set:onclick": (event) => select?.clear(event),
668
+ type: "button",
669
+ children: /* @__PURE__ */ jsx("span", {
670
+ "aria-hidden": "true",
671
+ class: clearIconClass
672
+ })
673
+ }) : null]
674
+ })
675
+ ]
676
+ });
677
+ };
678
+ /** Button that clears the current selection and search. */
679
+ var SelectClear = ({ children, class: classes, disabled, iconClass, type = "button", "set:onclick": onClick, ...attrs }) => {
680
+ const select = SelectContext();
681
+ const disabledFlag = Boolean(disabled ?? select?.disabled);
682
+ if (select && !select.selectedKeys.size && !select.search) return null;
683
+ return /* @__PURE__ */ jsx("button", {
684
+ ...attrs,
685
+ "aria-label": attrs["aria-label"] ?? "Clear selection",
686
+ class: classes,
687
+ "data-slot": "select-clear",
688
+ disabled: disabledFlag,
689
+ "set:onclick": (event) => {
690
+ callHandler(onClick, event);
691
+ if (event.defaultPrevented) return;
692
+ select?.clear(event);
693
+ },
694
+ type,
695
+ children: children ?? /* @__PURE__ */ jsx("span", {
696
+ "aria-hidden": "true",
697
+ class: iconClass
698
+ })
699
+ });
700
+ };
701
+ /** Popup panel for Select options. */
702
+ var SelectContent = ({ children, class: classes, ref, style, ...attrs }) => {
703
+ const select = SelectContext();
704
+ return /* @__PURE__ */ jsx("div", {
705
+ ...attrs,
706
+ ...contentAttrs({
707
+ id: select?.contentId,
708
+ open: Boolean(select?.open),
709
+ ref,
710
+ setContent: select?.setContent,
711
+ style: select?.contentStyle(style),
712
+ tabindex: "-1"
713
+ }),
714
+ class: classes,
715
+ "data-slot": "select-content",
716
+ children
717
+ });
718
+ };
719
+ /** Listbox for Select options; required in every composition. */
720
+ var SelectList = ({ children, class: classes, ...attrs }) => {
721
+ const select = SelectContext();
722
+ const render = typeof children === "function" ? (select?.filteredItems ?? []).map((item, index) => children(item, index)) : children;
723
+ return /* @__PURE__ */ jsx("div", {
724
+ ...attrs,
725
+ "aria-labelledby": select?.hasTrigger ? select.triggerId : void 0,
726
+ "aria-multiselectable": select?.multiple ? "true" : void 0,
727
+ class: classes,
728
+ "data-slot": "select-list",
729
+ id: select?.listId,
730
+ role: "listbox",
731
+ children: render
732
+ });
733
+ };
734
+ /** Selectable Select option. */
735
+ var SelectItem = ({ children, class: classes, disabled, forceMount, indicatorClass, indicatorIconClass, keywords = [], onSelect, textValue, value, ...attrs }) => {
736
+ const select = SelectContext();
737
+ const item = value ?? text(children);
738
+ const key = select ? select.stringValue(item) : fallbackString(item);
739
+ const label = textValue ?? (typeof item === "string" ? text(children) : void 0);
740
+ const selected = Boolean(select?.selectedKeys.has(key));
741
+ const highlighted = select?.activeKey === key;
742
+ const disabledFlag = Boolean(disabled ?? select?.disabled);
743
+ const hidden = !forceMount && select ? !select.matches(item, keywords) : false;
744
+ if (label != null) select?.registerLabel(key, label);
745
+ return /* @__PURE__ */ jsxs("div", {
746
+ ...attrs,
747
+ ...selectItems.attrs({
748
+ disabled: disabledFlag,
749
+ label: label ?? key,
750
+ value: key
751
+ }),
752
+ "aria-disabled": flag(disabledFlag),
753
+ "aria-selected": selected ? "true" : "false",
754
+ class: classes,
755
+ "data-highlighted": flag(highlighted),
756
+ "data-selected": flag(selected),
757
+ "data-slot": "select-item",
758
+ "data-state": selected ? "checked" : "unchecked",
759
+ hidden: hidden || void 0,
760
+ id: select?.itemId(key),
761
+ role: "option",
762
+ "set:onclick": (event) => {
763
+ if (disabledFlag) return;
764
+ onSelect?.(item, event);
765
+ if (event.defaultPrevented) return;
766
+ select?.select(item, event);
767
+ },
768
+ "set:onfocus": () => select?.setActive(key),
769
+ "set:onpointermove": () => {
770
+ if (!disabledFlag) select?.setActive(key);
771
+ },
772
+ tabindex: disabledFlag ? void 0 : "-1",
773
+ children: [children, /* @__PURE__ */ jsx("span", {
774
+ "aria-hidden": "true",
775
+ class: indicatorClass,
776
+ "data-selected": selected ? "true" : "false",
777
+ "data-slot": "select-item-indicator",
778
+ children: /* @__PURE__ */ jsx("span", { class: indicatorIconClass })
779
+ })]
780
+ });
781
+ };
782
+ /** Group wrapper for Select options. */
783
+ var SelectGroup = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("div", {
784
+ ...attrs,
785
+ class: classes,
786
+ "data-slot": "select-group",
787
+ role: "group",
788
+ children
789
+ });
790
+ /** Label for a SelectGroup. */
791
+ var SelectLabel = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("div", {
792
+ ...attrs,
793
+ class: classes,
794
+ "data-slot": "select-label",
795
+ children
796
+ });
797
+ /** Visual separator between Select groups. */
798
+ var SelectSeparator = ({ class: classes, ...attrs }) => /* @__PURE__ */ jsx("div", {
799
+ ...attrs,
800
+ class: classes,
801
+ "data-slot": "select-separator",
802
+ role: "separator"
803
+ });
804
+ /** Empty state shown when filtering hides every option. */
805
+ var SelectEmpty = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("div", {
806
+ ...attrs,
807
+ class: classes,
808
+ "data-slot": "select-empty",
809
+ children
810
+ });
811
+ /** Keep-mounted polite live region for async status; children swap. */
812
+ var SelectStatus = ({ children, class: classes, ...attrs }) => /* @__PURE__ */ jsx("div", {
813
+ ...attrs,
814
+ "aria-live": "polite",
815
+ class: classes,
816
+ "data-slot": "select-status",
817
+ role: "status",
818
+ children
819
+ });
820
+ /** Create-tag row: visible while the search matches no option label exactly. */
821
+ var SelectCreate = ({ children, class: classes, ...attrs }) => {
822
+ const select = SelectContext();
823
+ const highlighted = select?.activeKey === CREATE_KEY;
824
+ return /* @__PURE__ */ jsx("div", {
825
+ ...attrs,
826
+ ...selectItems.attrs({
827
+ label: select?.search ?? "",
828
+ value: CREATE_KEY
829
+ }),
830
+ class: classes,
831
+ "data-highlighted": flag(highlighted),
832
+ "data-slot": "select-create",
833
+ hidden: select?.createVisible ? void 0 : true,
834
+ id: select?.itemId(CREATE_KEY),
835
+ role: "option",
836
+ "aria-selected": "false",
837
+ "set:onclick": (event) => select?.create(event),
838
+ "set:onfocus": () => select?.setActive(CREATE_KEY),
839
+ "set:onpointermove": () => select?.setActive(CREATE_KEY),
840
+ tabindex: "-1",
841
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
842
+ "Create «",
843
+ select?.search,
844
+ "»"
845
+ ] })
846
+ });
847
+ };
848
+ /** Chip input wrapper for multiple Select selections. */
849
+ var SelectChips = ({ children, class: classes, ref, ...attrs }) => {
850
+ const select = SelectContext();
851
+ let mounted = null;
852
+ const reference = (element) => {
853
+ const previous = mounted;
854
+ mounted = element;
855
+ select?.setReference(element, previous);
856
+ callRef(ref, element);
857
+ };
858
+ return /* @__PURE__ */ jsx("div", {
859
+ ...attrs,
860
+ class: classes,
861
+ "data-slot": "select-chips",
862
+ ref: reference,
863
+ children
864
+ });
865
+ };
866
+ /** Selected chip for multiple Select usage. */
867
+ var SelectChip = ({ children, class: classes, removeClass, removeLabel, removeIconClass, showRemove = true, value, ...attrs }) => {
868
+ const select = SelectContext();
869
+ const item = value ?? text(children);
870
+ const key = select ? select.stringValue(item) : fallbackString(item);
871
+ return /* @__PURE__ */ jsxs("span", {
872
+ ...attrs,
873
+ class: classes,
874
+ "data-slot": "select-chip",
875
+ "data-value": key,
876
+ tabindex: "-1",
877
+ children: [children, showRemove ? /* @__PURE__ */ jsx("button", {
878
+ "aria-label": removeLabel ?? `Remove ${key}`,
879
+ class: removeClass,
880
+ "data-slot": "select-chip-remove",
881
+ disabled: select?.disabled,
882
+ tabindex: "-1",
883
+ type: "button",
884
+ "set:onclick": (event) => {
885
+ event.stopPropagation();
886
+ select?.remove(item, event);
887
+ },
888
+ children: /* @__PURE__ */ jsx("span", {
889
+ "aria-hidden": "true",
890
+ class: removeIconClass
891
+ })
892
+ }) : null]
893
+ });
894
+ };
895
+ /** Input used inside SelectChips. */
896
+ var SelectChipsInput = ({ class: classes, disabled, onValueChange, placeholder, ref, value, "set:oninput": onInput, ...attrs }) => {
897
+ const select = SelectContext();
898
+ const disabledFlag = Boolean(disabled ?? select?.disabled);
899
+ let mounted = null;
900
+ const reference = (element) => {
901
+ const previous = mounted;
902
+ mounted = element;
903
+ select?.setInput(element, previous);
904
+ callRef(ref, element);
905
+ };
906
+ return /* @__PURE__ */ jsx("input", {
907
+ ...attrs,
908
+ "aria-activedescendant": select?.activeId || void 0,
909
+ "aria-autocomplete": "list",
910
+ "aria-controls": select?.listId,
911
+ "aria-expanded": select?.open ? "true" : "false",
912
+ "aria-haspopup": "listbox",
913
+ "aria-required": flag(select?.required),
914
+ class: classes,
915
+ "data-slot": "select-chips-input",
916
+ disabled: disabledFlag,
917
+ placeholder,
918
+ ref: reference,
919
+ role: "combobox",
920
+ "set:onclick": (event) => select?.setOpen(true, event),
921
+ "set:onfocus": (event) => select?.setOpen(true, event),
922
+ "set:oninput": (event) => {
923
+ const next = event.target.value;
924
+ callHandler(onInput, event);
925
+ if (event.defaultPrevented) return;
926
+ onValueChange?.(next, event);
927
+ select?.setSearch(next, event);
928
+ },
929
+ "set:value": value == null ? select?.search ?? "" : String(value),
930
+ type: "text"
931
+ });
932
+ };
933
+ var scrollList = (event, top) => {
934
+ event.currentTarget.closest("[data-slot=\"select-content\"]")?.querySelector("[data-slot=\"select-list\"]")?.scrollBy({ top });
935
+ };
936
+ /** Scroll button for long Select lists. */
937
+ var SelectScrollUpButton = ({ children, class: classes, iconClass, type = "button", ...attrs }) => /* @__PURE__ */ jsx("button", {
938
+ ...attrs,
939
+ class: classes,
940
+ "data-slot": "select-scroll-up-button",
941
+ "set:onclick": (event) => scrollList(event, -96),
942
+ type,
943
+ children: children ?? /* @__PURE__ */ jsx("span", {
944
+ "aria-hidden": "true",
945
+ class: iconClass
946
+ })
947
+ });
948
+ /** Scroll button for long Select lists. */
949
+ var SelectScrollDownButton = ({ children, class: classes, iconClass, type = "button", ...attrs }) => /* @__PURE__ */ jsx("button", {
950
+ ...attrs,
951
+ class: classes,
952
+ "data-slot": "select-scroll-down-button",
953
+ "set:onclick": (event) => scrollList(event, 96),
954
+ type,
955
+ children: children ?? /* @__PURE__ */ jsx("span", {
956
+ "aria-hidden": "true",
957
+ class: iconClass
958
+ })
959
+ });
960
+ //#endregion
961
+ export { Select, SelectChip, SelectChips, SelectChipsInput, SelectClear, SelectContent, SelectCreate, SelectEmpty, SelectGroup, SelectInput, SelectItem, SelectLabel, SelectList, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectStatus, SelectTrigger, SelectValue };