@zag-js/combobox 0.49.0 → 0.50.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.js CHANGED
@@ -69,7 +69,6 @@ var dom = (0, import_dom_query.createScope)({
69
69
  getControlId: (ctx) => ctx.ids?.control ?? `combobox:${ctx.id}:control`,
70
70
  getInputId: (ctx) => ctx.ids?.input ?? `combobox:${ctx.id}:input`,
71
71
  getContentId: (ctx) => ctx.ids?.content ?? `combobox:${ctx.id}:content`,
72
- getListId: (ctx) => `combobox:${ctx.id}:listbox`,
73
72
  getPositionerId: (ctx) => ctx.ids?.positioner ?? `combobox:${ctx.id}:popper`,
74
73
  getTriggerId: (ctx) => ctx.ids?.trigger ?? `combobox:${ctx.id}:toggle-btn`,
75
74
  getClearTriggerId: (ctx) => ctx.ids?.clearTrigger ?? `combobox:${ctx.id}:clear-btn`,
@@ -77,18 +76,28 @@ var dom = (0, import_dom_query.createScope)({
77
76
  getItemGroupLabelId: (ctx, id) => ctx.ids?.itemGroupLabel?.(id) ?? `combobox:${ctx.id}:optgroup-label:${id}`,
78
77
  getItemId: (ctx, id) => `combobox:${ctx.id}:option:${id}`,
79
78
  getContentEl: (ctx) => dom.getById(ctx, dom.getContentId(ctx)),
80
- getListEl: (ctx) => dom.getById(ctx, dom.getListId(ctx)),
81
79
  getInputEl: (ctx) => dom.getById(ctx, dom.getInputId(ctx)),
82
80
  getPositionerEl: (ctx) => dom.getById(ctx, dom.getPositionerId(ctx)),
83
81
  getControlEl: (ctx) => dom.getById(ctx, dom.getControlId(ctx)),
84
82
  getTriggerEl: (ctx) => dom.getById(ctx, dom.getTriggerId(ctx)),
85
83
  getClearTriggerEl: (ctx) => dom.getById(ctx, dom.getClearTriggerId(ctx)),
86
- isInputFocused: (ctx) => dom.getDoc(ctx).activeElement === dom.getInputEl(ctx),
87
84
  getHighlightedItemEl: (ctx) => {
88
85
  const value = ctx.highlightedValue;
89
86
  if (value == null)
90
87
  return;
91
- return dom.getContentEl(ctx)?.querySelector(`[role=option][data-value="${CSS.escape(value)}"`);
88
+ return (0, import_dom_query.query)(dom.getContentEl(ctx), `[role=option][data-value="${CSS.escape(value)}"`);
89
+ },
90
+ focusInputEl: (ctx) => {
91
+ const inputEl = dom.getInputEl(ctx);
92
+ if (dom.getActiveElement(ctx) === inputEl)
93
+ return;
94
+ inputEl?.focus({ preventScroll: true });
95
+ },
96
+ focusTriggerEl: (ctx) => {
97
+ const triggerEl = dom.getTriggerEl(ctx);
98
+ if (dom.getActiveElement(ctx) === triggerEl)
99
+ return;
100
+ triggerEl?.focus({ preventScroll: true });
92
101
  }
93
102
  });
94
103
 
@@ -102,7 +111,8 @@ function connect(state, send, normalize) {
102
111
  const readOnly = state.context.readOnly;
103
112
  const open = state.hasTag("open");
104
113
  const focused = state.hasTag("focused");
105
- const isDialogPopup = state.context.popup === "dialog";
114
+ const composite = state.context.composite;
115
+ const highlightedValue = state.context.highlightedValue;
106
116
  const popperStyles = (0, import_popper.getPlacementStyles)({
107
117
  ...state.context.positioning,
108
118
  placement: state.context.currentPlacement
@@ -114,7 +124,7 @@ function connect(state, send, normalize) {
114
124
  return {
115
125
  value,
116
126
  disabled: Boolean(disabled2 || disabled2),
117
- highlighted: state.context.highlightedValue === value,
127
+ highlighted: highlightedValue === value,
118
128
  selected: state.context.value.includes(value)
119
129
  };
120
130
  }
@@ -122,8 +132,7 @@ function connect(state, send, normalize) {
122
132
  focused,
123
133
  open,
124
134
  inputValue: state.context.inputValue,
125
- inputEmpty: state.context.isInputValueEmpty,
126
- highlightedValue: state.context.highlightedValue,
135
+ highlightedValue,
127
136
  highlightedItem: state.context.highlightedItem,
128
137
  value: state.context.value,
129
138
  valueAsString: state.context.valueAsString,
@@ -136,7 +145,7 @@ function connect(state, send, normalize) {
136
145
  setCollection(collection3) {
137
146
  send({ type: "COLLECTION.SET", value: collection3 });
138
147
  },
139
- highlightValue(value) {
148
+ setHighlightValue(value) {
140
149
  send({ type: "HIGHLIGHTED_VALUE.SET", value });
141
150
  },
142
151
  selectValue(value) {
@@ -158,10 +167,10 @@ function connect(state, send, normalize) {
158
167
  focus() {
159
168
  dom.getInputEl(state.context)?.focus();
160
169
  },
161
- setOpen(_open) {
162
- if (_open === open)
170
+ setOpen(nextOpen) {
171
+ if (nextOpen === open)
163
172
  return;
164
- send(_open ? "OPEN" : "CLOSE");
173
+ send(nextOpen ? "OPEN" : "CLOSE");
165
174
  },
166
175
  rootProps: normalize.element({
167
176
  ...parts.root.attrs,
@@ -180,7 +189,7 @@ function connect(state, send, normalize) {
180
189
  "data-invalid": (0, import_dom_query2.dataAttr)(invalid),
181
190
  "data-focus": (0, import_dom_query2.dataAttr)(focused),
182
191
  onClick(event) {
183
- if (!isDialogPopup)
192
+ if (composite)
184
193
  return;
185
194
  event.preventDefault();
186
195
  dom.getTriggerEl(state.context)?.focus({ preventScroll: true });
@@ -221,11 +230,13 @@ function connect(state, send, normalize) {
221
230
  role: "combobox",
222
231
  defaultValue: state.context.inputValue,
223
232
  "aria-autocomplete": state.context.autoComplete ? "both" : "list",
224
- "aria-controls": isDialogPopup ? dom.getListId(state.context) : dom.getContentId(state.context),
233
+ "aria-controls": dom.getContentId(state.context),
225
234
  "aria-expanded": open,
226
235
  "data-state": open ? "open" : "closed",
227
- "aria-activedescendant": state.context.highlightedValue ? dom.getItemId(state.context, state.context.highlightedValue) : void 0,
228
- onClick() {
236
+ "aria-activedescendant": highlightedValue ? dom.getItemId(state.context, highlightedValue) : void 0,
237
+ onClick(event) {
238
+ if (event.defaultPrevented)
239
+ return;
229
240
  if (!state.context.openOnClick)
230
241
  return;
231
242
  if (!interactive)
@@ -305,86 +316,87 @@ function connect(state, send, normalize) {
305
316
  exec?.(event);
306
317
  }
307
318
  }),
308
- triggerProps: normalize.button({
309
- ...parts.trigger.attrs,
310
- dir: state.context.dir,
311
- id: dom.getTriggerId(state.context),
312
- "aria-haspopup": isDialogPopup ? "dialog" : "listbox",
313
- type: "button",
314
- tabIndex: isDialogPopup ? 0 : -1,
315
- "aria-label": translations.triggerLabel,
316
- "aria-expanded": open,
317
- "data-state": open ? "open" : "closed",
318
- "aria-controls": open ? dom.getContentId(state.context) : void 0,
319
- disabled,
320
- "data-readonly": (0, import_dom_query2.dataAttr)(readOnly),
321
- "data-disabled": (0, import_dom_query2.dataAttr)(disabled),
322
- onClick(event) {
323
- const evt = (0, import_dom_event.getNativeEvent)(event);
324
- if (!interactive)
325
- return;
326
- if (!(0, import_dom_event.isLeftClick)(evt))
327
- return;
328
- send("TRIGGER.CLICK");
329
- },
330
- onPointerDown(event) {
331
- if (!interactive)
332
- return;
333
- if (event.pointerType === "touch")
334
- return;
335
- event.preventDefault();
336
- queueMicrotask(() => {
337
- dom.getInputEl(state.context)?.focus({ preventScroll: true });
338
- });
339
- },
340
- onKeyDown(event) {
341
- if (event.defaultPrevented)
342
- return;
343
- if (!isDialogPopup)
344
- return;
345
- const keyMap = {
346
- ArrowDown() {
347
- send("INPUT.FOCUS");
348
- send("INPUT.ARROW_DOWN");
349
- (0, import_dom_query2.raf)(() => {
350
- dom.getInputEl(state.context)?.focus({ preventScroll: true });
351
- });
352
- },
353
- ArrowUp() {
354
- send("INPUT.FOCUS");
355
- send("INPUT.ARROW_UP");
356
- (0, import_dom_query2.raf)(() => {
357
- dom.getInputEl(state.context)?.focus({ preventScroll: true });
358
- });
359
- }
360
- };
361
- const key = (0, import_dom_event.getEventKey)(event, state.context);
362
- const exec = keyMap[key];
363
- if (exec) {
364
- exec(event);
319
+ getTriggerProps(props = {}) {
320
+ return normalize.button({
321
+ ...parts.trigger.attrs,
322
+ dir: state.context.dir,
323
+ id: dom.getTriggerId(state.context),
324
+ "aria-haspopup": composite ? "listbox" : "dialog",
325
+ type: "button",
326
+ tabIndex: props.focusable ? void 0 : -1,
327
+ "aria-label": translations.triggerLabel,
328
+ "aria-expanded": open,
329
+ "data-state": open ? "open" : "closed",
330
+ "aria-controls": open ? dom.getContentId(state.context) : void 0,
331
+ disabled,
332
+ "data-focusable": (0, import_dom_query2.dataAttr)(props.focusable),
333
+ "data-readonly": (0, import_dom_query2.dataAttr)(readOnly),
334
+ "data-disabled": (0, import_dom_query2.dataAttr)(disabled),
335
+ onFocus() {
336
+ if (!props.focusable)
337
+ return;
338
+ send({ type: "INPUT.FOCUS", src: "trigger" });
339
+ },
340
+ onClick(event) {
341
+ if (event.defaultPrevented)
342
+ return;
343
+ const evt = (0, import_dom_event.getNativeEvent)(event);
344
+ if (!interactive)
345
+ return;
346
+ if (!(0, import_dom_event.isLeftClick)(evt))
347
+ return;
348
+ send("TRIGGER.CLICK");
349
+ },
350
+ onPointerDown(event) {
351
+ if (!interactive)
352
+ return;
353
+ if (event.pointerType === "touch")
354
+ return;
365
355
  event.preventDefault();
356
+ queueMicrotask(() => {
357
+ dom.getInputEl(state.context)?.focus({ preventScroll: true });
358
+ });
359
+ },
360
+ onKeyDown(event) {
361
+ if (event.defaultPrevented)
362
+ return;
363
+ if (composite)
364
+ return;
365
+ const keyMap = {
366
+ ArrowDown() {
367
+ send({ type: "INPUT.ARROW_DOWN", src: "trigger" });
368
+ },
369
+ ArrowUp() {
370
+ send({ type: "INPUT.ARROW_UP", src: "trigger" });
371
+ }
372
+ };
373
+ const key = (0, import_dom_event.getEventKey)(event, state.context);
374
+ const exec = keyMap[key];
375
+ if (exec) {
376
+ exec(event);
377
+ event.preventDefault();
378
+ }
366
379
  }
367
- }
368
- }),
380
+ });
381
+ },
369
382
  contentProps: normalize.element({
370
383
  ...parts.content.attrs,
371
384
  dir: state.context.dir,
372
385
  id: dom.getContentId(state.context),
373
- role: isDialogPopup ? "dialog" : "listbox",
386
+ role: !composite ? "dialog" : "listbox",
374
387
  tabIndex: -1,
375
388
  hidden: !open,
376
389
  "data-state": open ? "open" : "closed",
377
390
  "aria-labelledby": dom.getLabelId(state.context),
378
- "aria-multiselectable": state.context.multiple && !isDialogPopup ? true : void 0,
391
+ "aria-multiselectable": state.context.multiple && composite ? true : void 0,
379
392
  onPointerDown(event) {
380
393
  event.preventDefault();
381
394
  }
382
395
  }),
383
- // only used when triggerOnly: true
384
396
  listProps: normalize.element({
385
- id: dom.getListId(state.context),
386
- role: isDialogPopup ? "listbox" : void 0,
387
- "aria-multiselectable": isDialogPopup && state.context.multiple ? true : void 0
397
+ role: !composite ? "listbox" : void 0,
398
+ "aria-labelledby": dom.getLabelId(state.context),
399
+ "aria-multiselectable": state.context.multiple && !composite ? true : void 0
388
400
  }),
389
401
  clearTriggerProps: normalize.button({
390
402
  ...parts.clearTrigger.attrs,
@@ -396,7 +408,12 @@ function connect(state, send, normalize) {
396
408
  "aria-label": translations.clearTriggerLabel,
397
409
  "aria-controls": dom.getInputId(state.context),
398
410
  hidden: !state.context.value.length,
399
- onClick() {
411
+ onPointerDown(event) {
412
+ event.preventDefault();
413
+ },
414
+ onClick(event) {
415
+ if (event.defaultPrevented)
416
+ return;
400
417
  if (!interactive)
401
418
  return;
402
419
  send({ type: "VALUE.CLEAR", src: "clear-trigger" });
@@ -421,6 +438,8 @@ function connect(state, send, normalize) {
421
438
  onPointerMove() {
422
439
  if (itemState.disabled)
423
440
  return;
441
+ if (itemState.highlighted)
442
+ return;
424
443
  send({ type: "ITEM.POINTER_MOVE", value });
425
444
  },
426
445
  onPointerLeave() {
@@ -428,7 +447,7 @@ function connect(state, send, normalize) {
428
447
  return;
429
448
  if (itemState.disabled)
430
449
  return;
431
- const mouseMoved = state.previousEvent.type === "ITEM.POINTER_MOVE";
450
+ const mouseMoved = state.previousEvent.type.includes("POINTER");
432
451
  if (!mouseMoved)
433
452
  return;
434
453
  send({ type: "ITEM.POINTER_LEAVE", value });
@@ -516,8 +535,7 @@ function machine(userContext) {
516
535
  selectionBehavior: "replace",
517
536
  openOnKeyPress: true,
518
537
  openOnChange: true,
519
- dismissable: true,
520
- popup: "listbox",
538
+ composite: true,
521
539
  ...ctx,
522
540
  highlightedItem: null,
523
541
  selectedItems: [],
@@ -546,7 +564,7 @@ function machine(userContext) {
546
564
  watch: {
547
565
  value: ["syncSelectedItems"],
548
566
  inputValue: ["syncInputValue"],
549
- highlightedValue: ["autofillInputValue"],
567
+ highlightedValue: ["syncHighlightedItem", "autofillInputValue"],
550
568
  multiple: ["syncSelectionBehavior"],
551
569
  open: ["toggleVisibility"]
552
570
  },
@@ -584,17 +602,17 @@ function machine(userContext) {
584
602
  "TRIGGER.CLICK": [
585
603
  {
586
604
  guard: "isOpenControlled",
587
- actions: ["focusInput", "highlightFirstSelectedItem", "invokeOnOpen"]
605
+ actions: ["setInitialFocus", "highlightFirstSelectedItem", "invokeOnOpen"]
588
606
  },
589
607
  {
590
608
  target: "interacting",
591
- actions: ["focusInput", "highlightFirstSelectedItem", "invokeOnOpen"]
609
+ actions: ["setInitialFocus", "highlightFirstSelectedItem", "invokeOnOpen"]
592
610
  }
593
611
  ],
594
612
  "INPUT.CLICK": [
595
613
  {
596
614
  guard: "isOpenControlled",
597
- actions: ["invokeOnOpen"]
615
+ actions: ["highlightFirstSelectedItem", "invokeOnOpen"]
598
616
  },
599
617
  {
600
618
  target: "interacting",
@@ -616,13 +634,13 @@ function machine(userContext) {
616
634
  ],
617
635
  "VALUE.CLEAR": {
618
636
  target: "focused",
619
- actions: ["clearInputValue", "clearSelectedItems"]
637
+ actions: ["clearInputValue", "clearSelectedItems", "setInitialFocus"]
620
638
  }
621
639
  }
622
640
  },
623
641
  focused: {
624
642
  tags: ["focused", "closed"],
625
- entry: ["focusInputOrTrigger", "scrollContentToTop", "clearHighlightedItem"],
643
+ entry: ["scrollContentToTop", "clearHighlightedItem"],
626
644
  on: {
627
645
  "CONTROLLED.OPEN": [
628
646
  {
@@ -636,12 +654,12 @@ function machine(userContext) {
636
654
  "INPUT.CHANGE": [
637
655
  {
638
656
  guard: and("isOpenControlled", "openOnChange"),
639
- actions: ["setInputValue", "invokeOnOpen"]
657
+ actions: ["setInputValue", "invokeOnOpen", "highlightFirstItemIfNeeded"]
640
658
  },
641
659
  {
642
660
  guard: "openOnChange",
643
661
  target: "suggesting",
644
- actions: ["setInputValue", "invokeOnOpen"]
662
+ actions: ["setInputValue", "invokeOnOpen", "highlightFirstItemIfNeeded"]
645
663
  },
646
664
  {
647
665
  actions: "setInputValue"
@@ -670,11 +688,11 @@ function machine(userContext) {
670
688
  "TRIGGER.CLICK": [
671
689
  {
672
690
  guard: "isOpenControlled",
673
- actions: ["focusInput", "highlightFirstSelectedItem", "invokeOnOpen"]
691
+ actions: ["setInitialFocus", "highlightFirstSelectedItem", "invokeOnOpen"]
674
692
  },
675
693
  {
676
694
  target: "interacting",
677
- actions: ["focusInput", "highlightFirstSelectedItem", "invokeOnOpen"]
695
+ actions: ["setInitialFocus", "highlightFirstSelectedItem", "invokeOnOpen"]
678
696
  }
679
697
  ],
680
698
  "INPUT.ARROW_DOWN": [
@@ -737,18 +755,14 @@ function machine(userContext) {
737
755
  },
738
756
  interacting: {
739
757
  tags: ["open", "focused"],
740
- activities: [
741
- "scrollIntoView",
742
- "trackDismissableLayer",
743
- "computePlacement",
744
- "hideOtherElements",
745
- "trackContentHeight"
746
- ],
758
+ entry: ["setInitialFocus"],
759
+ activities: ["scrollToHighlightedItem", "trackDismissableLayer", "computePlacement", "hideOtherElements"],
747
760
  on: {
748
761
  "CONTROLLED.CLOSE": [
749
762
  {
750
763
  guard: "restoreFocus",
751
- target: "focused"
764
+ target: "focused",
765
+ actions: ["setFinalFocus"]
752
766
  },
753
767
  {
754
768
  target: "idle"
@@ -786,7 +800,7 @@ function machine(userContext) {
786
800
  {
787
801
  guard: "closeOnSelect",
788
802
  target: "focused",
789
- actions: ["selectHighlightedItem", "invokeOnClose"]
803
+ actions: ["selectHighlightedItem", "invokeOnClose", "setFinalFocus"]
790
804
  },
791
805
  {
792
806
  actions: ["selectHighlightedItem"]
@@ -817,7 +831,7 @@ function machine(userContext) {
817
831
  {
818
832
  guard: "closeOnSelect",
819
833
  target: "focused",
820
- actions: ["selectItem", "invokeOnClose"]
834
+ actions: ["selectItem", "invokeOnClose", "setFinalFocus"]
821
835
  },
822
836
  {
823
837
  actions: ["selectItem"]
@@ -839,7 +853,7 @@ function machine(userContext) {
839
853
  },
840
854
  {
841
855
  target: "focused",
842
- actions: ["invokeOnClose"]
856
+ actions: ["invokeOnClose", "setFinalFocus"]
843
857
  }
844
858
  ],
845
859
  "TRIGGER.CLICK": [
@@ -876,11 +890,11 @@ function machine(userContext) {
876
890
  CLOSE: [
877
891
  {
878
892
  guard: "isOpenControlled",
879
- actions: "invokeOnClose"
893
+ actions: ["invokeOnClose"]
880
894
  },
881
895
  {
882
896
  target: "focused",
883
- actions: "invokeOnClose"
897
+ actions: ["invokeOnClose", "setFinalFocus"]
884
898
  }
885
899
  ],
886
900
  "VALUE.CLEAR": [
@@ -890,7 +904,7 @@ function machine(userContext) {
890
904
  },
891
905
  {
892
906
  target: "focused",
893
- actions: ["clearInputValue", "clearSelectedItems", "invokeOnClose"]
907
+ actions: ["clearInputValue", "clearSelectedItems", "invokeOnClose", "setFinalFocus"]
894
908
  }
895
909
  ]
896
910
  }
@@ -899,18 +913,18 @@ function machine(userContext) {
899
913
  tags: ["open", "focused"],
900
914
  activities: [
901
915
  "trackDismissableLayer",
902
- "scrollIntoView",
916
+ "scrollToHighlightedItem",
903
917
  "computePlacement",
904
918
  "trackChildNodes",
905
- "hideOtherElements",
906
- "trackContentHeight"
919
+ "hideOtherElements"
907
920
  ],
908
- entry: ["focusInput"],
921
+ entry: ["setInitialFocus"],
909
922
  on: {
910
923
  "CONTROLLED.CLOSE": [
911
924
  {
912
925
  guard: "restoreFocus",
913
- target: "focused"
926
+ target: "focused",
927
+ actions: ["setFinalFocus"]
914
928
  },
915
929
  {
916
930
  target: "idle"
@@ -921,11 +935,11 @@ function machine(userContext) {
921
935
  },
922
936
  "INPUT.ARROW_DOWN": {
923
937
  target: "interacting",
924
- actions: "highlightNextItem"
938
+ actions: ["highlightNextItem"]
925
939
  },
926
940
  "INPUT.ARROW_UP": {
927
941
  target: "interacting",
928
- actions: "highlightPrevItem"
942
+ actions: ["highlightPrevItem"]
929
943
  },
930
944
  "INPUT.HOME": {
931
945
  target: "interacting",
@@ -943,7 +957,7 @@ function machine(userContext) {
943
957
  {
944
958
  guard: "closeOnSelect",
945
959
  target: "focused",
946
- actions: ["selectHighlightedItem", "invokeOnClose"]
960
+ actions: ["selectHighlightedItem", "invokeOnClose", "setFinalFocus"]
947
961
  },
948
962
  {
949
963
  actions: ["selectHighlightedItem"]
@@ -961,19 +975,19 @@ function machine(userContext) {
961
975
  "LAYER.ESCAPE": [
962
976
  {
963
977
  guard: "isOpenControlled",
964
- actions: "invokeOnClose"
978
+ actions: ["invokeOnClose"]
965
979
  },
966
980
  {
967
981
  target: "focused",
968
- actions: "invokeOnClose"
982
+ actions: ["invokeOnClose"]
969
983
  }
970
984
  ],
971
985
  "ITEM.POINTER_MOVE": {
972
986
  target: "interacting",
973
- actions: "setHighlightedItem"
987
+ actions: ["setHighlightedItem"]
974
988
  },
975
989
  "ITEM.POINTER_LEAVE": {
976
- actions: "clearHighlightedItem"
990
+ actions: ["clearHighlightedItem"]
977
991
  },
978
992
  "LAYER.INTERACT_OUTSIDE": [
979
993
  // == group 1 ==
@@ -989,21 +1003,21 @@ function machine(userContext) {
989
1003
  // == group 2 ==
990
1004
  {
991
1005
  guard: "isOpenControlled",
992
- actions: "invokeOnClose"
1006
+ actions: ["invokeOnClose"]
993
1007
  },
994
1008
  {
995
1009
  target: "idle",
996
- actions: "invokeOnClose"
1010
+ actions: ["invokeOnClose"]
997
1011
  }
998
1012
  ],
999
1013
  "TRIGGER.CLICK": [
1000
1014
  {
1001
1015
  guard: "isOpenControlled",
1002
- actions: "invokeOnClose"
1016
+ actions: ["invokeOnClose"]
1003
1017
  },
1004
1018
  {
1005
1019
  target: "focused",
1006
- actions: "invokeOnClose"
1020
+ actions: ["invokeOnClose"]
1007
1021
  }
1008
1022
  ],
1009
1023
  "ITEM.CLICK": [
@@ -1014,7 +1028,7 @@ function machine(userContext) {
1014
1028
  {
1015
1029
  guard: "closeOnSelect",
1016
1030
  target: "focused",
1017
- actions: ["selectItem", "invokeOnClose"]
1031
+ actions: ["selectItem", "invokeOnClose", "setFinalFocus"]
1018
1032
  },
1019
1033
  {
1020
1034
  actions: ["selectItem"]
@@ -1023,11 +1037,11 @@ function machine(userContext) {
1023
1037
  CLOSE: [
1024
1038
  {
1025
1039
  guard: "isOpenControlled",
1026
- actions: "invokeOnClose"
1040
+ actions: ["invokeOnClose"]
1027
1041
  },
1028
1042
  {
1029
1043
  target: "focused",
1030
- actions: "invokeOnClose"
1044
+ actions: ["invokeOnClose", "setFinalFocus"]
1031
1045
  }
1032
1046
  ],
1033
1047
  "VALUE.CLEAR": [
@@ -1037,7 +1051,7 @@ function machine(userContext) {
1037
1051
  },
1038
1052
  {
1039
1053
  target: "focused",
1040
- actions: ["clearInputValue", "clearSelectedItems", "invokeOnClose"]
1054
+ actions: ["clearInputValue", "clearSelectedItems", "invokeOnClose", "setFinalFocus"]
1041
1055
  }
1042
1056
  ]
1043
1057
  }
@@ -1066,7 +1080,7 @@ function machine(userContext) {
1066
1080
  },
1067
1081
  activities: {
1068
1082
  trackDismissableLayer(ctx2, _evt, { send }) {
1069
- if (!ctx2.dismissable)
1083
+ if (ctx2.disableLayer)
1070
1084
  return;
1071
1085
  const contentEl = () => dom.getContentEl(ctx2);
1072
1086
  return (0, import_dismissable.trackDismissableElement)(contentEl, {
@@ -1105,56 +1119,42 @@ function machine(userContext) {
1105
1119
  if (!ctx2.autoHighlight)
1106
1120
  return;
1107
1121
  const exec = () => send("CHILDREN_CHANGE");
1108
- (0, import_dom_query3.raf)(() => exec());
1109
1122
  const contentEl = () => dom.getContentEl(ctx2);
1110
1123
  return (0, import_dom_query3.observeChildren)(contentEl, {
1111
1124
  callback: exec,
1112
1125
  defer: true
1113
1126
  });
1114
1127
  },
1115
- scrollIntoView(ctx2, _evt, { getState }) {
1128
+ scrollToHighlightedItem(ctx2, _evt, { getState }) {
1116
1129
  const inputEl = dom.getInputEl(ctx2);
1130
+ let cleanups = [];
1117
1131
  const exec = (immediate) => {
1118
1132
  const state = getState();
1119
- const pointer = state.event.type.startsWith("ITEM.POINTER");
1133
+ const pointer = state.event.type.includes("POINTER");
1120
1134
  if (pointer || !ctx2.highlightedValue)
1121
1135
  return;
1122
- const optionEl = dom.getHighlightedItemEl(ctx2);
1136
+ const itemEl = dom.getHighlightedItemEl(ctx2);
1123
1137
  const contentEl = dom.getContentEl(ctx2);
1124
1138
  if (ctx2.scrollToIndexFn) {
1125
1139
  const highlightedIndex = ctx2.collection.indexOf(ctx2.highlightedValue);
1126
1140
  ctx2.scrollToIndexFn({ index: highlightedIndex, immediate });
1127
1141
  return;
1128
1142
  }
1129
- (0, import_dom_query3.scrollIntoView)(optionEl, { rootEl: contentEl, block: "nearest" });
1143
+ const rafCleanup2 = (0, import_dom_query3.raf)(() => {
1144
+ (0, import_dom_query3.scrollIntoView)(itemEl, { rootEl: contentEl, block: "nearest" });
1145
+ });
1146
+ cleanups.push(rafCleanup2);
1130
1147
  };
1131
- (0, import_dom_query3.raf)(() => exec(true));
1132
- return (0, import_dom_query3.observeAttributes)(inputEl, {
1148
+ const rafCleanup = (0, import_dom_query3.raf)(() => exec(true));
1149
+ cleanups.push(rafCleanup);
1150
+ const observerCleanup = (0, import_dom_query3.observeAttributes)(inputEl, {
1133
1151
  attributes: ["aria-activedescendant"],
1134
1152
  callback: () => exec(false)
1135
1153
  });
1136
- },
1137
- trackContentHeight(ctx2) {
1138
- let cleanup;
1139
- (0, import_dom_query3.raf)(() => {
1140
- const contentEl = dom.getContentEl(ctx2);
1141
- const listboxEl = dom.getListEl(ctx2);
1142
- if (!contentEl || !listboxEl)
1143
- return;
1144
- const win = dom.getWin(ctx2);
1145
- let rafId;
1146
- const observer = new win.ResizeObserver(() => {
1147
- rafId = requestAnimationFrame(() => {
1148
- contentEl.style.setProperty(`--height`, `${listboxEl.offsetHeight}px`);
1149
- });
1150
- });
1151
- observer.observe(contentEl);
1152
- cleanup = () => {
1153
- cancelAnimationFrame(rafId);
1154
- observer.unobserve(contentEl);
1155
- };
1156
- });
1157
- return () => cleanup?.();
1154
+ cleanups.push(observerCleanup);
1155
+ return () => {
1156
+ cleanups.forEach((cleanup) => cleanup());
1157
+ };
1158
1158
  }
1159
1159
  },
1160
1160
  actions: {
@@ -1172,45 +1172,48 @@ function machine(userContext) {
1172
1172
  });
1173
1173
  },
1174
1174
  setHighlightedItem(ctx2, evt) {
1175
- set.highlightedItem(ctx2, evt.value);
1175
+ if (evt.value == null)
1176
+ return;
1177
+ set.highlightedValue(ctx2, evt.value);
1176
1178
  },
1177
1179
  clearHighlightedItem(ctx2) {
1178
- set.highlightedItem(ctx2, null, true);
1180
+ set.highlightedValue(ctx2, null, true);
1179
1181
  },
1180
1182
  selectHighlightedItem(ctx2) {
1181
- set.selectedItem(ctx2, ctx2.highlightedValue);
1183
+ set.value(ctx2, ctx2.highlightedValue);
1182
1184
  },
1183
1185
  selectItem(ctx2, evt) {
1184
- set.selectedItem(ctx2, evt.value);
1186
+ if (evt.value == null)
1187
+ return;
1188
+ set.value(ctx2, evt.value);
1185
1189
  },
1186
1190
  clearItem(ctx2, evt) {
1191
+ if (evt.value == null)
1192
+ return;
1187
1193
  const value = ctx2.value.filter((v) => v !== evt.value);
1188
- set.selectedItems(ctx2, value);
1194
+ set.value(ctx2, value);
1189
1195
  },
1190
- focusInput(ctx2) {
1196
+ setInitialFocus(ctx2) {
1191
1197
  (0, import_dom_query3.raf)(() => {
1192
- if (dom.isInputFocused(ctx2))
1193
- return;
1194
- dom.getInputEl(ctx2)?.focus({ preventScroll: true });
1198
+ dom.focusInputEl(ctx2);
1195
1199
  });
1196
1200
  },
1197
- focusInputOrTrigger(ctx2) {
1198
- queueMicrotask(() => {
1199
- if (ctx2.popup === "dialog") {
1200
- dom.getTriggerEl(ctx2)?.focus({ preventScroll: true });
1201
+ setFinalFocus(ctx2) {
1202
+ (0, import_dom_query3.raf)(() => {
1203
+ const triggerEl = dom.getTriggerEl(ctx2);
1204
+ if (triggerEl?.dataset.focusable == null) {
1205
+ dom.focusInputEl(ctx2);
1201
1206
  } else {
1202
- dom.getInputEl(ctx2)?.focus({ preventScroll: true });
1207
+ dom.focusTriggerEl(ctx2);
1203
1208
  }
1204
1209
  });
1205
1210
  },
1206
- syncInputValue(ctx2, evt) {
1211
+ syncInputValue(ctx2) {
1207
1212
  const inputEl = dom.getInputEl(ctx2);
1208
1213
  if (!inputEl)
1209
1214
  return;
1210
1215
  inputEl.value = ctx2.inputValue;
1211
- (0, import_dom_query3.raf)(() => {
1212
- if (!evt.keypress)
1213
- return;
1216
+ queueMicrotask(() => {
1214
1217
  const { selectionStart, selectionEnd } = inputEl;
1215
1218
  if (Math.abs((selectionEnd ?? 0) - (selectionStart ?? 0)) !== 0)
1216
1219
  return;
@@ -1251,10 +1254,12 @@ function machine(userContext) {
1251
1254
  }
1252
1255
  },
1253
1256
  setSelectedItems(ctx2, evt) {
1254
- set.selectedItems(ctx2, evt.value);
1257
+ if (!(0, import_utils.isArray)(evt.value))
1258
+ return;
1259
+ set.value(ctx2, evt.value);
1255
1260
  },
1256
1261
  clearSelectedItems(ctx2) {
1257
- set.selectedItems(ctx2, []);
1262
+ set.value(ctx2, []);
1258
1263
  },
1259
1264
  scrollContentToTop(ctx2) {
1260
1265
  if (ctx2.scrollToIndexFn) {
@@ -1275,13 +1280,21 @@ function machine(userContext) {
1275
1280
  highlightFirstItem(ctx2) {
1276
1281
  (0, import_dom_query3.raf)(() => {
1277
1282
  const value = ctx2.collection.first();
1278
- set.highlightedItem(ctx2, value);
1283
+ set.highlightedValue(ctx2, value);
1284
+ });
1285
+ },
1286
+ highlightFirstItemIfNeeded(ctx2) {
1287
+ if (!ctx2.autoHighlight)
1288
+ return;
1289
+ (0, import_dom_query3.raf)(() => {
1290
+ const value = ctx2.collection.first();
1291
+ set.highlightedValue(ctx2, value);
1279
1292
  });
1280
1293
  },
1281
1294
  highlightLastItem(ctx2) {
1282
1295
  (0, import_dom_query3.raf)(() => {
1283
1296
  const value = ctx2.collection.last();
1284
- set.highlightedItem(ctx2, value);
1297
+ set.highlightedValue(ctx2, value);
1285
1298
  });
1286
1299
  },
1287
1300
  highlightNextItem(ctx2) {
@@ -1293,7 +1306,7 @@ function machine(userContext) {
1293
1306
  } else {
1294
1307
  value = ctx2.collection.first();
1295
1308
  }
1296
- set.highlightedItem(ctx2, value);
1309
+ set.highlightedValue(ctx2, value);
1297
1310
  },
1298
1311
  highlightPrevItem(ctx2) {
1299
1312
  let value = null;
@@ -1304,12 +1317,12 @@ function machine(userContext) {
1304
1317
  } else {
1305
1318
  value = ctx2.collection.last();
1306
1319
  }
1307
- set.highlightedItem(ctx2, value);
1320
+ set.highlightedValue(ctx2, value);
1308
1321
  },
1309
1322
  highlightFirstSelectedItem(ctx2) {
1310
1323
  (0, import_dom_query3.raf)(() => {
1311
1324
  const [value] = ctx2.collection.sort(ctx2.value);
1312
- set.highlightedItem(ctx2, value);
1325
+ set.highlightedValue(ctx2, value);
1313
1326
  });
1314
1327
  },
1315
1328
  highlightFirstOrSelectedItem(ctx2) {
@@ -1320,7 +1333,7 @@ function machine(userContext) {
1320
1333
  } else {
1321
1334
  value = ctx2.collection.first();
1322
1335
  }
1323
- set.highlightedItem(ctx2, value);
1336
+ set.highlightedValue(ctx2, value);
1324
1337
  });
1325
1338
  },
1326
1339
  highlightLastOrSelectedItem(ctx2) {
@@ -1331,7 +1344,7 @@ function machine(userContext) {
1331
1344
  } else {
1332
1345
  value = ctx2.collection.last();
1333
1346
  }
1334
- set.highlightedItem(ctx2, value);
1347
+ set.highlightedValue(ctx2, value);
1335
1348
  });
1336
1349
  },
1337
1350
  autofillInputValue(ctx2, evt) {
@@ -1347,13 +1360,10 @@ function machine(userContext) {
1347
1360
  ctx2.collection = evt.value;
1348
1361
  },
1349
1362
  syncSelectedItems(ctx2) {
1350
- const prevSelectedItems = ctx2.selectedItems;
1351
- ctx2.selectedItems = ctx2.value.map((v) => {
1352
- const foundItem = prevSelectedItems.find((item) => ctx2.collection.itemToValue(item) === v);
1353
- if (foundItem)
1354
- return foundItem;
1355
- return ctx2.collection.item(v);
1356
- });
1363
+ sync.valueChange(ctx2);
1364
+ },
1365
+ syncHighlightedItem(ctx2) {
1366
+ sync.highlightChange(ctx2);
1357
1367
  },
1358
1368
  toggleVisibility(ctx2, evt, { send }) {
1359
1369
  send({ type: ctx2.open ? "CONTROLLED.OPEN" : "CONTROLLED.CLOSE", previousEvent: evt });
@@ -1362,12 +1372,8 @@ function machine(userContext) {
1362
1372
  }
1363
1373
  );
1364
1374
  }
1365
- var invoke = {
1375
+ var sync = {
1366
1376
  valueChange: (ctx) => {
1367
- ctx.onValueChange?.({
1368
- value: Array.from(ctx.value),
1369
- items: ctx.selectedItems
1370
- });
1371
1377
  const prevSelectedItems = ctx.selectedItems;
1372
1378
  ctx.selectedItems = ctx.value.map((v) => {
1373
1379
  const foundItem = prevSelectedItems.find((item) => ctx.collection.itemToValue(item) === v);
@@ -1377,36 +1383,47 @@ var invoke = {
1377
1383
  });
1378
1384
  const valueAsString = ctx.collection.itemsToString(ctx.selectedItems);
1379
1385
  ctx.valueAsString = valueAsString;
1380
- let nextInputValue;
1386
+ let inputValue;
1381
1387
  if (ctx.getSelectionValue) {
1382
- nextInputValue = ctx.getSelectionValue({
1388
+ inputValue = ctx.getSelectionValue({
1383
1389
  inputValue: ctx.inputValue,
1384
1390
  selectedItems: Array.from(ctx.selectedItems),
1385
1391
  valueAsString
1386
1392
  });
1387
1393
  } else {
1388
- nextInputValue = (0, import_utils.match)(ctx.selectionBehavior, {
1394
+ inputValue = (0, import_utils.match)(ctx.selectionBehavior, {
1389
1395
  replace: ctx.valueAsString,
1390
1396
  preserve: ctx.inputValue,
1391
1397
  clear: ""
1392
1398
  });
1393
1399
  }
1394
- ctx.inputValue = nextInputValue;
1395
- invoke.inputChange(ctx);
1400
+ set.inputValue(ctx, inputValue);
1396
1401
  },
1397
1402
  highlightChange: (ctx) => {
1403
+ ctx.highlightedItem = ctx.collection.item(ctx.highlightedValue);
1404
+ }
1405
+ };
1406
+ var invoke = {
1407
+ valueChange: (ctx) => {
1408
+ sync.valueChange(ctx);
1409
+ ctx.onValueChange?.({
1410
+ value: Array.from(ctx.value),
1411
+ items: Array.from(ctx.selectedItems)
1412
+ });
1413
+ },
1414
+ highlightChange: (ctx) => {
1415
+ sync.highlightChange(ctx);
1398
1416
  ctx.onHighlightChange?.({
1399
1417
  highlightedValue: ctx.highlightedValue,
1400
- highligtedItem: ctx.highlightedItem
1418
+ highlightedItem: ctx.highlightedItem
1401
1419
  });
1402
- ctx.highlightedItem = ctx.collection.item(ctx.highlightedValue);
1403
1420
  },
1404
1421
  inputChange: (ctx) => {
1405
1422
  ctx.onInputValueChange?.({ inputValue: ctx.inputValue });
1406
1423
  }
1407
1424
  };
1408
1425
  var set = {
1409
- selectedItem: (ctx, value, force = false) => {
1426
+ value: (ctx, value, force = false) => {
1410
1427
  if ((0, import_utils.isEqual)(ctx.value, value))
1411
1428
  return;
1412
1429
  if (value == null && !force)
@@ -1416,16 +1433,14 @@ var set = {
1416
1433
  invoke.valueChange(ctx);
1417
1434
  return;
1418
1435
  }
1419
- ctx.value = ctx.multiple ? (0, import_utils.addOrRemove)(ctx.value, value) : [value];
1420
- invoke.valueChange(ctx);
1421
- },
1422
- selectedItems: (ctx, value) => {
1423
- if ((0, import_utils.isEqual)(ctx.value, value))
1424
- return;
1425
- ctx.value = value;
1436
+ if ((0, import_utils.isArray)(value)) {
1437
+ ctx.value = value;
1438
+ } else if (value != null) {
1439
+ ctx.value = ctx.multiple ? (0, import_utils.addOrRemove)(ctx.value, value) : [value];
1440
+ }
1426
1441
  invoke.valueChange(ctx);
1427
1442
  },
1428
- highlightedItem: (ctx, value, force = false) => {
1443
+ highlightedValue: (ctx, value, force = false) => {
1429
1444
  if ((0, import_utils.isEqual)(ctx.highlightedValue, value))
1430
1445
  return;
1431
1446
  if (!value && !force)