@zag-js/combobox 1.18.0 → 1.18.1

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.d.mts CHANGED
@@ -24,11 +24,21 @@ interface HighlightChangeDetails<T extends CollectionItem = CollectionItem> {
24
24
  highlightedValue: string | null;
25
25
  highlightedItem: T | null;
26
26
  }
27
+ /**
28
+ * The reason for the input value change
29
+ */
30
+ type InputValueChangeReason = "input-change" | "item-select" | "clear-trigger" | "script" | "interact-outside";
27
31
  interface InputValueChangeDetails {
28
32
  inputValue: string;
33
+ reason?: InputValueChangeReason | undefined;
29
34
  }
35
+ /**
36
+ * The reason for the combobox open/close state change
37
+ */
38
+ type OpenChangeReason = "input-click" | "trigger-click" | "script" | "arrow-key" | "input-change" | "interact-outside" | "escape-key" | "item-select" | "clear-trigger";
30
39
  interface OpenChangeDetails {
31
40
  open: boolean;
41
+ reason?: OpenChangeReason | undefined;
32
42
  }
33
43
  interface ScrollToIndexDetails {
34
44
  index: number;
@@ -336,6 +346,10 @@ interface ComboboxApi<T extends PropTypes = PropTypes, V extends CollectionItem
336
346
  * The value of the combobox input
337
347
  */
338
348
  setHighlightValue(value: string): void;
349
+ /**
350
+ * Function to clear the highlighted value
351
+ */
352
+ clearHighlightValue(): void;
339
353
  /**
340
354
  * Function to sync the selected items with the value.
341
355
  * Useful when `value` is updated from async sources.
@@ -376,7 +390,7 @@ interface ComboboxApi<T extends PropTypes = PropTypes, V extends CollectionItem
376
390
  /**
377
391
  * Function to set the input value of the combobox
378
392
  */
379
- setInputValue(value: string): void;
393
+ setInputValue(value: string, reason?: InputValueChangeReason): void;
380
394
  /**
381
395
  * Returns the state of a combobox item
382
396
  */
@@ -384,7 +398,7 @@ interface ComboboxApi<T extends PropTypes = PropTypes, V extends CollectionItem
384
398
  /**
385
399
  * Function to open or close the combobox
386
400
  */
387
- setOpen(open: boolean): void;
401
+ setOpen(open: boolean, reason?: OpenChangeReason): void;
388
402
  /**
389
403
  * Function to toggle the combobox
390
404
  */
@@ -430,4 +444,4 @@ declare const splitItemGroupProps: <Props extends ItemGroupProps>(props: Props)
430
444
  declare const itemProps: (keyof ItemProps)[];
431
445
  declare const splitItemProps: <Props extends ItemProps>(props: Props) => [ItemProps, Omit<Props, keyof ItemProps>];
432
446
 
433
- export { type ComboboxApi as Api, type ElementIds, type HighlightChangeDetails, type InputValueChangeDetails, type IntlTranslations, type ItemGroupLabelProps, type ItemGroupProps, type ItemProps, type ItemState, type ComboboxMachine as Machine, type NavigateDetails, type OpenChangeDetails, type ComboboxProps as Props, type ScrollToIndexDetails, type SelectionDetails, type ComboboxService as Service, type TriggerProps, type ValueChangeDetails, anatomy, collection, connect, itemGroupLabelProps, itemGroupProps, itemProps, machine, props, splitItemGroupLabelProps, splitItemGroupProps, splitItemProps, splitProps };
447
+ export { type ComboboxApi as Api, type ElementIds, type HighlightChangeDetails, type InputValueChangeDetails, type InputValueChangeReason, type IntlTranslations, type ItemGroupLabelProps, type ItemGroupProps, type ItemProps, type ItemState, type ComboboxMachine as Machine, type NavigateDetails, type OpenChangeDetails, type OpenChangeReason, type ComboboxProps as Props, type ScrollToIndexDetails, type SelectionDetails, type ComboboxService as Service, type TriggerProps, type ValueChangeDetails, anatomy, collection, connect, itemGroupLabelProps, itemGroupProps, itemProps, machine, props, splitItemGroupLabelProps, splitItemGroupProps, splitItemProps, splitProps };
package/dist/index.d.ts CHANGED
@@ -24,11 +24,21 @@ interface HighlightChangeDetails<T extends CollectionItem = CollectionItem> {
24
24
  highlightedValue: string | null;
25
25
  highlightedItem: T | null;
26
26
  }
27
+ /**
28
+ * The reason for the input value change
29
+ */
30
+ type InputValueChangeReason = "input-change" | "item-select" | "clear-trigger" | "script" | "interact-outside";
27
31
  interface InputValueChangeDetails {
28
32
  inputValue: string;
33
+ reason?: InputValueChangeReason | undefined;
29
34
  }
35
+ /**
36
+ * The reason for the combobox open/close state change
37
+ */
38
+ type OpenChangeReason = "input-click" | "trigger-click" | "script" | "arrow-key" | "input-change" | "interact-outside" | "escape-key" | "item-select" | "clear-trigger";
30
39
  interface OpenChangeDetails {
31
40
  open: boolean;
41
+ reason?: OpenChangeReason | undefined;
32
42
  }
33
43
  interface ScrollToIndexDetails {
34
44
  index: number;
@@ -336,6 +346,10 @@ interface ComboboxApi<T extends PropTypes = PropTypes, V extends CollectionItem
336
346
  * The value of the combobox input
337
347
  */
338
348
  setHighlightValue(value: string): void;
349
+ /**
350
+ * Function to clear the highlighted value
351
+ */
352
+ clearHighlightValue(): void;
339
353
  /**
340
354
  * Function to sync the selected items with the value.
341
355
  * Useful when `value` is updated from async sources.
@@ -376,7 +390,7 @@ interface ComboboxApi<T extends PropTypes = PropTypes, V extends CollectionItem
376
390
  /**
377
391
  * Function to set the input value of the combobox
378
392
  */
379
- setInputValue(value: string): void;
393
+ setInputValue(value: string, reason?: InputValueChangeReason): void;
380
394
  /**
381
395
  * Returns the state of a combobox item
382
396
  */
@@ -384,7 +398,7 @@ interface ComboboxApi<T extends PropTypes = PropTypes, V extends CollectionItem
384
398
  /**
385
399
  * Function to open or close the combobox
386
400
  */
387
- setOpen(open: boolean): void;
401
+ setOpen(open: boolean, reason?: OpenChangeReason): void;
388
402
  /**
389
403
  * Function to toggle the combobox
390
404
  */
@@ -430,4 +444,4 @@ declare const splitItemGroupProps: <Props extends ItemGroupProps>(props: Props)
430
444
  declare const itemProps: (keyof ItemProps)[];
431
445
  declare const splitItemProps: <Props extends ItemProps>(props: Props) => [ItemProps, Omit<Props, keyof ItemProps>];
432
446
 
433
- export { type ComboboxApi as Api, type ElementIds, type HighlightChangeDetails, type InputValueChangeDetails, type IntlTranslations, type ItemGroupLabelProps, type ItemGroupProps, type ItemProps, type ItemState, type ComboboxMachine as Machine, type NavigateDetails, type OpenChangeDetails, type ComboboxProps as Props, type ScrollToIndexDetails, type SelectionDetails, type ComboboxService as Service, type TriggerProps, type ValueChangeDetails, anatomy, collection, connect, itemGroupLabelProps, itemGroupProps, itemProps, machine, props, splitItemGroupLabelProps, splitItemGroupProps, splitItemProps, splitProps };
447
+ export { type ComboboxApi as Api, type ElementIds, type HighlightChangeDetails, type InputValueChangeDetails, type InputValueChangeReason, type IntlTranslations, type ItemGroupLabelProps, type ItemGroupProps, type ItemProps, type ItemState, type ComboboxMachine as Machine, type NavigateDetails, type OpenChangeDetails, type OpenChangeReason, type ComboboxProps as Props, type ScrollToIndexDetails, type SelectionDetails, type ComboboxService as Service, type TriggerProps, type ValueChangeDetails, anatomy, collection, connect, itemGroupLabelProps, itemGroupProps, itemProps, machine, props, splitItemGroupLabelProps, splitItemGroupProps, splitItemProps, splitProps };
package/dist/index.js CHANGED
@@ -117,14 +117,17 @@ function connect(service, normalize) {
117
117
  setHighlightValue(value) {
118
118
  send({ type: "HIGHLIGHTED_VALUE.SET", value });
119
119
  },
120
+ clearHighlightValue() {
121
+ send({ type: "HIGHLIGHTED_VALUE.CLEAR" });
122
+ },
120
123
  selectValue(value) {
121
124
  send({ type: "ITEM.SELECT", value });
122
125
  },
123
126
  setValue(value) {
124
127
  send({ type: "VALUE.SET", value });
125
128
  },
126
- setInputValue(value) {
127
- send({ type: "INPUT_VALUE.SET", value });
129
+ setInputValue(value, reason = "script") {
130
+ send({ type: "INPUT_VALUE.SET", value, src: reason });
128
131
  },
129
132
  clearValue(value) {
130
133
  if (value != null) {
@@ -136,10 +139,10 @@ function connect(service, normalize) {
136
139
  focus() {
137
140
  getInputEl(scope)?.focus();
138
141
  },
139
- setOpen(nextOpen) {
142
+ setOpen(nextOpen, reason = "script") {
140
143
  const open2 = state.hasTag("open");
141
144
  if (open2 === nextOpen) return;
142
- send({ type: nextOpen ? "OPEN" : "CLOSE" });
145
+ send({ type: nextOpen ? "OPEN" : "CLOSE", src: reason });
143
146
  },
144
147
  getRootProps() {
145
148
  return normalize.element({
@@ -216,7 +219,7 @@ function connect(service, normalize) {
216
219
  if (event2.defaultPrevented) return;
217
220
  if (!prop("openOnClick")) return;
218
221
  if (!interactive) return;
219
- send({ type: "INPUT.CLICK" });
222
+ send({ type: "INPUT.CLICK", src: "input-click" });
220
223
  },
221
224
  onFocus() {
222
225
  if (disabled) return;
@@ -227,7 +230,7 @@ function connect(service, normalize) {
227
230
  send({ type: "INPUT.BLUR" });
228
231
  },
229
232
  onChange(event2) {
230
- send({ type: "INPUT.CHANGE", value: event2.currentTarget.value });
233
+ send({ type: "INPUT.CHANGE", value: event2.currentTarget.value, src: "input-change" });
231
234
  },
232
235
  onKeyDown(event2) {
233
236
  if (event2.defaultPrevented) return;
@@ -239,12 +242,12 @@ function connect(service, normalize) {
239
242
  const keymap = {
240
243
  ArrowDown(event3) {
241
244
  if (!openOnKeyPress && !open) return;
242
- send({ type: event3.altKey ? "OPEN" : "INPUT.ARROW_DOWN", keypress });
245
+ send({ type: event3.altKey ? "OPEN" : "INPUT.ARROW_DOWN", keypress, src: "arrow-key" });
243
246
  event3.preventDefault();
244
247
  },
245
248
  ArrowUp() {
246
249
  if (!openOnKeyPress && !open) return;
247
- send({ type: event2.altKey ? "CLOSE" : "INPUT.ARROW_UP", keypress });
250
+ send({ type: event2.altKey ? "CLOSE" : "INPUT.ARROW_UP", keypress, src: "arrow-key" });
248
251
  event2.preventDefault();
249
252
  },
250
253
  Home(event3) {
@@ -262,7 +265,7 @@ function connect(service, normalize) {
262
265
  }
263
266
  },
264
267
  Enter(event3) {
265
- send({ type: "INPUT.ENTER", keypress });
268
+ send({ type: "INPUT.ENTER", keypress, src: "item-select" });
266
269
  const submittable = computed("isCustomValue") && prop("allowCustomValue");
267
270
  if (open && !submittable) {
268
271
  event3.preventDefault();
@@ -274,7 +277,7 @@ function connect(service, normalize) {
274
277
  }
275
278
  },
276
279
  Escape() {
277
- send({ type: "INPUT.ESCAPE", keypress });
280
+ send({ type: "INPUT.ESCAPE", keypress, src: "escape-key" });
278
281
  event2.preventDefault();
279
282
  }
280
283
  };
@@ -309,7 +312,7 @@ function connect(service, normalize) {
309
312
  if (event2.defaultPrevented) return;
310
313
  if (!interactive) return;
311
314
  if (!domQuery.isLeftClick(event2)) return;
312
- send({ type: "TRIGGER.CLICK" });
315
+ send({ type: "TRIGGER.CLICK", src: "trigger-click" });
313
316
  },
314
317
  onPointerDown(event2) {
315
318
  if (!interactive) return;
@@ -325,10 +328,10 @@ function connect(service, normalize) {
325
328
  if (composite) return;
326
329
  const keyMap = {
327
330
  ArrowDown() {
328
- send({ type: "INPUT.ARROW_DOWN", src: "trigger" });
331
+ send({ type: "INPUT.ARROW_DOWN", src: "arrow-key" });
329
332
  },
330
333
  ArrowUp() {
331
- send({ type: "INPUT.ARROW_UP", src: "trigger" });
334
+ send({ type: "INPUT.ARROW_UP", src: "arrow-key" });
332
335
  }
333
336
  };
334
337
  const key = domQuery.getEventKey(event2, { dir: prop("dir") });
@@ -425,7 +428,7 @@ function connect(service, normalize) {
425
428
  if (domQuery.isOpeningInNewTab(event2)) return;
426
429
  if (domQuery.isContextMenuEvent(event2)) return;
427
430
  if (itemState.disabled) return;
428
- send({ type: "ITEM.CLICK", src: "click", value });
431
+ send({ type: "ITEM.CLICK", src: "item-select", value });
429
432
  }
430
433
  });
431
434
  },
@@ -507,7 +510,7 @@ var machine = createMachine({
507
510
  const open = prop("open") || prop("defaultOpen");
508
511
  return open ? "suggesting" : "idle";
509
512
  },
510
- context({ prop, bindable, getContext }) {
513
+ context({ prop, bindable, getContext, getEvent }) {
511
514
  return {
512
515
  currentPlacement: bindable(() => ({
513
516
  defaultValue: void 0
@@ -554,7 +557,9 @@ var machine = createMachine({
554
557
  defaultValue: inputValue,
555
558
  value: prop("inputValue"),
556
559
  onChange(value2) {
557
- prop("onInputValueChange")?.({ inputValue: value2 });
560
+ const event = getEvent();
561
+ const reason = (event.previousEvent || event).src;
562
+ prop("onInputValueChange")?.({ inputValue: value2, reason });
558
563
  }
559
564
  };
560
565
  }),
@@ -579,7 +584,7 @@ var machine = createMachine({
579
584
  valueAsString: ({ context, prop }) => prop("collection").stringifyItems(context.get("selectedItems")),
580
585
  isCustomValue: ({ context, computed }) => context.get("inputValue") !== computed("valueAsString")
581
586
  },
582
- watch({ context, prop, track, action }) {
587
+ watch({ context, prop, track, action, send }) {
583
588
  track([() => context.hash("value")], () => {
584
589
  action(["syncSelectedItems"]);
585
590
  });
@@ -592,13 +597,19 @@ var machine = createMachine({
592
597
  track([() => prop("open")], () => {
593
598
  action(["toggleVisibility"]);
594
599
  });
600
+ track([() => prop("collection").toString()], () => {
601
+ send({ type: "CHILDREN_CHANGE" });
602
+ });
595
603
  },
596
604
  on: {
597
605
  "SELECTED_ITEMS.SYNC": {
598
606
  actions: ["syncSelectedItems"]
599
607
  },
600
608
  "HIGHLIGHTED_VALUE.SET": {
601
- actions: ["setHighlightedItem"]
609
+ actions: ["setHighlightedValue"]
610
+ },
611
+ "HIGHLIGHTED_VALUE.CLEAR": {
612
+ actions: ["clearHighlightedValue"]
602
613
  },
603
614
  "ITEM.SELECT": {
604
615
  actions: ["selectItem"]
@@ -625,7 +636,7 @@ var machine = createMachine({
625
636
  states: {
626
637
  idle: {
627
638
  tags: ["idle", "closed"],
628
- entry: ["scrollContentToTop", "clearHighlightedItem"],
639
+ entry: ["scrollContentToTop", "clearHighlightedValue"],
629
640
  on: {
630
641
  "CONTROLLED.OPEN": {
631
642
  target: "interacting"
@@ -671,7 +682,7 @@ var machine = createMachine({
671
682
  },
672
683
  focused: {
673
684
  tags: ["focused", "closed"],
674
- entry: ["scrollContentToTop", "clearHighlightedItem"],
685
+ entry: ["scrollContentToTop", "clearHighlightedValue"],
675
686
  on: {
676
687
  "CONTROLLED.OPEN": [
677
688
  {
@@ -799,6 +810,15 @@ var machine = createMachine({
799
810
  target: "idle"
800
811
  }
801
812
  ],
813
+ CHILDREN_CHANGE: [
814
+ {
815
+ guard: "isHighlightedItemRemoved",
816
+ actions: ["clearHighlightedValue"]
817
+ },
818
+ {
819
+ actions: ["scrollToHighlightedItem"]
820
+ }
821
+ ],
802
822
  "INPUT.HOME": {
803
823
  actions: ["highlightFirstItem"]
804
824
  },
@@ -808,7 +828,7 @@ var machine = createMachine({
808
828
  "INPUT.ARROW_DOWN": [
809
829
  {
810
830
  guard: and("autoComplete", "isLastItemHighlighted"),
811
- actions: ["clearHighlightedItem", "scrollContentToTop"]
831
+ actions: ["clearHighlightedValue", "scrollContentToTop"]
812
832
  },
813
833
  {
814
834
  actions: ["highlightNextItem"]
@@ -817,7 +837,7 @@ var machine = createMachine({
817
837
  "INPUT.ARROW_UP": [
818
838
  {
819
839
  guard: and("autoComplete", "isFirstItemHighlighted"),
820
- actions: ["clearHighlightedItem"]
840
+ actions: ["clearHighlightedValue"]
821
841
  },
822
842
  {
823
843
  actions: ["highlightPrevItem"]
@@ -856,14 +876,14 @@ var machine = createMachine({
856
876
  },
857
877
  {
858
878
  target: "suggesting",
859
- actions: ["clearHighlightedItem", "setInputValue"]
879
+ actions: ["clearHighlightedValue", "setInputValue"]
860
880
  }
861
881
  ],
862
882
  "ITEM.POINTER_MOVE": {
863
- actions: ["setHighlightedItem"]
883
+ actions: ["setHighlightedValue"]
864
884
  },
865
885
  "ITEM.POINTER_LEAVE": {
866
- actions: ["clearHighlightedItem"]
886
+ actions: ["clearHighlightedValue"]
867
887
  },
868
888
  "ITEM.CLICK": [
869
889
  {
@@ -953,13 +973,7 @@ var machine = createMachine({
953
973
  },
954
974
  suggesting: {
955
975
  tags: ["open", "focused"],
956
- effects: [
957
- "trackDismissableLayer",
958
- "scrollToHighlightedItem",
959
- "trackPlacement",
960
- "trackChildNodes",
961
- "hideOtherElements"
962
- ],
976
+ effects: ["trackDismissableLayer", "scrollToHighlightedItem", "trackPlacement", "hideOtherElements"],
963
977
  entry: ["setInitialFocus"],
964
978
  on: {
965
979
  "CONTROLLED.CLOSE": [
@@ -972,10 +986,16 @@ var machine = createMachine({
972
986
  target: "idle"
973
987
  }
974
988
  ],
975
- CHILDREN_CHANGE: {
976
- guard: "autoHighlight",
977
- actions: ["highlightFirstItem"]
978
- },
989
+ CHILDREN_CHANGE: [
990
+ {
991
+ guard: "autoHighlight",
992
+ actions: ["highlightFirstItem"]
993
+ },
994
+ {
995
+ guard: "isHighlightedItemRemoved",
996
+ actions: ["clearHighlightedValue"]
997
+ }
998
+ ],
979
999
  "INPUT.ARROW_DOWN": {
980
1000
  target: "interacting",
981
1001
  actions: ["highlightNextItem"]
@@ -1032,10 +1052,10 @@ var machine = createMachine({
1032
1052
  ],
1033
1053
  "ITEM.POINTER_MOVE": {
1034
1054
  target: "interacting",
1035
- actions: ["setHighlightedItem"]
1055
+ actions: ["setHighlightedValue"]
1036
1056
  },
1037
1057
  "ITEM.POINTER_LEAVE": {
1038
- actions: ["clearHighlightedItem"]
1058
+ actions: ["clearHighlightedValue"]
1039
1059
  },
1040
1060
  "LAYER.INTERACT_OUTSIDE": [
1041
1061
  // == group 1 ==
@@ -1124,7 +1144,8 @@ var machine = createMachine({
1124
1144
  },
1125
1145
  restoreFocus: ({ event }) => event.restoreFocus == null ? true : !!event.restoreFocus,
1126
1146
  isChangeEvent: ({ event }) => event.previousEvent?.type === "INPUT.CHANGE",
1127
- autoFocus: ({ prop }) => !!prop("autoFocus")
1147
+ autoFocus: ({ prop }) => !!prop("autoFocus"),
1148
+ isHighlightedItemRemoved: ({ prop, context }) => !prop("collection").has(context.get("highlightedValue"))
1128
1149
  },
1129
1150
  effects: {
1130
1151
  trackDismissableLayer({ send, prop, scope }) {
@@ -1139,10 +1160,10 @@ var machine = createMachine({
1139
1160
  onEscapeKeyDown(event) {
1140
1161
  event.preventDefault();
1141
1162
  event.stopPropagation();
1142
- send({ type: "LAYER.ESCAPE" });
1163
+ send({ type: "LAYER.ESCAPE", src: "escape-key" });
1143
1164
  },
1144
1165
  onDismiss() {
1145
- send({ type: "LAYER.INTERACT_OUTSIDE", restoreFocus: false });
1166
+ send({ type: "LAYER.INTERACT_OUTSIDE", src: "interact-outside", restoreFocus: false });
1146
1167
  }
1147
1168
  });
1148
1169
  },
@@ -1166,16 +1187,6 @@ var machine = createMachine({
1166
1187
  }
1167
1188
  });
1168
1189
  },
1169
- // in event the options are fetched (async), we still want to auto-highlight the first option
1170
- trackChildNodes({ scope, computed, send }) {
1171
- if (!computed("autoHighlight")) return;
1172
- const exec = () => send({ type: "CHILDREN_CHANGE" });
1173
- const contentEl = () => getContentEl(scope);
1174
- return domQuery.observeChildren(contentEl, {
1175
- callback: exec,
1176
- defer: true
1177
- });
1178
- },
1179
1190
  scrollToHighlightedItem({ context, prop, scope, event }) {
1180
1191
  const inputEl = getInputEl(scope);
1181
1192
  let cleanups = [];
@@ -1222,23 +1233,42 @@ var machine = createMachine({
1222
1233
  }
1223
1234
  });
1224
1235
  },
1225
- setHighlightedItem(params) {
1226
- const { context, event } = params;
1236
+ setHighlightedValue({ context, event }) {
1227
1237
  if (event.value == null) return;
1228
1238
  context.set("highlightedValue", event.value);
1229
1239
  },
1230
- clearHighlightedItem(params) {
1231
- const { context } = params;
1240
+ clearHighlightedValue({ context }) {
1232
1241
  context.set("highlightedValue", null);
1233
1242
  },
1234
1243
  selectHighlightedItem(params) {
1235
1244
  const { context, prop } = params;
1245
+ const collection2 = prop("collection");
1236
1246
  const highlightedValue = context.get("highlightedValue");
1237
- if (!highlightedValue) return;
1247
+ if (!highlightedValue || !collection2.has(highlightedValue)) return;
1238
1248
  const nextValue = prop("multiple") ? utils.addOrRemove(context.get("value"), highlightedValue) : [highlightedValue];
1239
1249
  prop("onSelect")?.({ value: nextValue, itemValue: highlightedValue });
1240
1250
  context.set("value", nextValue);
1241
- context.set("inputValue", getInputValue(params));
1251
+ const inputValue = utils.match(prop("selectionBehavior"), {
1252
+ preserve: context.get("inputValue"),
1253
+ replace: collection2.stringifyMany(nextValue),
1254
+ clear: ""
1255
+ });
1256
+ context.set("inputValue", inputValue);
1257
+ },
1258
+ scrollToHighlightedItem({ context, prop, scope }) {
1259
+ domQuery.nextTick(() => {
1260
+ const highlightedValue = context.get("highlightedValue");
1261
+ if (highlightedValue == null) return;
1262
+ const itemEl = getItemEl(scope, highlightedValue);
1263
+ const contentEl = getContentEl(scope);
1264
+ const scrollToIndexFn = prop("scrollToIndexFn");
1265
+ if (scrollToIndexFn) {
1266
+ const highlightedIndex = prop("collection").indexOf(highlightedValue);
1267
+ scrollToIndexFn({ index: highlightedIndex, immediate: true });
1268
+ return;
1269
+ }
1270
+ domQuery.scrollIntoView(itemEl, { rootEl: contentEl, block: "nearest" });
1271
+ });
1242
1272
  },
1243
1273
  selectItem(params) {
1244
1274
  const { context, event, flush, prop } = params;
@@ -1247,16 +1277,26 @@ var machine = createMachine({
1247
1277
  const nextValue = prop("multiple") ? utils.addOrRemove(context.get("value"), event.value) : [event.value];
1248
1278
  prop("onSelect")?.({ value: nextValue, itemValue: event.value });
1249
1279
  context.set("value", nextValue);
1250
- context.set("inputValue", getInputValue(params));
1280
+ const inputValue = utils.match(prop("selectionBehavior"), {
1281
+ preserve: context.get("inputValue"),
1282
+ replace: prop("collection").stringifyMany(nextValue),
1283
+ clear: ""
1284
+ });
1285
+ context.set("inputValue", inputValue);
1251
1286
  });
1252
1287
  },
1253
1288
  clearItem(params) {
1254
- const { context, event, flush } = params;
1289
+ const { context, event, flush, prop } = params;
1255
1290
  if (event.value == null) return;
1256
1291
  flush(() => {
1257
1292
  const nextValue = utils.remove(context.get("value"), event.value);
1258
1293
  context.set("value", nextValue);
1259
- context.set("inputValue", getInputValue(params));
1294
+ const inputValue = utils.match(prop("selectionBehavior"), {
1295
+ preserve: context.get("inputValue"),
1296
+ replace: prop("collection").stringifyMany(nextValue),
1297
+ clear: ""
1298
+ });
1299
+ context.set("inputValue", inputValue);
1260
1300
  });
1261
1301
  },
1262
1302
  setInitialFocus({ scope }) {
@@ -1299,17 +1339,27 @@ var machine = createMachine({
1299
1339
  context.set("inputValue", inputValue);
1300
1340
  },
1301
1341
  setValue(params) {
1302
- const { context, flush, event } = params;
1342
+ const { context, flush, event, prop } = params;
1303
1343
  flush(() => {
1304
1344
  context.set("value", event.value);
1305
- context.set("inputValue", getInputValue(params));
1345
+ const inputValue = utils.match(prop("selectionBehavior"), {
1346
+ preserve: context.get("inputValue"),
1347
+ replace: prop("collection").stringifyMany(event.value),
1348
+ clear: ""
1349
+ });
1350
+ context.set("inputValue", inputValue);
1306
1351
  });
1307
1352
  },
1308
1353
  clearSelectedItems(params) {
1309
- const { context, flush } = params;
1354
+ const { context, flush, prop } = params;
1310
1355
  flush(() => {
1311
1356
  context.set("value", []);
1312
- context.set("inputValue", getInputValue(params));
1357
+ const inputValue = utils.match(prop("selectionBehavior"), {
1358
+ preserve: context.get("inputValue"),
1359
+ replace: prop("collection").stringifyMany([]),
1360
+ clear: ""
1361
+ });
1362
+ context.set("inputValue", inputValue);
1313
1363
  });
1314
1364
  },
1315
1365
  scrollContentToTop({ prop, scope }) {
@@ -1322,11 +1372,13 @@ var machine = createMachine({
1322
1372
  contentEl.scrollTop = 0;
1323
1373
  }
1324
1374
  },
1325
- invokeOnOpen({ prop }) {
1326
- prop("onOpenChange")?.({ open: true });
1375
+ invokeOnOpen({ prop, event }) {
1376
+ const reason = getOpenChangeReason(event);
1377
+ prop("onOpenChange")?.({ open: true, reason });
1327
1378
  },
1328
- invokeOnClose({ prop }) {
1329
- prop("onOpenChange")?.({ open: false });
1379
+ invokeOnClose({ prop, event }) {
1380
+ const reason = getOpenChangeReason(event);
1381
+ prop("onOpenChange")?.({ open: false, reason });
1330
1382
  },
1331
1383
  highlightFirstItem({ context, prop, scope }) {
1332
1384
  const exec = getContentEl(scope) ? queueMicrotask : domQuery.raf;
@@ -1411,12 +1463,18 @@ var machine = createMachine({
1411
1463
  syncSelectedItems(params) {
1412
1464
  queueMicrotask(() => {
1413
1465
  const { context, prop } = params;
1466
+ const collection2 = prop("collection");
1467
+ const value = context.get("value");
1468
+ const selectedItems = value.map((v) => {
1469
+ const item = context.get("selectedItems").find((item2) => collection2.getItemValue(item2) === v);
1470
+ return item || collection2.find(v);
1471
+ });
1472
+ context.set("selectedItems", selectedItems);
1414
1473
  const inputValue = utils.match(prop("selectionBehavior"), {
1415
1474
  preserve: context.get("inputValue"),
1416
- replace: prop("collection").stringifyMany(context.get("value")),
1475
+ replace: collection2.stringifyMany(value),
1417
1476
  clear: ""
1418
1477
  });
1419
- context.set("selectedItems", getSelectedItems(params));
1420
1478
  context.set("inputValue", inputValue);
1421
1479
  });
1422
1480
  },
@@ -1430,20 +1488,8 @@ var machine = createMachine({
1430
1488
  }
1431
1489
  }
1432
1490
  });
1433
- function getInputValue({ context, prop, computed }) {
1434
- return utils.match(prop("selectionBehavior"), {
1435
- preserve: context.get("inputValue"),
1436
- replace: computed("valueAsString"),
1437
- clear: ""
1438
- });
1439
- }
1440
- function getSelectedItems({ context, prop }) {
1441
- const collection2 = prop("collection");
1442
- return context.get("value").map((v) => {
1443
- const foundItem = context.get("selectedItems").find((item) => collection2.getItemValue(item) === v);
1444
- if (foundItem) return foundItem;
1445
- return collection2.find(v);
1446
- });
1491
+ function getOpenChangeReason(event) {
1492
+ return (event.previousEvent || event).src;
1447
1493
  }
1448
1494
  var props = types.createProps()([
1449
1495
  "allowCustomValue",
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createAnatomy } from '@zag-js/anatomy';
2
2
  import { ListCollection } from '@zag-js/collection';
3
- import { raf, setCaretToEnd, observeAttributes, observeChildren, clickIfLink, scrollIntoView, query, dataAttr, ariaAttr, isDownloadingEvent, isOpeningInNewTab, isContextMenuEvent, isLeftClick, getEventKey, isComposingEvent, isAnchorElement } from '@zag-js/dom-query';
3
+ import { raf, setCaretToEnd, nextTick, scrollIntoView, observeAttributes, clickIfLink, query, dataAttr, ariaAttr, isDownloadingEvent, isOpeningInNewTab, isContextMenuEvent, isLeftClick, getEventKey, isComposingEvent, isAnchorElement } from '@zag-js/dom-query';
4
4
  import { getPlacement, getPlacementStyles } from '@zag-js/popper';
5
5
  import { match, remove, addOrRemove, isBoolean, isEqual, createSplitProps, ensure } from '@zag-js/utils';
6
6
  import { ariaHidden } from '@zag-js/aria-hidden';
@@ -115,14 +115,17 @@ function connect(service, normalize) {
115
115
  setHighlightValue(value) {
116
116
  send({ type: "HIGHLIGHTED_VALUE.SET", value });
117
117
  },
118
+ clearHighlightValue() {
119
+ send({ type: "HIGHLIGHTED_VALUE.CLEAR" });
120
+ },
118
121
  selectValue(value) {
119
122
  send({ type: "ITEM.SELECT", value });
120
123
  },
121
124
  setValue(value) {
122
125
  send({ type: "VALUE.SET", value });
123
126
  },
124
- setInputValue(value) {
125
- send({ type: "INPUT_VALUE.SET", value });
127
+ setInputValue(value, reason = "script") {
128
+ send({ type: "INPUT_VALUE.SET", value, src: reason });
126
129
  },
127
130
  clearValue(value) {
128
131
  if (value != null) {
@@ -134,10 +137,10 @@ function connect(service, normalize) {
134
137
  focus() {
135
138
  getInputEl(scope)?.focus();
136
139
  },
137
- setOpen(nextOpen) {
140
+ setOpen(nextOpen, reason = "script") {
138
141
  const open2 = state.hasTag("open");
139
142
  if (open2 === nextOpen) return;
140
- send({ type: nextOpen ? "OPEN" : "CLOSE" });
143
+ send({ type: nextOpen ? "OPEN" : "CLOSE", src: reason });
141
144
  },
142
145
  getRootProps() {
143
146
  return normalize.element({
@@ -214,7 +217,7 @@ function connect(service, normalize) {
214
217
  if (event2.defaultPrevented) return;
215
218
  if (!prop("openOnClick")) return;
216
219
  if (!interactive) return;
217
- send({ type: "INPUT.CLICK" });
220
+ send({ type: "INPUT.CLICK", src: "input-click" });
218
221
  },
219
222
  onFocus() {
220
223
  if (disabled) return;
@@ -225,7 +228,7 @@ function connect(service, normalize) {
225
228
  send({ type: "INPUT.BLUR" });
226
229
  },
227
230
  onChange(event2) {
228
- send({ type: "INPUT.CHANGE", value: event2.currentTarget.value });
231
+ send({ type: "INPUT.CHANGE", value: event2.currentTarget.value, src: "input-change" });
229
232
  },
230
233
  onKeyDown(event2) {
231
234
  if (event2.defaultPrevented) return;
@@ -237,12 +240,12 @@ function connect(service, normalize) {
237
240
  const keymap = {
238
241
  ArrowDown(event3) {
239
242
  if (!openOnKeyPress && !open) return;
240
- send({ type: event3.altKey ? "OPEN" : "INPUT.ARROW_DOWN", keypress });
243
+ send({ type: event3.altKey ? "OPEN" : "INPUT.ARROW_DOWN", keypress, src: "arrow-key" });
241
244
  event3.preventDefault();
242
245
  },
243
246
  ArrowUp() {
244
247
  if (!openOnKeyPress && !open) return;
245
- send({ type: event2.altKey ? "CLOSE" : "INPUT.ARROW_UP", keypress });
248
+ send({ type: event2.altKey ? "CLOSE" : "INPUT.ARROW_UP", keypress, src: "arrow-key" });
246
249
  event2.preventDefault();
247
250
  },
248
251
  Home(event3) {
@@ -260,7 +263,7 @@ function connect(service, normalize) {
260
263
  }
261
264
  },
262
265
  Enter(event3) {
263
- send({ type: "INPUT.ENTER", keypress });
266
+ send({ type: "INPUT.ENTER", keypress, src: "item-select" });
264
267
  const submittable = computed("isCustomValue") && prop("allowCustomValue");
265
268
  if (open && !submittable) {
266
269
  event3.preventDefault();
@@ -272,7 +275,7 @@ function connect(service, normalize) {
272
275
  }
273
276
  },
274
277
  Escape() {
275
- send({ type: "INPUT.ESCAPE", keypress });
278
+ send({ type: "INPUT.ESCAPE", keypress, src: "escape-key" });
276
279
  event2.preventDefault();
277
280
  }
278
281
  };
@@ -307,7 +310,7 @@ function connect(service, normalize) {
307
310
  if (event2.defaultPrevented) return;
308
311
  if (!interactive) return;
309
312
  if (!isLeftClick(event2)) return;
310
- send({ type: "TRIGGER.CLICK" });
313
+ send({ type: "TRIGGER.CLICK", src: "trigger-click" });
311
314
  },
312
315
  onPointerDown(event2) {
313
316
  if (!interactive) return;
@@ -323,10 +326,10 @@ function connect(service, normalize) {
323
326
  if (composite) return;
324
327
  const keyMap = {
325
328
  ArrowDown() {
326
- send({ type: "INPUT.ARROW_DOWN", src: "trigger" });
329
+ send({ type: "INPUT.ARROW_DOWN", src: "arrow-key" });
327
330
  },
328
331
  ArrowUp() {
329
- send({ type: "INPUT.ARROW_UP", src: "trigger" });
332
+ send({ type: "INPUT.ARROW_UP", src: "arrow-key" });
330
333
  }
331
334
  };
332
335
  const key = getEventKey(event2, { dir: prop("dir") });
@@ -423,7 +426,7 @@ function connect(service, normalize) {
423
426
  if (isOpeningInNewTab(event2)) return;
424
427
  if (isContextMenuEvent(event2)) return;
425
428
  if (itemState.disabled) return;
426
- send({ type: "ITEM.CLICK", src: "click", value });
429
+ send({ type: "ITEM.CLICK", src: "item-select", value });
427
430
  }
428
431
  });
429
432
  },
@@ -505,7 +508,7 @@ var machine = createMachine({
505
508
  const open = prop("open") || prop("defaultOpen");
506
509
  return open ? "suggesting" : "idle";
507
510
  },
508
- context({ prop, bindable, getContext }) {
511
+ context({ prop, bindable, getContext, getEvent }) {
509
512
  return {
510
513
  currentPlacement: bindable(() => ({
511
514
  defaultValue: void 0
@@ -552,7 +555,9 @@ var machine = createMachine({
552
555
  defaultValue: inputValue,
553
556
  value: prop("inputValue"),
554
557
  onChange(value2) {
555
- prop("onInputValueChange")?.({ inputValue: value2 });
558
+ const event = getEvent();
559
+ const reason = (event.previousEvent || event).src;
560
+ prop("onInputValueChange")?.({ inputValue: value2, reason });
556
561
  }
557
562
  };
558
563
  }),
@@ -577,7 +582,7 @@ var machine = createMachine({
577
582
  valueAsString: ({ context, prop }) => prop("collection").stringifyItems(context.get("selectedItems")),
578
583
  isCustomValue: ({ context, computed }) => context.get("inputValue") !== computed("valueAsString")
579
584
  },
580
- watch({ context, prop, track, action }) {
585
+ watch({ context, prop, track, action, send }) {
581
586
  track([() => context.hash("value")], () => {
582
587
  action(["syncSelectedItems"]);
583
588
  });
@@ -590,13 +595,19 @@ var machine = createMachine({
590
595
  track([() => prop("open")], () => {
591
596
  action(["toggleVisibility"]);
592
597
  });
598
+ track([() => prop("collection").toString()], () => {
599
+ send({ type: "CHILDREN_CHANGE" });
600
+ });
593
601
  },
594
602
  on: {
595
603
  "SELECTED_ITEMS.SYNC": {
596
604
  actions: ["syncSelectedItems"]
597
605
  },
598
606
  "HIGHLIGHTED_VALUE.SET": {
599
- actions: ["setHighlightedItem"]
607
+ actions: ["setHighlightedValue"]
608
+ },
609
+ "HIGHLIGHTED_VALUE.CLEAR": {
610
+ actions: ["clearHighlightedValue"]
600
611
  },
601
612
  "ITEM.SELECT": {
602
613
  actions: ["selectItem"]
@@ -623,7 +634,7 @@ var machine = createMachine({
623
634
  states: {
624
635
  idle: {
625
636
  tags: ["idle", "closed"],
626
- entry: ["scrollContentToTop", "clearHighlightedItem"],
637
+ entry: ["scrollContentToTop", "clearHighlightedValue"],
627
638
  on: {
628
639
  "CONTROLLED.OPEN": {
629
640
  target: "interacting"
@@ -669,7 +680,7 @@ var machine = createMachine({
669
680
  },
670
681
  focused: {
671
682
  tags: ["focused", "closed"],
672
- entry: ["scrollContentToTop", "clearHighlightedItem"],
683
+ entry: ["scrollContentToTop", "clearHighlightedValue"],
673
684
  on: {
674
685
  "CONTROLLED.OPEN": [
675
686
  {
@@ -797,6 +808,15 @@ var machine = createMachine({
797
808
  target: "idle"
798
809
  }
799
810
  ],
811
+ CHILDREN_CHANGE: [
812
+ {
813
+ guard: "isHighlightedItemRemoved",
814
+ actions: ["clearHighlightedValue"]
815
+ },
816
+ {
817
+ actions: ["scrollToHighlightedItem"]
818
+ }
819
+ ],
800
820
  "INPUT.HOME": {
801
821
  actions: ["highlightFirstItem"]
802
822
  },
@@ -806,7 +826,7 @@ var machine = createMachine({
806
826
  "INPUT.ARROW_DOWN": [
807
827
  {
808
828
  guard: and("autoComplete", "isLastItemHighlighted"),
809
- actions: ["clearHighlightedItem", "scrollContentToTop"]
829
+ actions: ["clearHighlightedValue", "scrollContentToTop"]
810
830
  },
811
831
  {
812
832
  actions: ["highlightNextItem"]
@@ -815,7 +835,7 @@ var machine = createMachine({
815
835
  "INPUT.ARROW_UP": [
816
836
  {
817
837
  guard: and("autoComplete", "isFirstItemHighlighted"),
818
- actions: ["clearHighlightedItem"]
838
+ actions: ["clearHighlightedValue"]
819
839
  },
820
840
  {
821
841
  actions: ["highlightPrevItem"]
@@ -854,14 +874,14 @@ var machine = createMachine({
854
874
  },
855
875
  {
856
876
  target: "suggesting",
857
- actions: ["clearHighlightedItem", "setInputValue"]
877
+ actions: ["clearHighlightedValue", "setInputValue"]
858
878
  }
859
879
  ],
860
880
  "ITEM.POINTER_MOVE": {
861
- actions: ["setHighlightedItem"]
881
+ actions: ["setHighlightedValue"]
862
882
  },
863
883
  "ITEM.POINTER_LEAVE": {
864
- actions: ["clearHighlightedItem"]
884
+ actions: ["clearHighlightedValue"]
865
885
  },
866
886
  "ITEM.CLICK": [
867
887
  {
@@ -951,13 +971,7 @@ var machine = createMachine({
951
971
  },
952
972
  suggesting: {
953
973
  tags: ["open", "focused"],
954
- effects: [
955
- "trackDismissableLayer",
956
- "scrollToHighlightedItem",
957
- "trackPlacement",
958
- "trackChildNodes",
959
- "hideOtherElements"
960
- ],
974
+ effects: ["trackDismissableLayer", "scrollToHighlightedItem", "trackPlacement", "hideOtherElements"],
961
975
  entry: ["setInitialFocus"],
962
976
  on: {
963
977
  "CONTROLLED.CLOSE": [
@@ -970,10 +984,16 @@ var machine = createMachine({
970
984
  target: "idle"
971
985
  }
972
986
  ],
973
- CHILDREN_CHANGE: {
974
- guard: "autoHighlight",
975
- actions: ["highlightFirstItem"]
976
- },
987
+ CHILDREN_CHANGE: [
988
+ {
989
+ guard: "autoHighlight",
990
+ actions: ["highlightFirstItem"]
991
+ },
992
+ {
993
+ guard: "isHighlightedItemRemoved",
994
+ actions: ["clearHighlightedValue"]
995
+ }
996
+ ],
977
997
  "INPUT.ARROW_DOWN": {
978
998
  target: "interacting",
979
999
  actions: ["highlightNextItem"]
@@ -1030,10 +1050,10 @@ var machine = createMachine({
1030
1050
  ],
1031
1051
  "ITEM.POINTER_MOVE": {
1032
1052
  target: "interacting",
1033
- actions: ["setHighlightedItem"]
1053
+ actions: ["setHighlightedValue"]
1034
1054
  },
1035
1055
  "ITEM.POINTER_LEAVE": {
1036
- actions: ["clearHighlightedItem"]
1056
+ actions: ["clearHighlightedValue"]
1037
1057
  },
1038
1058
  "LAYER.INTERACT_OUTSIDE": [
1039
1059
  // == group 1 ==
@@ -1122,7 +1142,8 @@ var machine = createMachine({
1122
1142
  },
1123
1143
  restoreFocus: ({ event }) => event.restoreFocus == null ? true : !!event.restoreFocus,
1124
1144
  isChangeEvent: ({ event }) => event.previousEvent?.type === "INPUT.CHANGE",
1125
- autoFocus: ({ prop }) => !!prop("autoFocus")
1145
+ autoFocus: ({ prop }) => !!prop("autoFocus"),
1146
+ isHighlightedItemRemoved: ({ prop, context }) => !prop("collection").has(context.get("highlightedValue"))
1126
1147
  },
1127
1148
  effects: {
1128
1149
  trackDismissableLayer({ send, prop, scope }) {
@@ -1137,10 +1158,10 @@ var machine = createMachine({
1137
1158
  onEscapeKeyDown(event) {
1138
1159
  event.preventDefault();
1139
1160
  event.stopPropagation();
1140
- send({ type: "LAYER.ESCAPE" });
1161
+ send({ type: "LAYER.ESCAPE", src: "escape-key" });
1141
1162
  },
1142
1163
  onDismiss() {
1143
- send({ type: "LAYER.INTERACT_OUTSIDE", restoreFocus: false });
1164
+ send({ type: "LAYER.INTERACT_OUTSIDE", src: "interact-outside", restoreFocus: false });
1144
1165
  }
1145
1166
  });
1146
1167
  },
@@ -1164,16 +1185,6 @@ var machine = createMachine({
1164
1185
  }
1165
1186
  });
1166
1187
  },
1167
- // in event the options are fetched (async), we still want to auto-highlight the first option
1168
- trackChildNodes({ scope, computed, send }) {
1169
- if (!computed("autoHighlight")) return;
1170
- const exec = () => send({ type: "CHILDREN_CHANGE" });
1171
- const contentEl = () => getContentEl(scope);
1172
- return observeChildren(contentEl, {
1173
- callback: exec,
1174
- defer: true
1175
- });
1176
- },
1177
1188
  scrollToHighlightedItem({ context, prop, scope, event }) {
1178
1189
  const inputEl = getInputEl(scope);
1179
1190
  let cleanups = [];
@@ -1220,23 +1231,42 @@ var machine = createMachine({
1220
1231
  }
1221
1232
  });
1222
1233
  },
1223
- setHighlightedItem(params) {
1224
- const { context, event } = params;
1234
+ setHighlightedValue({ context, event }) {
1225
1235
  if (event.value == null) return;
1226
1236
  context.set("highlightedValue", event.value);
1227
1237
  },
1228
- clearHighlightedItem(params) {
1229
- const { context } = params;
1238
+ clearHighlightedValue({ context }) {
1230
1239
  context.set("highlightedValue", null);
1231
1240
  },
1232
1241
  selectHighlightedItem(params) {
1233
1242
  const { context, prop } = params;
1243
+ const collection2 = prop("collection");
1234
1244
  const highlightedValue = context.get("highlightedValue");
1235
- if (!highlightedValue) return;
1245
+ if (!highlightedValue || !collection2.has(highlightedValue)) return;
1236
1246
  const nextValue = prop("multiple") ? addOrRemove(context.get("value"), highlightedValue) : [highlightedValue];
1237
1247
  prop("onSelect")?.({ value: nextValue, itemValue: highlightedValue });
1238
1248
  context.set("value", nextValue);
1239
- context.set("inputValue", getInputValue(params));
1249
+ const inputValue = match(prop("selectionBehavior"), {
1250
+ preserve: context.get("inputValue"),
1251
+ replace: collection2.stringifyMany(nextValue),
1252
+ clear: ""
1253
+ });
1254
+ context.set("inputValue", inputValue);
1255
+ },
1256
+ scrollToHighlightedItem({ context, prop, scope }) {
1257
+ nextTick(() => {
1258
+ const highlightedValue = context.get("highlightedValue");
1259
+ if (highlightedValue == null) return;
1260
+ const itemEl = getItemEl(scope, highlightedValue);
1261
+ const contentEl = getContentEl(scope);
1262
+ const scrollToIndexFn = prop("scrollToIndexFn");
1263
+ if (scrollToIndexFn) {
1264
+ const highlightedIndex = prop("collection").indexOf(highlightedValue);
1265
+ scrollToIndexFn({ index: highlightedIndex, immediate: true });
1266
+ return;
1267
+ }
1268
+ scrollIntoView(itemEl, { rootEl: contentEl, block: "nearest" });
1269
+ });
1240
1270
  },
1241
1271
  selectItem(params) {
1242
1272
  const { context, event, flush, prop } = params;
@@ -1245,16 +1275,26 @@ var machine = createMachine({
1245
1275
  const nextValue = prop("multiple") ? addOrRemove(context.get("value"), event.value) : [event.value];
1246
1276
  prop("onSelect")?.({ value: nextValue, itemValue: event.value });
1247
1277
  context.set("value", nextValue);
1248
- context.set("inputValue", getInputValue(params));
1278
+ const inputValue = match(prop("selectionBehavior"), {
1279
+ preserve: context.get("inputValue"),
1280
+ replace: prop("collection").stringifyMany(nextValue),
1281
+ clear: ""
1282
+ });
1283
+ context.set("inputValue", inputValue);
1249
1284
  });
1250
1285
  },
1251
1286
  clearItem(params) {
1252
- const { context, event, flush } = params;
1287
+ const { context, event, flush, prop } = params;
1253
1288
  if (event.value == null) return;
1254
1289
  flush(() => {
1255
1290
  const nextValue = remove(context.get("value"), event.value);
1256
1291
  context.set("value", nextValue);
1257
- context.set("inputValue", getInputValue(params));
1292
+ const inputValue = match(prop("selectionBehavior"), {
1293
+ preserve: context.get("inputValue"),
1294
+ replace: prop("collection").stringifyMany(nextValue),
1295
+ clear: ""
1296
+ });
1297
+ context.set("inputValue", inputValue);
1258
1298
  });
1259
1299
  },
1260
1300
  setInitialFocus({ scope }) {
@@ -1297,17 +1337,27 @@ var machine = createMachine({
1297
1337
  context.set("inputValue", inputValue);
1298
1338
  },
1299
1339
  setValue(params) {
1300
- const { context, flush, event } = params;
1340
+ const { context, flush, event, prop } = params;
1301
1341
  flush(() => {
1302
1342
  context.set("value", event.value);
1303
- context.set("inputValue", getInputValue(params));
1343
+ const inputValue = match(prop("selectionBehavior"), {
1344
+ preserve: context.get("inputValue"),
1345
+ replace: prop("collection").stringifyMany(event.value),
1346
+ clear: ""
1347
+ });
1348
+ context.set("inputValue", inputValue);
1304
1349
  });
1305
1350
  },
1306
1351
  clearSelectedItems(params) {
1307
- const { context, flush } = params;
1352
+ const { context, flush, prop } = params;
1308
1353
  flush(() => {
1309
1354
  context.set("value", []);
1310
- context.set("inputValue", getInputValue(params));
1355
+ const inputValue = match(prop("selectionBehavior"), {
1356
+ preserve: context.get("inputValue"),
1357
+ replace: prop("collection").stringifyMany([]),
1358
+ clear: ""
1359
+ });
1360
+ context.set("inputValue", inputValue);
1311
1361
  });
1312
1362
  },
1313
1363
  scrollContentToTop({ prop, scope }) {
@@ -1320,11 +1370,13 @@ var machine = createMachine({
1320
1370
  contentEl.scrollTop = 0;
1321
1371
  }
1322
1372
  },
1323
- invokeOnOpen({ prop }) {
1324
- prop("onOpenChange")?.({ open: true });
1373
+ invokeOnOpen({ prop, event }) {
1374
+ const reason = getOpenChangeReason(event);
1375
+ prop("onOpenChange")?.({ open: true, reason });
1325
1376
  },
1326
- invokeOnClose({ prop }) {
1327
- prop("onOpenChange")?.({ open: false });
1377
+ invokeOnClose({ prop, event }) {
1378
+ const reason = getOpenChangeReason(event);
1379
+ prop("onOpenChange")?.({ open: false, reason });
1328
1380
  },
1329
1381
  highlightFirstItem({ context, prop, scope }) {
1330
1382
  const exec = getContentEl(scope) ? queueMicrotask : raf;
@@ -1409,12 +1461,18 @@ var machine = createMachine({
1409
1461
  syncSelectedItems(params) {
1410
1462
  queueMicrotask(() => {
1411
1463
  const { context, prop } = params;
1464
+ const collection2 = prop("collection");
1465
+ const value = context.get("value");
1466
+ const selectedItems = value.map((v) => {
1467
+ const item = context.get("selectedItems").find((item2) => collection2.getItemValue(item2) === v);
1468
+ return item || collection2.find(v);
1469
+ });
1470
+ context.set("selectedItems", selectedItems);
1412
1471
  const inputValue = match(prop("selectionBehavior"), {
1413
1472
  preserve: context.get("inputValue"),
1414
- replace: prop("collection").stringifyMany(context.get("value")),
1473
+ replace: collection2.stringifyMany(value),
1415
1474
  clear: ""
1416
1475
  });
1417
- context.set("selectedItems", getSelectedItems(params));
1418
1476
  context.set("inputValue", inputValue);
1419
1477
  });
1420
1478
  },
@@ -1428,20 +1486,8 @@ var machine = createMachine({
1428
1486
  }
1429
1487
  }
1430
1488
  });
1431
- function getInputValue({ context, prop, computed }) {
1432
- return match(prop("selectionBehavior"), {
1433
- preserve: context.get("inputValue"),
1434
- replace: computed("valueAsString"),
1435
- clear: ""
1436
- });
1437
- }
1438
- function getSelectedItems({ context, prop }) {
1439
- const collection2 = prop("collection");
1440
- return context.get("value").map((v) => {
1441
- const foundItem = context.get("selectedItems").find((item) => collection2.getItemValue(item) === v);
1442
- if (foundItem) return foundItem;
1443
- return collection2.find(v);
1444
- });
1489
+ function getOpenChangeReason(event) {
1490
+ return (event.previousEvent || event).src;
1445
1491
  }
1446
1492
  var props = createProps()([
1447
1493
  "allowCustomValue",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/combobox",
3
- "version": "1.18.0",
3
+ "version": "1.18.1",
4
4
  "description": "Core logic for the combobox widget implemented as a state machine",
5
5
  "keywords": [
6
6
  "js",
@@ -26,15 +26,15 @@
26
26
  "url": "https://github.com/chakra-ui/zag/issues"
27
27
  },
28
28
  "dependencies": {
29
- "@zag-js/anatomy": "1.18.0",
30
- "@zag-js/aria-hidden": "1.18.0",
31
- "@zag-js/collection": "1.18.0",
32
- "@zag-js/core": "1.18.0",
33
- "@zag-js/dismissable": "1.18.0",
34
- "@zag-js/dom-query": "1.18.0",
35
- "@zag-js/utils": "1.18.0",
36
- "@zag-js/popper": "1.18.0",
37
- "@zag-js/types": "1.18.0"
29
+ "@zag-js/anatomy": "1.18.1",
30
+ "@zag-js/aria-hidden": "1.18.1",
31
+ "@zag-js/collection": "1.18.1",
32
+ "@zag-js/core": "1.18.1",
33
+ "@zag-js/dismissable": "1.18.1",
34
+ "@zag-js/dom-query": "1.18.1",
35
+ "@zag-js/utils": "1.18.1",
36
+ "@zag-js/popper": "1.18.1",
37
+ "@zag-js/types": "1.18.1"
38
38
  },
39
39
  "devDependencies": {
40
40
  "clean-package": "2.2.0"