@zag-js/combobox 1.34.1 → 1.35.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.
package/dist/index.mjs CHANGED
@@ -1,1577 +1,12 @@
1
- import { createAnatomy } from '@zag-js/anatomy';
2
- import { ListCollection } from '@zag-js/collection';
3
- import { raf, setCaretToEnd, nextTick, scrollIntoView, observeAttributes, clickIfLink, query, dataAttr, isDownloadingEvent, isOpeningInNewTab, isContextMenuEvent, ariaAttr, isLeftClick, getEventKey, isComposingEvent, isAnchorElement } from '@zag-js/dom-query';
4
- import { getPlacement, getPlacementStyles } from '@zag-js/popper';
5
- import { match, remove, addOrRemove, isBoolean, isEqual, createSplitProps, ensure } from '@zag-js/utils';
6
- import { setup } from '@zag-js/core';
7
- import { trackDismissableElement } from '@zag-js/dismissable';
8
- import { setInteractionModality, trackFocusVisible, getInteractionModality } from '@zag-js/focus-visible';
9
- import { createProps } from '@zag-js/types';
10
-
11
- // src/combobox.anatomy.ts
12
- var anatomy = createAnatomy("combobox").parts(
13
- "root",
14
- "clearTrigger",
15
- "content",
16
- "control",
17
- "input",
18
- "item",
19
- "itemGroup",
20
- "itemGroupLabel",
21
- "itemIndicator",
22
- "itemText",
23
- "label",
24
- "list",
25
- "positioner",
26
- "trigger"
27
- );
28
- var parts = anatomy.build();
29
- var collection = (options) => {
30
- return new ListCollection(options);
1
+ // src/index.ts
2
+ import { anatomy } from "./combobox.anatomy.mjs";
3
+ import { collection } from "./combobox.collection.mjs";
4
+ import { connect } from "./combobox.connect.mjs";
5
+ import { machine } from "./combobox.machine.mjs";
6
+ export * from "./combobox.props.mjs";
7
+ export {
8
+ anatomy,
9
+ collection,
10
+ connect,
11
+ machine
31
12
  };
32
- collection.empty = () => {
33
- return new ListCollection({ items: [] });
34
- };
35
- var getRootId = (ctx) => ctx.ids?.root ?? `combobox:${ctx.id}`;
36
- var getLabelId = (ctx) => ctx.ids?.label ?? `combobox:${ctx.id}:label`;
37
- var getControlId = (ctx) => ctx.ids?.control ?? `combobox:${ctx.id}:control`;
38
- var getInputId = (ctx) => ctx.ids?.input ?? `combobox:${ctx.id}:input`;
39
- var getContentId = (ctx) => ctx.ids?.content ?? `combobox:${ctx.id}:content`;
40
- var getPositionerId = (ctx) => ctx.ids?.positioner ?? `combobox:${ctx.id}:popper`;
41
- var getTriggerId = (ctx) => ctx.ids?.trigger ?? `combobox:${ctx.id}:toggle-btn`;
42
- var getClearTriggerId = (ctx) => ctx.ids?.clearTrigger ?? `combobox:${ctx.id}:clear-btn`;
43
- var getItemGroupId = (ctx, id) => ctx.ids?.itemGroup?.(id) ?? `combobox:${ctx.id}:optgroup:${id}`;
44
- var getItemGroupLabelId = (ctx, id) => ctx.ids?.itemGroupLabel?.(id) ?? `combobox:${ctx.id}:optgroup-label:${id}`;
45
- var getItemId = (ctx, id) => ctx.ids?.item?.(id) ?? `combobox:${ctx.id}:option:${id}`;
46
- var getContentEl = (ctx) => ctx.getById(getContentId(ctx));
47
- var getInputEl = (ctx) => ctx.getById(getInputId(ctx));
48
- var getPositionerEl = (ctx) => ctx.getById(getPositionerId(ctx));
49
- var getControlEl = (ctx) => ctx.getById(getControlId(ctx));
50
- var getTriggerEl = (ctx) => ctx.getById(getTriggerId(ctx));
51
- var getClearTriggerEl = (ctx) => ctx.getById(getClearTriggerId(ctx));
52
- var getItemEl = (ctx, value) => {
53
- if (value == null) return null;
54
- const selector = `[role=option][data-value="${CSS.escape(value)}"]`;
55
- return query(getContentEl(ctx), selector);
56
- };
57
- var focusInputEl = (ctx) => {
58
- const inputEl = getInputEl(ctx);
59
- if (!ctx.isActiveElement(inputEl)) {
60
- inputEl?.focus({ preventScroll: true });
61
- }
62
- setCaretToEnd(inputEl);
63
- };
64
- var focusTriggerEl = (ctx) => {
65
- const triggerEl = getTriggerEl(ctx);
66
- if (ctx.isActiveElement(triggerEl)) return;
67
- triggerEl?.focus({ preventScroll: true });
68
- };
69
-
70
- // src/combobox.connect.ts
71
- function connect(service, normalize) {
72
- const { context, prop, state, send, scope, computed, event } = service;
73
- const translations = prop("translations");
74
- const collection2 = prop("collection");
75
- const disabled = !!prop("disabled");
76
- const interactive = computed("isInteractive");
77
- const invalid = !!prop("invalid");
78
- const required = !!prop("required");
79
- const readOnly = !!prop("readOnly");
80
- const open = state.hasTag("open");
81
- const focused = state.hasTag("focused");
82
- const composite = prop("composite");
83
- const highlightedValue = context.get("highlightedValue");
84
- const popperStyles = getPlacementStyles({
85
- ...prop("positioning"),
86
- placement: context.get("currentPlacement")
87
- });
88
- function getItemState(props2) {
89
- const itemDisabled = collection2.getItemDisabled(props2.item);
90
- const value = collection2.getItemValue(props2.item);
91
- ensure(value, () => `[zag-js] No value found for item ${JSON.stringify(props2.item)}`);
92
- return {
93
- value,
94
- disabled: Boolean(disabled || itemDisabled),
95
- highlighted: highlightedValue === value,
96
- selected: context.get("value").includes(value)
97
- };
98
- }
99
- return {
100
- focused,
101
- open,
102
- inputValue: context.get("inputValue"),
103
- highlightedValue,
104
- highlightedItem: context.get("highlightedItem"),
105
- value: context.get("value"),
106
- valueAsString: computed("valueAsString"),
107
- hasSelectedItems: computed("hasSelectedItems"),
108
- selectedItems: context.get("selectedItems"),
109
- collection: prop("collection"),
110
- multiple: !!prop("multiple"),
111
- disabled: !!disabled,
112
- syncSelectedItems() {
113
- send({ type: "SELECTED_ITEMS.SYNC" });
114
- },
115
- reposition(options = {}) {
116
- send({ type: "POSITIONING.SET", options });
117
- },
118
- setHighlightValue(value) {
119
- send({ type: "HIGHLIGHTED_VALUE.SET", value });
120
- },
121
- clearHighlightValue() {
122
- send({ type: "HIGHLIGHTED_VALUE.CLEAR" });
123
- },
124
- selectValue(value) {
125
- send({ type: "ITEM.SELECT", value });
126
- },
127
- setValue(value) {
128
- send({ type: "VALUE.SET", value });
129
- },
130
- setInputValue(value, reason = "script") {
131
- send({ type: "INPUT_VALUE.SET", value, src: reason });
132
- },
133
- clearValue(value) {
134
- if (value != null) {
135
- send({ type: "ITEM.CLEAR", value });
136
- } else {
137
- send({ type: "VALUE.CLEAR" });
138
- }
139
- },
140
- focus() {
141
- getInputEl(scope)?.focus();
142
- },
143
- setOpen(nextOpen, reason = "script") {
144
- const open2 = state.hasTag("open");
145
- if (open2 === nextOpen) return;
146
- send({ type: nextOpen ? "OPEN" : "CLOSE", src: reason });
147
- },
148
- getRootProps() {
149
- return normalize.element({
150
- ...parts.root.attrs,
151
- dir: prop("dir"),
152
- id: getRootId(scope),
153
- "data-invalid": dataAttr(invalid),
154
- "data-readonly": dataAttr(readOnly)
155
- });
156
- },
157
- getLabelProps() {
158
- return normalize.label({
159
- ...parts.label.attrs,
160
- dir: prop("dir"),
161
- htmlFor: getInputId(scope),
162
- id: getLabelId(scope),
163
- "data-readonly": dataAttr(readOnly),
164
- "data-disabled": dataAttr(disabled),
165
- "data-invalid": dataAttr(invalid),
166
- "data-required": dataAttr(required),
167
- "data-focus": dataAttr(focused),
168
- onClick(event2) {
169
- if (composite) return;
170
- event2.preventDefault();
171
- getTriggerEl(scope)?.focus({ preventScroll: true });
172
- }
173
- });
174
- },
175
- getControlProps() {
176
- return normalize.element({
177
- ...parts.control.attrs,
178
- dir: prop("dir"),
179
- id: getControlId(scope),
180
- "data-state": open ? "open" : "closed",
181
- "data-focus": dataAttr(focused),
182
- "data-disabled": dataAttr(disabled),
183
- "data-invalid": dataAttr(invalid)
184
- });
185
- },
186
- getPositionerProps() {
187
- return normalize.element({
188
- ...parts.positioner.attrs,
189
- dir: prop("dir"),
190
- id: getPositionerId(scope),
191
- style: popperStyles.floating
192
- });
193
- },
194
- getInputProps() {
195
- return normalize.input({
196
- ...parts.input.attrs,
197
- dir: prop("dir"),
198
- "aria-invalid": ariaAttr(invalid),
199
- "data-invalid": dataAttr(invalid),
200
- "data-autofocus": dataAttr(prop("autoFocus")),
201
- name: prop("name"),
202
- form: prop("form"),
203
- disabled,
204
- required: prop("required"),
205
- autoComplete: "off",
206
- autoCorrect: "off",
207
- autoCapitalize: "none",
208
- spellCheck: "false",
209
- readOnly,
210
- placeholder: prop("placeholder"),
211
- id: getInputId(scope),
212
- type: "text",
213
- role: "combobox",
214
- defaultValue: context.get("inputValue"),
215
- "aria-autocomplete": computed("autoComplete") ? "both" : "list",
216
- "aria-controls": getContentId(scope),
217
- "aria-expanded": open,
218
- "data-state": open ? "open" : "closed",
219
- "aria-activedescendant": highlightedValue ? getItemId(scope, highlightedValue) : void 0,
220
- onClick(event2) {
221
- if (event2.defaultPrevented) return;
222
- if (!prop("openOnClick")) return;
223
- if (!interactive) return;
224
- send({ type: "INPUT.CLICK", src: "input-click" });
225
- },
226
- onFocus() {
227
- if (disabled) return;
228
- send({ type: "INPUT.FOCUS" });
229
- },
230
- onBlur() {
231
- if (disabled) return;
232
- send({ type: "INPUT.BLUR" });
233
- },
234
- onChange(event2) {
235
- send({ type: "INPUT.CHANGE", value: event2.currentTarget.value, src: "input-change" });
236
- },
237
- onKeyDown(event2) {
238
- if (event2.defaultPrevented) return;
239
- if (!interactive) return;
240
- if (event2.ctrlKey || event2.shiftKey || isComposingEvent(event2)) return;
241
- const openOnKeyPress = prop("openOnKeyPress");
242
- const isModifierKey = event2.ctrlKey || event2.metaKey || event2.shiftKey;
243
- const keypress = true;
244
- const keymap = {
245
- ArrowDown(event3) {
246
- if (!openOnKeyPress && !open) return;
247
- send({ type: event3.altKey ? "OPEN" : "INPUT.ARROW_DOWN", keypress, src: "arrow-key" });
248
- event3.preventDefault();
249
- },
250
- ArrowUp() {
251
- if (!openOnKeyPress && !open) return;
252
- send({ type: event2.altKey ? "CLOSE" : "INPUT.ARROW_UP", keypress, src: "arrow-key" });
253
- event2.preventDefault();
254
- },
255
- Home(event3) {
256
- if (isModifierKey) return;
257
- send({ type: "INPUT.HOME", keypress });
258
- if (open) {
259
- event3.preventDefault();
260
- }
261
- },
262
- End(event3) {
263
- if (isModifierKey) return;
264
- send({ type: "INPUT.END", keypress });
265
- if (open) {
266
- event3.preventDefault();
267
- }
268
- },
269
- Enter(event3) {
270
- send({ type: "INPUT.ENTER", keypress, src: "item-select" });
271
- const submittable = computed("isCustomValue") && prop("allowCustomValue");
272
- const hasHighlight = highlightedValue != null;
273
- const alwaysSubmit = prop("alwaysSubmitOnEnter");
274
- if (open && !submittable && !alwaysSubmit && hasHighlight) {
275
- event3.preventDefault();
276
- }
277
- if (highlightedValue == null) return;
278
- const itemEl = getItemEl(scope, highlightedValue);
279
- if (isAnchorElement(itemEl)) {
280
- prop("navigate")?.({ value: highlightedValue, node: itemEl, href: itemEl.href });
281
- }
282
- },
283
- Escape() {
284
- send({ type: "INPUT.ESCAPE", keypress, src: "escape-key" });
285
- event2.preventDefault();
286
- }
287
- };
288
- const key = getEventKey(event2, { dir: prop("dir") });
289
- const exec = keymap[key];
290
- exec?.(event2);
291
- }
292
- });
293
- },
294
- getTriggerProps(props2 = {}) {
295
- return normalize.button({
296
- ...parts.trigger.attrs,
297
- dir: prop("dir"),
298
- id: getTriggerId(scope),
299
- "aria-haspopup": composite ? "listbox" : "dialog",
300
- type: "button",
301
- tabIndex: props2.focusable ? void 0 : -1,
302
- "aria-label": translations.triggerLabel,
303
- "aria-expanded": open,
304
- "data-state": open ? "open" : "closed",
305
- "aria-controls": open ? getContentId(scope) : void 0,
306
- disabled,
307
- "data-invalid": dataAttr(invalid),
308
- "data-focusable": dataAttr(props2.focusable),
309
- "data-readonly": dataAttr(readOnly),
310
- "data-disabled": dataAttr(disabled),
311
- onFocus() {
312
- if (!props2.focusable) return;
313
- send({ type: "INPUT.FOCUS", src: "trigger" });
314
- },
315
- onClick(event2) {
316
- if (event2.defaultPrevented) return;
317
- if (!interactive) return;
318
- if (!isLeftClick(event2)) return;
319
- send({ type: "TRIGGER.CLICK", src: "trigger-click" });
320
- },
321
- onPointerDown(event2) {
322
- if (!interactive) return;
323
- if (event2.pointerType === "touch") return;
324
- if (!isLeftClick(event2)) return;
325
- event2.preventDefault();
326
- queueMicrotask(() => {
327
- focusInputEl(scope);
328
- });
329
- },
330
- onKeyDown(event2) {
331
- if (event2.defaultPrevented) return;
332
- if (composite) return;
333
- const keyMap = {
334
- ArrowDown() {
335
- send({ type: "INPUT.ARROW_DOWN", src: "arrow-key" });
336
- },
337
- ArrowUp() {
338
- send({ type: "INPUT.ARROW_UP", src: "arrow-key" });
339
- }
340
- };
341
- const key = getEventKey(event2, { dir: prop("dir") });
342
- const exec = keyMap[key];
343
- if (exec) {
344
- exec(event2);
345
- event2.preventDefault();
346
- }
347
- }
348
- });
349
- },
350
- getContentProps() {
351
- return normalize.element({
352
- ...parts.content.attrs,
353
- dir: prop("dir"),
354
- id: getContentId(scope),
355
- role: !composite ? "dialog" : "listbox",
356
- tabIndex: -1,
357
- hidden: !open,
358
- "data-state": open ? "open" : "closed",
359
- "data-placement": context.get("currentPlacement"),
360
- "aria-labelledby": getLabelId(scope),
361
- "aria-multiselectable": prop("multiple") && composite ? true : void 0,
362
- "data-empty": dataAttr(collection2.size === 0),
363
- onPointerDown(event2) {
364
- if (!isLeftClick(event2)) return;
365
- event2.preventDefault();
366
- }
367
- });
368
- },
369
- getListProps() {
370
- return normalize.element({
371
- ...parts.list.attrs,
372
- role: !composite ? "listbox" : void 0,
373
- "data-empty": dataAttr(collection2.size === 0),
374
- "aria-labelledby": getLabelId(scope),
375
- "aria-multiselectable": prop("multiple") && !composite ? true : void 0
376
- });
377
- },
378
- getClearTriggerProps() {
379
- return normalize.button({
380
- ...parts.clearTrigger.attrs,
381
- dir: prop("dir"),
382
- id: getClearTriggerId(scope),
383
- type: "button",
384
- tabIndex: -1,
385
- disabled,
386
- "data-invalid": dataAttr(invalid),
387
- "aria-label": translations.clearTriggerLabel,
388
- "aria-controls": getInputId(scope),
389
- hidden: !context.get("value").length,
390
- onPointerDown(event2) {
391
- if (!isLeftClick(event2)) return;
392
- event2.preventDefault();
393
- },
394
- onClick(event2) {
395
- if (event2.defaultPrevented) return;
396
- if (!interactive) return;
397
- send({ type: "VALUE.CLEAR", src: "clear-trigger" });
398
- }
399
- });
400
- },
401
- getItemState,
402
- getItemProps(props2) {
403
- const itemState = getItemState(props2);
404
- const value = itemState.value;
405
- return normalize.element({
406
- ...parts.item.attrs,
407
- dir: prop("dir"),
408
- id: getItemId(scope, value),
409
- role: "option",
410
- tabIndex: -1,
411
- "data-highlighted": dataAttr(itemState.highlighted),
412
- "data-state": itemState.selected ? "checked" : "unchecked",
413
- "aria-selected": ariaAttr(itemState.selected),
414
- "aria-disabled": ariaAttr(itemState.disabled),
415
- "data-disabled": dataAttr(itemState.disabled),
416
- "data-value": itemState.value,
417
- onPointerMove() {
418
- if (itemState.disabled) return;
419
- if (itemState.highlighted) return;
420
- send({ type: "ITEM.POINTER_MOVE", value });
421
- },
422
- onPointerLeave() {
423
- if (props2.persistFocus) return;
424
- if (itemState.disabled) return;
425
- const prev = event.previous();
426
- const mouseMoved = prev?.type.includes("POINTER");
427
- if (!mouseMoved) return;
428
- send({ type: "ITEM.POINTER_LEAVE", value });
429
- },
430
- onClick(event2) {
431
- if (isDownloadingEvent(event2)) return;
432
- if (isOpeningInNewTab(event2)) return;
433
- if (isContextMenuEvent(event2)) return;
434
- if (itemState.disabled) return;
435
- send({ type: "ITEM.CLICK", src: "item-select", value });
436
- }
437
- });
438
- },
439
- getItemTextProps(props2) {
440
- const itemState = getItemState(props2);
441
- return normalize.element({
442
- ...parts.itemText.attrs,
443
- dir: prop("dir"),
444
- "data-state": itemState.selected ? "checked" : "unchecked",
445
- "data-disabled": dataAttr(itemState.disabled),
446
- "data-highlighted": dataAttr(itemState.highlighted)
447
- });
448
- },
449
- getItemIndicatorProps(props2) {
450
- const itemState = getItemState(props2);
451
- return normalize.element({
452
- "aria-hidden": true,
453
- ...parts.itemIndicator.attrs,
454
- dir: prop("dir"),
455
- "data-state": itemState.selected ? "checked" : "unchecked",
456
- hidden: !itemState.selected
457
- });
458
- },
459
- getItemGroupProps(props2) {
460
- const { id } = props2;
461
- return normalize.element({
462
- ...parts.itemGroup.attrs,
463
- dir: prop("dir"),
464
- id: getItemGroupId(scope, id),
465
- "aria-labelledby": getItemGroupLabelId(scope, id),
466
- "data-empty": dataAttr(collection2.size === 0),
467
- role: "group"
468
- });
469
- },
470
- getItemGroupLabelProps(props2) {
471
- const { htmlFor } = props2;
472
- return normalize.element({
473
- ...parts.itemGroupLabel.attrs,
474
- dir: prop("dir"),
475
- id: getItemGroupLabelId(scope, htmlFor),
476
- role: "presentation"
477
- });
478
- }
479
- };
480
- }
481
- var { guards, createMachine, choose } = setup();
482
- var { and, not } = guards;
483
- var machine = createMachine({
484
- props({ props: props2 }) {
485
- return {
486
- loopFocus: true,
487
- openOnClick: false,
488
- defaultValue: [],
489
- defaultInputValue: "",
490
- closeOnSelect: !props2.multiple,
491
- allowCustomValue: false,
492
- alwaysSubmitOnEnter: false,
493
- inputBehavior: "none",
494
- selectionBehavior: props2.multiple ? "clear" : "replace",
495
- openOnKeyPress: true,
496
- openOnChange: true,
497
- composite: true,
498
- navigate({ node }) {
499
- clickIfLink(node);
500
- },
501
- collection: collection.empty(),
502
- ...props2,
503
- positioning: {
504
- placement: "bottom",
505
- sameWidth: true,
506
- ...props2.positioning
507
- },
508
- translations: {
509
- triggerLabel: "Toggle suggestions",
510
- clearTriggerLabel: "Clear value",
511
- ...props2.translations
512
- }
513
- };
514
- },
515
- initialState({ prop }) {
516
- const open = prop("open") || prop("defaultOpen");
517
- return open ? "suggesting" : "idle";
518
- },
519
- context({ prop, bindable, getContext, getEvent }) {
520
- return {
521
- currentPlacement: bindable(() => ({
522
- defaultValue: void 0
523
- })),
524
- value: bindable(() => ({
525
- defaultValue: prop("defaultValue"),
526
- value: prop("value"),
527
- isEqual,
528
- hash(value) {
529
- return value.join(",");
530
- },
531
- onChange(value) {
532
- const context = getContext();
533
- const prevSelectedItems = context.get("selectedItems");
534
- const collection2 = prop("collection");
535
- const effectiveValue = prop("value") || value;
536
- const nextItems = effectiveValue.map((v) => {
537
- const item = prevSelectedItems.find((item2) => collection2.getItemValue(item2) === v);
538
- return item || collection2.find(v);
539
- });
540
- context.set("selectedItems", nextItems);
541
- prop("onValueChange")?.({ value, items: nextItems });
542
- }
543
- })),
544
- highlightedValue: bindable(() => ({
545
- defaultValue: prop("defaultHighlightedValue") || null,
546
- value: prop("highlightedValue"),
547
- onChange(value) {
548
- const item = prop("collection").find(value);
549
- prop("onHighlightChange")?.({ highlightedValue: value, highlightedItem: item });
550
- }
551
- })),
552
- inputValue: bindable(() => {
553
- let inputValue = prop("inputValue") || prop("defaultInputValue");
554
- const value = prop("value") || prop("defaultValue");
555
- if (!inputValue.trim() && !prop("multiple")) {
556
- const valueAsString = prop("collection").stringifyMany(value);
557
- inputValue = match(prop("selectionBehavior"), {
558
- preserve: inputValue || valueAsString,
559
- replace: valueAsString,
560
- clear: ""
561
- });
562
- }
563
- return {
564
- defaultValue: inputValue,
565
- value: prop("inputValue"),
566
- onChange(value2) {
567
- const event = getEvent();
568
- const reason = (event.previousEvent || event).src;
569
- prop("onInputValueChange")?.({ inputValue: value2, reason });
570
- }
571
- };
572
- }),
573
- highlightedItem: bindable(() => {
574
- const highlightedValue = prop("highlightedValue");
575
- const highlightedItem = prop("collection").find(highlightedValue);
576
- return { defaultValue: highlightedItem };
577
- }),
578
- selectedItems: bindable(() => {
579
- const value = prop("value") || prop("defaultValue") || [];
580
- const selectedItems = prop("collection").findMany(value);
581
- return { defaultValue: selectedItems };
582
- })
583
- };
584
- },
585
- computed: {
586
- isInputValueEmpty: ({ context }) => context.get("inputValue").length === 0,
587
- isInteractive: ({ prop }) => !(prop("readOnly") || prop("disabled")),
588
- autoComplete: ({ prop }) => prop("inputBehavior") === "autocomplete",
589
- autoHighlight: ({ prop }) => prop("inputBehavior") === "autohighlight",
590
- hasSelectedItems: ({ context }) => context.get("value").length > 0,
591
- valueAsString: ({ context, prop }) => prop("collection").stringifyItems(context.get("selectedItems")),
592
- isCustomValue: ({ context, computed }) => context.get("inputValue") !== computed("valueAsString")
593
- },
594
- watch({ context, prop, track, action, send }) {
595
- track([() => context.hash("value")], () => {
596
- action(["syncSelectedItems"]);
597
- });
598
- track([() => context.get("inputValue")], () => {
599
- action(["syncInputValue"]);
600
- });
601
- track([() => context.get("highlightedValue")], () => {
602
- action(["syncHighlightedItem", "autofillInputValue"]);
603
- });
604
- track([() => prop("open")], () => {
605
- action(["toggleVisibility"]);
606
- });
607
- track([() => prop("collection").toString()], () => {
608
- send({ type: "CHILDREN_CHANGE" });
609
- });
610
- },
611
- on: {
612
- "SELECTED_ITEMS.SYNC": {
613
- actions: ["syncSelectedItems"]
614
- },
615
- "HIGHLIGHTED_VALUE.SET": {
616
- actions: ["setHighlightedValue"]
617
- },
618
- "HIGHLIGHTED_VALUE.CLEAR": {
619
- actions: ["clearHighlightedValue"]
620
- },
621
- "ITEM.SELECT": {
622
- actions: ["selectItem"]
623
- },
624
- "ITEM.CLEAR": {
625
- actions: ["clearItem"]
626
- },
627
- "VALUE.SET": {
628
- actions: ["setValue"]
629
- },
630
- "INPUT_VALUE.SET": {
631
- actions: ["setInputValue"]
632
- },
633
- "POSITIONING.SET": {
634
- actions: ["reposition"]
635
- }
636
- },
637
- entry: choose([
638
- {
639
- guard: "autoFocus",
640
- actions: ["setInitialFocus"]
641
- }
642
- ]),
643
- states: {
644
- idle: {
645
- tags: ["idle", "closed"],
646
- entry: ["scrollContentToTop", "clearHighlightedValue"],
647
- on: {
648
- "CONTROLLED.OPEN": {
649
- target: "interacting"
650
- },
651
- "TRIGGER.CLICK": [
652
- {
653
- guard: "isOpenControlled",
654
- actions: ["setInitialFocus", "highlightFirstSelectedItem", "invokeOnOpen"]
655
- },
656
- {
657
- target: "interacting",
658
- actions: ["setInitialFocus", "highlightFirstSelectedItem", "invokeOnOpen"]
659
- }
660
- ],
661
- "INPUT.CLICK": [
662
- {
663
- guard: "isOpenControlled",
664
- actions: ["highlightFirstSelectedItem", "invokeOnOpen"]
665
- },
666
- {
667
- target: "interacting",
668
- actions: ["highlightFirstSelectedItem", "invokeOnOpen"]
669
- }
670
- ],
671
- "INPUT.FOCUS": {
672
- target: "focused"
673
- },
674
- OPEN: [
675
- {
676
- guard: "isOpenControlled",
677
- actions: ["invokeOnOpen"]
678
- },
679
- {
680
- target: "interacting",
681
- actions: ["invokeOnOpen"]
682
- }
683
- ],
684
- "VALUE.CLEAR": {
685
- target: "focused",
686
- actions: ["clearInputValue", "clearSelectedItems", "setInitialFocus"]
687
- }
688
- }
689
- },
690
- focused: {
691
- tags: ["focused", "closed"],
692
- entry: ["scrollContentToTop", "clearHighlightedValue"],
693
- on: {
694
- "CONTROLLED.OPEN": [
695
- {
696
- guard: "isChangeEvent",
697
- target: "suggesting"
698
- },
699
- {
700
- target: "interacting"
701
- }
702
- ],
703
- "INPUT.CHANGE": [
704
- {
705
- guard: and("isOpenControlled", "openOnChange"),
706
- actions: ["setInputValue", "invokeOnOpen", "highlightFirstItemIfNeeded"]
707
- },
708
- {
709
- guard: "openOnChange",
710
- target: "suggesting",
711
- actions: ["setInputValue", "invokeOnOpen", "highlightFirstItemIfNeeded"]
712
- },
713
- {
714
- actions: ["setInputValue"]
715
- }
716
- ],
717
- "LAYER.INTERACT_OUTSIDE": {
718
- target: "idle"
719
- },
720
- "INPUT.ESCAPE": {
721
- guard: and("isCustomValue", not("allowCustomValue")),
722
- actions: ["revertInputValue"]
723
- },
724
- "INPUT.BLUR": {
725
- target: "idle"
726
- },
727
- "INPUT.CLICK": [
728
- {
729
- guard: "isOpenControlled",
730
- actions: ["highlightFirstSelectedItem", "invokeOnOpen"]
731
- },
732
- {
733
- target: "interacting",
734
- actions: ["highlightFirstSelectedItem", "invokeOnOpen"]
735
- }
736
- ],
737
- "TRIGGER.CLICK": [
738
- {
739
- guard: "isOpenControlled",
740
- actions: ["setInitialFocus", "highlightFirstSelectedItem", "invokeOnOpen"]
741
- },
742
- {
743
- target: "interacting",
744
- actions: ["setInitialFocus", "highlightFirstSelectedItem", "invokeOnOpen"]
745
- }
746
- ],
747
- "INPUT.ARROW_DOWN": [
748
- // == group 1 ==
749
- {
750
- guard: and("isOpenControlled", "autoComplete"),
751
- actions: ["invokeOnOpen"]
752
- },
753
- {
754
- guard: "autoComplete",
755
- target: "interacting",
756
- actions: ["invokeOnOpen"]
757
- },
758
- // == group 2 ==
759
- {
760
- guard: "isOpenControlled",
761
- actions: ["highlightFirstOrSelectedItem", "invokeOnOpen"]
762
- },
763
- {
764
- target: "interacting",
765
- actions: ["highlightFirstOrSelectedItem", "invokeOnOpen"]
766
- }
767
- ],
768
- "INPUT.ARROW_UP": [
769
- // == group 1 ==
770
- {
771
- guard: and("isOpenControlled", "autoComplete"),
772
- actions: ["invokeOnOpen"]
773
- },
774
- {
775
- guard: "autoComplete",
776
- target: "interacting",
777
- actions: ["invokeOnOpen"]
778
- },
779
- // == group 2 ==
780
- {
781
- guard: "isOpenControlled",
782
- actions: ["highlightLastOrSelectedItem", "invokeOnOpen"]
783
- },
784
- {
785
- target: "interacting",
786
- actions: ["highlightLastOrSelectedItem", "invokeOnOpen"]
787
- }
788
- ],
789
- OPEN: [
790
- {
791
- guard: "isOpenControlled",
792
- actions: ["invokeOnOpen"]
793
- },
794
- {
795
- target: "interacting",
796
- actions: ["invokeOnOpen"]
797
- }
798
- ],
799
- "VALUE.CLEAR": {
800
- actions: ["clearInputValue", "clearSelectedItems"]
801
- }
802
- }
803
- },
804
- interacting: {
805
- tags: ["open", "focused"],
806
- entry: ["setInitialFocus"],
807
- effects: ["trackFocusVisible", "scrollToHighlightedItem", "trackDismissableLayer", "trackPlacement"],
808
- on: {
809
- "CONTROLLED.CLOSE": [
810
- {
811
- guard: "restoreFocus",
812
- target: "focused",
813
- actions: ["setFinalFocus"]
814
- },
815
- {
816
- target: "idle"
817
- }
818
- ],
819
- CHILDREN_CHANGE: [
820
- {
821
- guard: "isHighlightedItemRemoved",
822
- actions: ["clearHighlightedValue"]
823
- },
824
- {
825
- actions: ["scrollToHighlightedItem"]
826
- }
827
- ],
828
- "INPUT.HOME": {
829
- actions: ["highlightFirstItem"]
830
- },
831
- "INPUT.END": {
832
- actions: ["highlightLastItem"]
833
- },
834
- "INPUT.ARROW_DOWN": [
835
- {
836
- guard: and("autoComplete", "isLastItemHighlighted"),
837
- actions: ["clearHighlightedValue", "scrollContentToTop"]
838
- },
839
- {
840
- actions: ["highlightNextItem"]
841
- }
842
- ],
843
- "INPUT.ARROW_UP": [
844
- {
845
- guard: and("autoComplete", "isFirstItemHighlighted"),
846
- actions: ["clearHighlightedValue"]
847
- },
848
- {
849
- actions: ["highlightPrevItem"]
850
- }
851
- ],
852
- "INPUT.ENTER": [
853
- // == group 1 ==
854
- {
855
- guard: and("isOpenControlled", "isCustomValue", not("hasHighlightedItem"), not("allowCustomValue")),
856
- actions: ["revertInputValue", "invokeOnClose"]
857
- },
858
- {
859
- guard: and("isCustomValue", not("hasHighlightedItem"), not("allowCustomValue")),
860
- target: "focused",
861
- actions: ["revertInputValue", "invokeOnClose"]
862
- },
863
- // == group 2 ==
864
- {
865
- guard: and("isOpenControlled", "closeOnSelect"),
866
- actions: ["selectHighlightedItem", "invokeOnClose"]
867
- },
868
- {
869
- guard: "closeOnSelect",
870
- target: "focused",
871
- actions: ["selectHighlightedItem", "invokeOnClose", "setFinalFocus"]
872
- },
873
- {
874
- actions: ["selectHighlightedItem"]
875
- }
876
- ],
877
- "INPUT.CHANGE": [
878
- {
879
- guard: "autoComplete",
880
- target: "suggesting",
881
- actions: ["setInputValue"]
882
- },
883
- {
884
- target: "suggesting",
885
- actions: ["clearHighlightedValue", "setInputValue"]
886
- }
887
- ],
888
- "ITEM.POINTER_MOVE": {
889
- actions: ["setHighlightedValue"]
890
- },
891
- "ITEM.POINTER_LEAVE": {
892
- actions: ["clearHighlightedValue"]
893
- },
894
- "ITEM.CLICK": [
895
- {
896
- guard: and("isOpenControlled", "closeOnSelect"),
897
- actions: ["selectItem", "invokeOnClose"]
898
- },
899
- {
900
- guard: "closeOnSelect",
901
- target: "focused",
902
- actions: ["selectItem", "invokeOnClose", "setFinalFocus"]
903
- },
904
- {
905
- actions: ["selectItem"]
906
- }
907
- ],
908
- "LAYER.ESCAPE": [
909
- {
910
- guard: and("isOpenControlled", "autoComplete"),
911
- actions: ["syncInputValue", "invokeOnClose"]
912
- },
913
- {
914
- guard: "autoComplete",
915
- target: "focused",
916
- actions: ["syncInputValue", "invokeOnClose"]
917
- },
918
- {
919
- guard: "isOpenControlled",
920
- actions: ["invokeOnClose"]
921
- },
922
- {
923
- target: "focused",
924
- actions: ["invokeOnClose", "setFinalFocus"]
925
- }
926
- ],
927
- "TRIGGER.CLICK": [
928
- {
929
- guard: "isOpenControlled",
930
- actions: ["invokeOnClose"]
931
- },
932
- {
933
- target: "focused",
934
- actions: ["invokeOnClose"]
935
- }
936
- ],
937
- "LAYER.INTERACT_OUTSIDE": [
938
- // == group 1 ==
939
- {
940
- guard: and("isOpenControlled", "isCustomValue", not("allowCustomValue")),
941
- actions: ["revertInputValue", "invokeOnClose"]
942
- },
943
- {
944
- guard: and("isCustomValue", not("allowCustomValue")),
945
- target: "idle",
946
- actions: ["revertInputValue", "invokeOnClose"]
947
- },
948
- // == group 2 ==
949
- {
950
- guard: "isOpenControlled",
951
- actions: ["invokeOnClose"]
952
- },
953
- {
954
- target: "idle",
955
- actions: ["invokeOnClose"]
956
- }
957
- ],
958
- CLOSE: [
959
- {
960
- guard: "isOpenControlled",
961
- actions: ["invokeOnClose"]
962
- },
963
- {
964
- target: "focused",
965
- actions: ["invokeOnClose", "setFinalFocus"]
966
- }
967
- ],
968
- "VALUE.CLEAR": [
969
- {
970
- guard: "isOpenControlled",
971
- actions: ["clearInputValue", "clearSelectedItems", "invokeOnClose"]
972
- },
973
- {
974
- target: "focused",
975
- actions: ["clearInputValue", "clearSelectedItems", "invokeOnClose", "setFinalFocus"]
976
- }
977
- ]
978
- }
979
- },
980
- suggesting: {
981
- tags: ["open", "focused"],
982
- effects: ["trackFocusVisible", "trackDismissableLayer", "scrollToHighlightedItem", "trackPlacement"],
983
- entry: ["setInitialFocus"],
984
- on: {
985
- "CONTROLLED.CLOSE": [
986
- {
987
- guard: "restoreFocus",
988
- target: "focused",
989
- actions: ["setFinalFocus"]
990
- },
991
- {
992
- target: "idle"
993
- }
994
- ],
995
- CHILDREN_CHANGE: [
996
- {
997
- guard: and("isHighlightedItemRemoved", "hasCollectionItems", "autoHighlight"),
998
- actions: ["clearHighlightedValue", "highlightFirstItem"]
999
- },
1000
- {
1001
- guard: "isHighlightedItemRemoved",
1002
- actions: ["clearHighlightedValue"]
1003
- },
1004
- {
1005
- guard: "autoHighlight",
1006
- actions: ["highlightFirstItem"]
1007
- }
1008
- ],
1009
- "INPUT.ARROW_DOWN": {
1010
- target: "interacting",
1011
- actions: ["highlightNextItem"]
1012
- },
1013
- "INPUT.ARROW_UP": {
1014
- target: "interacting",
1015
- actions: ["highlightPrevItem"]
1016
- },
1017
- "INPUT.HOME": {
1018
- target: "interacting",
1019
- actions: ["highlightFirstItem"]
1020
- },
1021
- "INPUT.END": {
1022
- target: "interacting",
1023
- actions: ["highlightLastItem"]
1024
- },
1025
- "INPUT.ENTER": [
1026
- // == group 1 ==
1027
- {
1028
- guard: and("isOpenControlled", "isCustomValue", not("hasHighlightedItem"), not("allowCustomValue")),
1029
- actions: ["revertInputValue", "invokeOnClose"]
1030
- },
1031
- {
1032
- guard: and("isCustomValue", not("hasHighlightedItem"), not("allowCustomValue")),
1033
- target: "focused",
1034
- actions: ["revertInputValue", "invokeOnClose"]
1035
- },
1036
- // == group 2 ==
1037
- {
1038
- guard: and("isOpenControlled", "closeOnSelect"),
1039
- actions: ["selectHighlightedItem", "invokeOnClose"]
1040
- },
1041
- {
1042
- guard: "closeOnSelect",
1043
- target: "focused",
1044
- actions: ["selectHighlightedItem", "invokeOnClose", "setFinalFocus"]
1045
- },
1046
- {
1047
- actions: ["selectHighlightedItem"]
1048
- }
1049
- ],
1050
- "INPUT.CHANGE": {
1051
- actions: ["setInputValue"]
1052
- },
1053
- "LAYER.ESCAPE": [
1054
- {
1055
- guard: "isOpenControlled",
1056
- actions: ["invokeOnClose"]
1057
- },
1058
- {
1059
- target: "focused",
1060
- actions: ["invokeOnClose"]
1061
- }
1062
- ],
1063
- "ITEM.POINTER_MOVE": {
1064
- target: "interacting",
1065
- actions: ["setHighlightedValue"]
1066
- },
1067
- "ITEM.POINTER_LEAVE": {
1068
- actions: ["clearHighlightedValue"]
1069
- },
1070
- "LAYER.INTERACT_OUTSIDE": [
1071
- // == group 1 ==
1072
- {
1073
- guard: and("isOpenControlled", "isCustomValue", not("allowCustomValue")),
1074
- actions: ["revertInputValue", "invokeOnClose"]
1075
- },
1076
- {
1077
- guard: and("isCustomValue", not("allowCustomValue")),
1078
- target: "idle",
1079
- actions: ["revertInputValue", "invokeOnClose"]
1080
- },
1081
- // == group 2 ==
1082
- {
1083
- guard: "isOpenControlled",
1084
- actions: ["invokeOnClose"]
1085
- },
1086
- {
1087
- target: "idle",
1088
- actions: ["invokeOnClose"]
1089
- }
1090
- ],
1091
- "TRIGGER.CLICK": [
1092
- {
1093
- guard: "isOpenControlled",
1094
- actions: ["invokeOnClose"]
1095
- },
1096
- {
1097
- target: "focused",
1098
- actions: ["invokeOnClose"]
1099
- }
1100
- ],
1101
- "ITEM.CLICK": [
1102
- {
1103
- guard: and("isOpenControlled", "closeOnSelect"),
1104
- actions: ["selectItem", "invokeOnClose"]
1105
- },
1106
- {
1107
- guard: "closeOnSelect",
1108
- target: "focused",
1109
- actions: ["selectItem", "invokeOnClose", "setFinalFocus"]
1110
- },
1111
- {
1112
- actions: ["selectItem"]
1113
- }
1114
- ],
1115
- CLOSE: [
1116
- {
1117
- guard: "isOpenControlled",
1118
- actions: ["invokeOnClose"]
1119
- },
1120
- {
1121
- target: "focused",
1122
- actions: ["invokeOnClose", "setFinalFocus"]
1123
- }
1124
- ],
1125
- "VALUE.CLEAR": [
1126
- {
1127
- guard: "isOpenControlled",
1128
- actions: ["clearInputValue", "clearSelectedItems", "invokeOnClose"]
1129
- },
1130
- {
1131
- target: "focused",
1132
- actions: ["clearInputValue", "clearSelectedItems", "invokeOnClose", "setFinalFocus"]
1133
- }
1134
- ]
1135
- }
1136
- }
1137
- },
1138
- implementations: {
1139
- guards: {
1140
- isInputValueEmpty: ({ computed }) => computed("isInputValueEmpty"),
1141
- autoComplete: ({ computed, prop }) => computed("autoComplete") && !prop("multiple"),
1142
- autoHighlight: ({ computed }) => computed("autoHighlight"),
1143
- isFirstItemHighlighted: ({ prop, context }) => prop("collection").firstValue === context.get("highlightedValue"),
1144
- isLastItemHighlighted: ({ prop, context }) => prop("collection").lastValue === context.get("highlightedValue"),
1145
- isCustomValue: ({ computed }) => computed("isCustomValue"),
1146
- allowCustomValue: ({ prop }) => !!prop("allowCustomValue"),
1147
- hasHighlightedItem: ({ context }) => context.get("highlightedValue") != null,
1148
- closeOnSelect: ({ prop }) => !!prop("closeOnSelect"),
1149
- isOpenControlled: ({ prop }) => prop("open") != null,
1150
- openOnChange: ({ prop, context }) => {
1151
- const openOnChange = prop("openOnChange");
1152
- if (isBoolean(openOnChange)) return openOnChange;
1153
- return !!openOnChange?.({ inputValue: context.get("inputValue") });
1154
- },
1155
- restoreFocus: ({ event }) => {
1156
- const restoreFocus = event.restoreFocus ?? event.previousEvent?.restoreFocus;
1157
- return restoreFocus == null ? true : !!restoreFocus;
1158
- },
1159
- isChangeEvent: ({ event }) => event.previousEvent?.type === "INPUT.CHANGE",
1160
- autoFocus: ({ prop }) => !!prop("autoFocus"),
1161
- isHighlightedItemRemoved: ({ prop, context }) => !prop("collection").has(context.get("highlightedValue")),
1162
- hasCollectionItems: ({ prop }) => prop("collection").size > 0
1163
- },
1164
- effects: {
1165
- trackFocusVisible({ scope }) {
1166
- return trackFocusVisible({ root: scope.getRootNode?.() });
1167
- },
1168
- trackDismissableLayer({ send, prop, scope }) {
1169
- if (prop("disableLayer")) return;
1170
- const contentEl = () => getContentEl(scope);
1171
- return trackDismissableElement(contentEl, {
1172
- type: "listbox",
1173
- defer: true,
1174
- exclude: () => [getInputEl(scope), getTriggerEl(scope), getClearTriggerEl(scope)],
1175
- onFocusOutside: prop("onFocusOutside"),
1176
- onPointerDownOutside: prop("onPointerDownOutside"),
1177
- onInteractOutside: prop("onInteractOutside"),
1178
- onEscapeKeyDown(event) {
1179
- event.preventDefault();
1180
- event.stopPropagation();
1181
- send({ type: "LAYER.ESCAPE", src: "escape-key" });
1182
- },
1183
- onDismiss() {
1184
- send({ type: "LAYER.INTERACT_OUTSIDE", src: "interact-outside", restoreFocus: false });
1185
- }
1186
- });
1187
- },
1188
- trackPlacement({ context, prop, scope }) {
1189
- const anchorEl = () => getControlEl(scope) || getTriggerEl(scope);
1190
- const positionerEl = () => getPositionerEl(scope);
1191
- context.set("currentPlacement", prop("positioning").placement);
1192
- return getPlacement(anchorEl, positionerEl, {
1193
- ...prop("positioning"),
1194
- defer: true,
1195
- onComplete(data) {
1196
- context.set("currentPlacement", data.placement);
1197
- }
1198
- });
1199
- },
1200
- scrollToHighlightedItem({ context, prop, scope }) {
1201
- const inputEl = getInputEl(scope);
1202
- let cleanups = [];
1203
- const exec = (immediate) => {
1204
- const modality = getInteractionModality();
1205
- if (modality === "pointer") return;
1206
- const highlightedValue = context.get("highlightedValue");
1207
- if (!highlightedValue) return;
1208
- const contentEl = getContentEl(scope);
1209
- const scrollToIndexFn = prop("scrollToIndexFn");
1210
- if (scrollToIndexFn) {
1211
- const highlightedIndex = prop("collection").indexOf(highlightedValue);
1212
- scrollToIndexFn({
1213
- index: highlightedIndex,
1214
- immediate,
1215
- getElement: () => getItemEl(scope, highlightedValue)
1216
- });
1217
- return;
1218
- }
1219
- const itemEl = getItemEl(scope, highlightedValue);
1220
- const raf_cleanup = raf(() => {
1221
- scrollIntoView(itemEl, { rootEl: contentEl, block: "nearest" });
1222
- });
1223
- cleanups.push(raf_cleanup);
1224
- };
1225
- const rafCleanup = raf(() => {
1226
- setInteractionModality("virtual");
1227
- exec(true);
1228
- });
1229
- cleanups.push(rafCleanup);
1230
- const observerCleanup = observeAttributes(inputEl, {
1231
- attributes: ["aria-activedescendant"],
1232
- callback: () => exec(false)
1233
- });
1234
- cleanups.push(observerCleanup);
1235
- return () => {
1236
- cleanups.forEach((cleanup) => cleanup());
1237
- };
1238
- }
1239
- },
1240
- actions: {
1241
- reposition({ context, prop, scope, event }) {
1242
- const controlEl = () => getControlEl(scope);
1243
- const positionerEl = () => getPositionerEl(scope);
1244
- getPlacement(controlEl, positionerEl, {
1245
- ...prop("positioning"),
1246
- ...event.options,
1247
- defer: true,
1248
- listeners: false,
1249
- onComplete(data) {
1250
- context.set("currentPlacement", data.placement);
1251
- }
1252
- });
1253
- },
1254
- setHighlightedValue({ context, event }) {
1255
- if (event.value == null) return;
1256
- context.set("highlightedValue", event.value);
1257
- },
1258
- clearHighlightedValue({ context }) {
1259
- context.set("highlightedValue", null);
1260
- },
1261
- selectHighlightedItem(params) {
1262
- const { context, prop } = params;
1263
- const collection2 = prop("collection");
1264
- const highlightedValue = context.get("highlightedValue");
1265
- if (!highlightedValue || !collection2.has(highlightedValue)) return;
1266
- const nextValue = prop("multiple") ? addOrRemove(context.get("value"), highlightedValue) : [highlightedValue];
1267
- prop("onSelect")?.({ value: nextValue, itemValue: highlightedValue });
1268
- context.set("value", nextValue);
1269
- const inputValue = match(prop("selectionBehavior"), {
1270
- preserve: context.get("inputValue"),
1271
- replace: collection2.stringifyMany(nextValue),
1272
- clear: ""
1273
- });
1274
- context.set("inputValue", inputValue);
1275
- },
1276
- scrollToHighlightedItem({ context, prop, scope }) {
1277
- nextTick(() => {
1278
- const highlightedValue = context.get("highlightedValue");
1279
- if (highlightedValue == null) return;
1280
- const itemEl = getItemEl(scope, highlightedValue);
1281
- const contentEl = getContentEl(scope);
1282
- const scrollToIndexFn = prop("scrollToIndexFn");
1283
- if (scrollToIndexFn) {
1284
- const highlightedIndex = prop("collection").indexOf(highlightedValue);
1285
- scrollToIndexFn({
1286
- index: highlightedIndex,
1287
- immediate: true,
1288
- getElement: () => getItemEl(scope, highlightedValue)
1289
- });
1290
- return;
1291
- }
1292
- scrollIntoView(itemEl, { rootEl: contentEl, block: "nearest" });
1293
- });
1294
- },
1295
- selectItem(params) {
1296
- const { context, event, flush, prop } = params;
1297
- if (event.value == null) return;
1298
- flush(() => {
1299
- const nextValue = prop("multiple") ? addOrRemove(context.get("value"), event.value) : [event.value];
1300
- prop("onSelect")?.({ value: nextValue, itemValue: event.value });
1301
- context.set("value", nextValue);
1302
- const inputValue = match(prop("selectionBehavior"), {
1303
- preserve: context.get("inputValue"),
1304
- replace: prop("collection").stringifyMany(nextValue),
1305
- clear: ""
1306
- });
1307
- context.set("inputValue", inputValue);
1308
- });
1309
- },
1310
- clearItem(params) {
1311
- const { context, event, flush, prop } = params;
1312
- if (event.value == null) return;
1313
- flush(() => {
1314
- const nextValue = remove(context.get("value"), event.value);
1315
- context.set("value", nextValue);
1316
- const inputValue = match(prop("selectionBehavior"), {
1317
- preserve: context.get("inputValue"),
1318
- replace: prop("collection").stringifyMany(nextValue),
1319
- clear: ""
1320
- });
1321
- context.set("inputValue", inputValue);
1322
- });
1323
- },
1324
- setInitialFocus({ scope }) {
1325
- raf(() => {
1326
- focusInputEl(scope);
1327
- });
1328
- },
1329
- setFinalFocus({ scope }) {
1330
- raf(() => {
1331
- const triggerEl = getTriggerEl(scope);
1332
- if (triggerEl?.dataset.focusable == null) {
1333
- focusInputEl(scope);
1334
- } else {
1335
- focusTriggerEl(scope);
1336
- }
1337
- });
1338
- },
1339
- syncInputValue({ context, scope, event }) {
1340
- const inputEl = getInputEl(scope);
1341
- if (!inputEl) return;
1342
- inputEl.value = context.get("inputValue");
1343
- queueMicrotask(() => {
1344
- if (event.current().type === "INPUT.CHANGE") return;
1345
- setCaretToEnd(inputEl);
1346
- });
1347
- },
1348
- setInputValue({ context, event }) {
1349
- context.set("inputValue", event.value);
1350
- },
1351
- clearInputValue({ context }) {
1352
- context.set("inputValue", "");
1353
- },
1354
- revertInputValue({ context, prop, computed }) {
1355
- const selectionBehavior = prop("selectionBehavior");
1356
- const inputValue = match(selectionBehavior, {
1357
- replace: computed("hasSelectedItems") ? computed("valueAsString") : "",
1358
- preserve: context.get("inputValue"),
1359
- clear: ""
1360
- });
1361
- context.set("inputValue", inputValue);
1362
- },
1363
- setValue(params) {
1364
- const { context, flush, event, prop } = params;
1365
- flush(() => {
1366
- context.set("value", event.value);
1367
- const inputValue = match(prop("selectionBehavior"), {
1368
- preserve: context.get("inputValue"),
1369
- replace: prop("collection").stringifyMany(event.value),
1370
- clear: ""
1371
- });
1372
- context.set("inputValue", inputValue);
1373
- });
1374
- },
1375
- clearSelectedItems(params) {
1376
- const { context, flush, prop } = params;
1377
- flush(() => {
1378
- context.set("value", []);
1379
- const inputValue = match(prop("selectionBehavior"), {
1380
- preserve: context.get("inputValue"),
1381
- replace: prop("collection").stringifyMany([]),
1382
- clear: ""
1383
- });
1384
- context.set("inputValue", inputValue);
1385
- });
1386
- },
1387
- scrollContentToTop({ prop, scope }) {
1388
- const scrollToIndexFn = prop("scrollToIndexFn");
1389
- if (scrollToIndexFn) {
1390
- const firstValue = prop("collection").firstValue;
1391
- scrollToIndexFn({
1392
- index: 0,
1393
- immediate: true,
1394
- getElement: () => getItemEl(scope, firstValue)
1395
- });
1396
- } else {
1397
- const contentEl = getContentEl(scope);
1398
- if (!contentEl) return;
1399
- contentEl.scrollTop = 0;
1400
- }
1401
- },
1402
- invokeOnOpen({ prop, event, context }) {
1403
- const reason = getOpenChangeReason(event);
1404
- prop("onOpenChange")?.({ open: true, reason, value: context.get("value") });
1405
- },
1406
- invokeOnClose({ prop, event, context }) {
1407
- const reason = getOpenChangeReason(event);
1408
- prop("onOpenChange")?.({ open: false, reason, value: context.get("value") });
1409
- },
1410
- highlightFirstItem({ context, prop, scope }) {
1411
- const exec = getContentEl(scope) ? queueMicrotask : raf;
1412
- exec(() => {
1413
- const value = prop("collection").firstValue;
1414
- if (value) context.set("highlightedValue", value);
1415
- });
1416
- },
1417
- highlightFirstItemIfNeeded({ computed, action }) {
1418
- if (!computed("autoHighlight")) return;
1419
- action(["highlightFirstItem"]);
1420
- },
1421
- highlightLastItem({ context, prop, scope }) {
1422
- const exec = getContentEl(scope) ? queueMicrotask : raf;
1423
- exec(() => {
1424
- const value = prop("collection").lastValue;
1425
- if (value) context.set("highlightedValue", value);
1426
- });
1427
- },
1428
- highlightNextItem({ context, prop }) {
1429
- let value = null;
1430
- const highlightedValue = context.get("highlightedValue");
1431
- const collection2 = prop("collection");
1432
- if (highlightedValue) {
1433
- value = collection2.getNextValue(highlightedValue);
1434
- if (!value && prop("loopFocus")) value = collection2.firstValue;
1435
- } else {
1436
- value = collection2.firstValue;
1437
- }
1438
- if (value) context.set("highlightedValue", value);
1439
- },
1440
- highlightPrevItem({ context, prop }) {
1441
- let value = null;
1442
- const highlightedValue = context.get("highlightedValue");
1443
- const collection2 = prop("collection");
1444
- if (highlightedValue) {
1445
- value = collection2.getPreviousValue(highlightedValue);
1446
- if (!value && prop("loopFocus")) value = collection2.lastValue;
1447
- } else {
1448
- value = collection2.lastValue;
1449
- }
1450
- if (value) context.set("highlightedValue", value);
1451
- },
1452
- highlightFirstSelectedItem({ context, prop }) {
1453
- raf(() => {
1454
- const [value] = prop("collection").sort(context.get("value"));
1455
- if (value) context.set("highlightedValue", value);
1456
- });
1457
- },
1458
- highlightFirstOrSelectedItem({ context, prop, computed }) {
1459
- raf(() => {
1460
- let value = null;
1461
- if (computed("hasSelectedItems")) {
1462
- value = prop("collection").sort(context.get("value"))[0];
1463
- } else {
1464
- value = prop("collection").firstValue;
1465
- }
1466
- if (value) context.set("highlightedValue", value);
1467
- });
1468
- },
1469
- highlightLastOrSelectedItem({ context, prop, computed }) {
1470
- raf(() => {
1471
- const collection2 = prop("collection");
1472
- let value = null;
1473
- if (computed("hasSelectedItems")) {
1474
- value = collection2.sort(context.get("value"))[0];
1475
- } else {
1476
- value = collection2.lastValue;
1477
- }
1478
- if (value) context.set("highlightedValue", value);
1479
- });
1480
- },
1481
- autofillInputValue({ context, computed, prop, event, scope }) {
1482
- const inputEl = getInputEl(scope);
1483
- const collection2 = prop("collection");
1484
- if (!computed("autoComplete") || !inputEl || !event.keypress) return;
1485
- const valueText = collection2.stringify(context.get("highlightedValue"));
1486
- raf(() => {
1487
- inputEl.value = valueText || context.get("inputValue");
1488
- });
1489
- },
1490
- syncSelectedItems(params) {
1491
- queueMicrotask(() => {
1492
- const { context, prop } = params;
1493
- const collection2 = prop("collection");
1494
- const value = context.get("value");
1495
- const selectedItems = value.map((v) => {
1496
- const item = context.get("selectedItems").find((item2) => collection2.getItemValue(item2) === v);
1497
- return item || collection2.find(v);
1498
- });
1499
- context.set("selectedItems", selectedItems);
1500
- const inputValue = match(prop("selectionBehavior"), {
1501
- preserve: context.get("inputValue"),
1502
- replace: collection2.stringifyMany(value),
1503
- clear: ""
1504
- });
1505
- context.set("inputValue", inputValue);
1506
- });
1507
- },
1508
- syncHighlightedItem({ context, prop }) {
1509
- const item = prop("collection").find(context.get("highlightedValue"));
1510
- context.set("highlightedItem", item);
1511
- },
1512
- toggleVisibility({ event, send, prop }) {
1513
- send({ type: prop("open") ? "CONTROLLED.OPEN" : "CONTROLLED.CLOSE", previousEvent: event });
1514
- }
1515
- }
1516
- }
1517
- });
1518
- function getOpenChangeReason(event) {
1519
- return (event.previousEvent || event).src;
1520
- }
1521
- var props = createProps()([
1522
- "allowCustomValue",
1523
- "autoFocus",
1524
- "closeOnSelect",
1525
- "collection",
1526
- "composite",
1527
- "defaultHighlightedValue",
1528
- "defaultInputValue",
1529
- "defaultOpen",
1530
- "defaultValue",
1531
- "dir",
1532
- "disabled",
1533
- "disableLayer",
1534
- "form",
1535
- "getRootNode",
1536
- "highlightedValue",
1537
- "id",
1538
- "ids",
1539
- "inputBehavior",
1540
- "inputValue",
1541
- "invalid",
1542
- "loopFocus",
1543
- "multiple",
1544
- "name",
1545
- "navigate",
1546
- "onFocusOutside",
1547
- "onHighlightChange",
1548
- "onInputValueChange",
1549
- "onInteractOutside",
1550
- "onOpenChange",
1551
- "onOpenChange",
1552
- "onPointerDownOutside",
1553
- "onSelect",
1554
- "onValueChange",
1555
- "open",
1556
- "openOnChange",
1557
- "openOnClick",
1558
- "openOnKeyPress",
1559
- "placeholder",
1560
- "positioning",
1561
- "readOnly",
1562
- "required",
1563
- "scrollToIndexFn",
1564
- "selectionBehavior",
1565
- "translations",
1566
- "value",
1567
- "alwaysSubmitOnEnter"
1568
- ]);
1569
- var splitProps = createSplitProps(props);
1570
- var itemGroupLabelProps = createProps()(["htmlFor"]);
1571
- var splitItemGroupLabelProps = createSplitProps(itemGroupLabelProps);
1572
- var itemGroupProps = createProps()(["id"]);
1573
- var splitItemGroupProps = createSplitProps(itemGroupProps);
1574
- var itemProps = createProps()(["item", "persistFocus"]);
1575
- var splitItemProps = createSplitProps(itemProps);
1576
-
1577
- export { anatomy, collection, connect, itemGroupLabelProps, itemGroupProps, itemProps, machine, props, splitItemGroupLabelProps, splitItemGroupProps, splitItemProps, splitProps };