@arun-dev/headless 4.8.0 → 4.9.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.
@@ -33,12 +33,14 @@ __export(index_parts_exports, {
33
33
  Clear: () => ComboboxClear,
34
34
  Empty: () => ComboboxEmpty,
35
35
  Input: () => ComboboxInput,
36
+ InputGroup: () => ComboboxInputGroup,
36
37
  Item: () => ComboboxItem,
37
38
  List: () => ComboboxList,
38
39
  Popup: () => ComboboxPopup,
39
40
  Root: () => ComboboxRoot,
40
41
  Status: () => ComboboxStatus,
41
- Trigger: () => ComboboxTrigger
42
+ Trigger: () => ComboboxTrigger,
43
+ Value: () => ComboboxValue
42
44
  });
43
45
 
44
46
  // src/combobox/ComboboxRoot.tsx
@@ -342,6 +344,11 @@ function ComboboxRoot({
342
344
  },
343
345
  [insideRefs]
344
346
  );
347
+ const [hasGroup, setHasGroup] = (0, import_react3.useState)(false);
348
+ const registerGroup = (0, import_react3.useCallback)(() => {
349
+ setHasGroup(true);
350
+ return () => setHasGroup(false);
351
+ }, []);
345
352
  const inputRef = (0, import_react3.useRef)(null);
346
353
  const popupRef = (0, import_react3.useRef)(null);
347
354
  const { current: initial } = (0, import_react3.useRef)({ value, inputValue });
@@ -392,6 +399,8 @@ function ComboboxRoot({
392
399
  chips,
393
400
  registerInside,
394
401
  isInside,
402
+ hasGroup,
403
+ registerGroup,
395
404
  onLoadMore,
396
405
  inputId: `${id}-input`,
397
406
  listId: `${id}-list`,
@@ -426,6 +435,8 @@ function ComboboxRoot({
426
435
  chips,
427
436
  registerInside,
428
437
  isInside,
438
+ hasGroup,
439
+ registerGroup,
429
440
  onLoadMore,
430
441
  id
431
442
  ]
@@ -586,6 +597,7 @@ function ComboboxInput({ className, render, ...rest }) {
586
597
  listId,
587
598
  optionId,
588
599
  anchorName,
600
+ hasGroup,
589
601
  inputRef,
590
602
  popupRef
591
603
  } = useComboboxRootContext("Input");
@@ -633,7 +645,8 @@ function ComboboxInput({ className, render, ...rest }) {
633
645
  disabled: disabled || void 0,
634
646
  value: inputValue,
635
647
  ...comboboxDataAttributes({ open, multiple, disabled }),
636
- style: { anchorName },
648
+ // The anchor, unless an InputGroup around it is.
649
+ style: hasGroup ? void 0 : { anchorName },
637
650
  className,
638
651
  ref: inputRef,
639
652
  onChange(event) {
@@ -700,12 +713,43 @@ function ComboboxInput({ className, render, ...rest }) {
700
713
  });
701
714
  }
702
715
 
703
- // src/combobox/ComboboxTrigger.tsx
716
+ // src/combobox/ComboboxInputGroup.tsx
704
717
  var import_react6 = require("react");
705
- function ComboboxTrigger({ className, children, render, ...rest }) {
706
- const { open, multiple, disabled, setOpen, close, listId, inputRef, registerInside } = useComboboxRootContext("Trigger");
718
+ function ComboboxInputGroup({
719
+ className,
720
+ children,
721
+ render,
722
+ ...rest
723
+ }) {
724
+ const { open, multiple, disabled, anchorName, registerGroup, registerInside, inputRef } = useComboboxRootContext("InputGroup");
707
725
  const elementRef = (0, import_react6.useRef)(null);
726
+ (0, import_react6.useLayoutEffect)(() => registerGroup(), [registerGroup]);
708
727
  (0, import_react6.useLayoutEffect)(() => registerInside(elementRef), [registerInside]);
728
+ return useRender({
729
+ render,
730
+ defaultTagName: "div",
731
+ props: {
732
+ ...comboboxDataAttributes({ open, multiple, disabled }),
733
+ style: { anchorName },
734
+ className,
735
+ children,
736
+ ref: elementRef,
737
+ onMouseDown(event) {
738
+ if (event.target !== event.currentTarget || disabled) return;
739
+ event.preventDefault();
740
+ inputRef.current?.focus();
741
+ }
742
+ },
743
+ consumerProps: rest
744
+ });
745
+ }
746
+
747
+ // src/combobox/ComboboxTrigger.tsx
748
+ var import_react7 = require("react");
749
+ function ComboboxTrigger({ className, children, render, ...rest }) {
750
+ const { open, multiple, disabled, setOpen, close, listId, inputRef, registerInside } = useComboboxRootContext("Trigger");
751
+ const elementRef = (0, import_react7.useRef)(null);
752
+ (0, import_react7.useLayoutEffect)(() => registerInside(elementRef), [registerInside]);
709
753
  return useRender({
710
754
  render,
711
755
  defaultTagName: "button",
@@ -733,11 +777,11 @@ function ComboboxTrigger({ className, children, render, ...rest }) {
733
777
  }
734
778
 
735
779
  // src/combobox/ComboboxClear.tsx
736
- var import_react7 = require("react");
780
+ var import_react8 = require("react");
737
781
  function ComboboxClear({ className, children, render, ...rest }) {
738
782
  const { disabled, selectedItems, clear, inputRef, registerInside } = useComboboxRootContext("Clear");
739
- const elementRef = (0, import_react7.useRef)(null);
740
- (0, import_react7.useLayoutEffect)(() => registerInside(elementRef), [registerInside]);
783
+ const elementRef = (0, import_react8.useRef)(null);
784
+ (0, import_react8.useLayoutEffect)(() => registerInside(elementRef), [registerInside]);
741
785
  return useRender({
742
786
  render,
743
787
  defaultTagName: "button",
@@ -763,7 +807,7 @@ function ComboboxClear({ className, children, render, ...rest }) {
763
807
  }
764
808
 
765
809
  // src/combobox/ComboboxPopup.tsx
766
- var import_react8 = require("react");
810
+ var import_react9 = require("react");
767
811
  function ComboboxPopup({
768
812
  side = "bottom",
769
813
  align = "start",
@@ -773,7 +817,7 @@ function ComboboxPopup({
773
817
  ...rest
774
818
  }) {
775
819
  const { open, multiple, disabled, setOpen, close, isInside, anchorName, inputRef, popupRef } = useComboboxRootContext("Popup");
776
- (0, import_react8.useEffect)(() => {
820
+ (0, import_react9.useEffect)(() => {
777
821
  if (!open) return;
778
822
  function onPointerDown(event) {
779
823
  if (event.target instanceof Node && isInside(event.target)) return;
@@ -782,8 +826,8 @@ function ComboboxPopup({
782
826
  document.addEventListener("pointerdown", onPointerDown, true);
783
827
  return () => document.removeEventListener("pointerdown", onPointerDown, true);
784
828
  }, [open, isInside, close]);
785
- const [, resync] = (0, import_react8.useReducer)((n) => n + 1, 0);
786
- (0, import_react8.useEffect)(() => {
829
+ const [, resync] = (0, import_react9.useReducer)((n) => n + 1, 0);
830
+ (0, import_react9.useEffect)(() => {
787
831
  const popup = popupRef.current;
788
832
  if (!popup || typeof popup.showPopover !== "function") return;
789
833
  const shown = popup.matches(":popover-open");
@@ -820,7 +864,7 @@ function ComboboxPopup({
820
864
  }
821
865
 
822
866
  // src/combobox/ComboboxList.tsx
823
- var import_react9 = require("react");
867
+ var import_react10 = require("react");
824
868
  var import_jsx_runtime2 = require("react/jsx-runtime");
825
869
  function ComboboxList({
826
870
  className,
@@ -849,10 +893,10 @@ function ComboboxList({
849
893
  }
850
894
  function LoadMoreSentinel() {
851
895
  const { open, loading, filteredItems, onLoadMore } = useComboboxRootContext("List");
852
- const ref = (0, import_react9.useRef)(null);
853
- const onLoadMoreRef = (0, import_react9.useRef)(onLoadMore);
896
+ const ref = (0, import_react10.useRef)(null);
897
+ const onLoadMoreRef = (0, import_react10.useRef)(onLoadMore);
854
898
  onLoadMoreRef.current = onLoadMore;
855
- (0, import_react9.useEffect)(() => {
899
+ (0, import_react10.useEffect)(() => {
856
900
  const element = ref.current;
857
901
  if (!element || !open || loading || typeof IntersectionObserver === "undefined") return;
858
902
  const observer = new IntersectionObserver((entries) => {
@@ -865,7 +909,7 @@ function LoadMoreSentinel() {
865
909
  }
866
910
 
867
911
  // src/combobox/ComboboxItem.tsx
868
- var import_react10 = require("react");
912
+ var import_react11 = require("react");
869
913
  function ComboboxItem({
870
914
  value,
871
915
  disabled = false,
@@ -887,20 +931,20 @@ function ComboboxItem({
887
931
  const key = itemToKey(value);
888
932
  const index = indexByKey.get(key) ?? -1;
889
933
  const selected = selectedKeys.has(key);
890
- const highlighted = (0, import_react10.useSyncExternalStore)(
934
+ const highlighted = (0, import_react11.useSyncExternalStore)(
891
935
  highlight.subscribe,
892
936
  () => index >= 0 && highlight.get().index === index,
893
937
  () => false
894
938
  );
895
- const elementRef = (0, import_react10.useRef)(null);
896
- (0, import_react10.useLayoutEffect)(() => {
939
+ const elementRef = (0, import_react11.useRef)(null);
940
+ (0, import_react11.useLayoutEffect)(() => {
897
941
  if (!disabled) return;
898
942
  disabledKeys.add(key);
899
943
  return () => {
900
944
  disabledKeys.delete(key);
901
945
  };
902
946
  }, [disabled, disabledKeys, key]);
903
- (0, import_react10.useEffect)(() => {
947
+ (0, import_react11.useEffect)(() => {
904
948
  if (highlighted && highlight.get().reason === "keyboard") {
905
949
  elementRef.current?.scrollIntoView?.({ block: "nearest" });
906
950
  }
@@ -955,11 +999,11 @@ function ComboboxStatus({ className, children, render, ...rest }) {
955
999
  }
956
1000
 
957
1001
  // src/combobox/ComboboxChip.tsx
958
- var import_react11 = require("react");
1002
+ var import_react12 = require("react");
959
1003
  var import_jsx_runtime3 = require("react/jsx-runtime");
960
- var ComboboxChipContext = (0, import_react11.createContext)(null);
1004
+ var ComboboxChipContext = (0, import_react12.createContext)(null);
961
1005
  function useComboboxChipContext() {
962
- const context = (0, import_react11.useContext)(ComboboxChipContext);
1006
+ const context = (0, import_react12.useContext)(ComboboxChipContext);
963
1007
  if (context === null) {
964
1008
  throw new Error("<Combobox.ChipRemove> must be rendered inside <Combobox.Chip>.");
965
1009
  }
@@ -974,9 +1018,9 @@ function ComboboxChip({
974
1018
  }) {
975
1019
  const { disabled, itemToKey, remove, registerChip, chips, inputRef } = useComboboxRootContext("Chip");
976
1020
  const key = itemToKey(value);
977
- const elementRef = (0, import_react11.useRef)(null);
978
- (0, import_react11.useLayoutEffect)(() => registerChip({ key, ref: elementRef }), [registerChip, key]);
979
- const chipContext = (0, import_react11.useMemo)(() => ({ value }), [value]);
1021
+ const elementRef = (0, import_react12.useRef)(null);
1022
+ (0, import_react12.useLayoutEffect)(() => registerChip({ key, ref: elementRef }), [registerChip, key]);
1023
+ const chipContext = (0, import_react12.useMemo)(() => ({ value }), [value]);
980
1024
  function focusAt(index) {
981
1025
  const chip = chips[index];
982
1026
  if (chip) chip.ref.current?.focus();
@@ -1050,6 +1094,14 @@ function ComboboxChipRemove({
1050
1094
  consumerProps: rest
1051
1095
  });
1052
1096
  }
1097
+
1098
+ // src/combobox/ComboboxValue.tsx
1099
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1100
+ function ComboboxValue({ children }) {
1101
+ const { multiple, selectedItems, itemToString } = useComboboxRootContext("Value");
1102
+ const value = multiple ? selectedItems : selectedItems[0] ?? null;
1103
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: children(value, { itemToString }) });
1104
+ }
1053
1105
  // Annotate the CommonJS export names for ESM import in node:
1054
1106
  0 && (module.exports = {
1055
1107
  Combobox,
@@ -21,7 +21,7 @@ type ComboboxState = {
21
21
  };
22
22
 
23
23
  /** The selection: one item or `null`, or an array with `multiple`. */
24
- type ComboboxValue<T, Multiple extends boolean> = Multiple extends true ? T[] : T | null;
24
+ type ComboboxValue$1<T, Multiple extends boolean> = Multiple extends true ? T[] : T | null;
25
25
  /** Takes the whole list, so a filter can rank as well as narrow — a fuzzy search, say. */
26
26
  type ComboboxFilter<T> = (items: readonly T[], query: string, itemToString: (item: T) => string) => T[];
27
27
  type ComboboxRootProps<T, Multiple extends boolean = false> = {
@@ -51,10 +51,10 @@ type ComboboxRootProps<T, Multiple extends boolean = false> = {
51
51
  * Never `undefined` once mounted: the mode is latched at mount. Use `null` — or `[]` with
52
52
  * `multiple` — for nothing selected.
53
53
  */
54
- value?: ComboboxValue<T, Multiple>;
54
+ value?: ComboboxValue$1<T, Multiple>;
55
55
  /** Initial selection when uncontrolled. Read once, at mount. */
56
- defaultValue?: ComboboxValue<T, Multiple>;
57
- onValueChange?: (value: ComboboxValue<T, Multiple>, details: ComboboxChangeDetails) => void;
56
+ defaultValue?: ComboboxValue$1<T, Multiple>;
57
+ onValueChange?: (value: ComboboxValue$1<T, Multiple>, details: ComboboxChangeDetails) => void;
58
58
  /** Controlled text of the input. Provide `onInputValueChange` alongside it. */
59
59
  inputValue?: string;
60
60
  /** Initial text when uncontrolled. Defaults to the selected item's label, without `multiple`. */
@@ -107,7 +107,7 @@ type ComboboxInputOwnProps = {
107
107
  type ComboboxInputProps = ComboboxInputOwnProps & Omit<ComponentPropsWithRef<'input'>, keyof ComboboxInputOwnProps | 'value' | 'defaultValue'>;
108
108
  /**
109
109
  * The text input: `role="combobox"`, and the element focus stays on. Its text is the Root's
110
- * `inputValue`. It is what the popup is anchored to.
110
+ * `inputValue`. It is what the popup is anchored to, unless an InputGroup wraps it.
111
111
  *
112
112
  * Typing opens the list and filters it. Up and Down open it, or move the highlight, wrapping;
113
113
  * Home and End jump to the ends while it is open; Alt+Down opens it without highlighting.
@@ -119,6 +119,22 @@ type ComboboxInputProps = ComboboxInputOwnProps & Omit<ComponentPropsWithRef<'in
119
119
  */
120
120
  declare function ComboboxInput({ className, render, ...rest }: ComboboxInputProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
121
121
 
122
+ type ComboboxInputGroupOwnProps = {
123
+ /** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
124
+ render?: ReactElement;
125
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
126
+ ref?: Ref<HTMLElement>;
127
+ };
128
+ type ComboboxInputGroupProps = ComboboxInputGroupOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof ComboboxInputGroupOwnProps>;
129
+ /**
130
+ * The field around the Input — chips, Clear and Trigger with it — when they are drawn as one
131
+ * box. It takes over as the popup's anchor, so the list lines up with the whole field rather
132
+ * than the text alone, and a press on it is not a press outside.
133
+ *
134
+ * A press on the box itself, between its children, focuses the input.
135
+ */
136
+ declare function ComboboxInputGroup({ className, children, render, ...rest }: ComboboxInputGroupProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
137
+
122
138
  type ComboboxTriggerOwnProps = {
123
139
  /** Element to render instead of the default `<button>`. Props and ref are merged onto it. */
124
140
  render?: ReactElement;
@@ -273,8 +289,26 @@ type ComboboxChipRemoveProps = ComboboxChipRemoveOwnProps & Omit<ComponentPropsW
273
289
  */
274
290
  declare function ComboboxChipRemove({ className, children, render, ...rest }: ComboboxChipRemoveProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
275
291
 
292
+ type ComboboxValueProps<T = unknown> = {
293
+ /**
294
+ * Renders the selection: `T | null`, or `T[]` with `multiple`. The Root's `itemToString` is
295
+ * passed along for labels — a chip's text, say.
296
+ */
297
+ children: (value: T, details: {
298
+ itemToString: (item: unknown) => string;
299
+ }) => ReactNode;
300
+ };
301
+ /**
302
+ * The current selection, for whatever shows it outside the input — chips, most often. Renders
303
+ * no element of its own.
304
+ *
305
+ * It exists so that a separate styling package can show the selection without owning a copy
306
+ * of it (decision 9): the value stays the Root's.
307
+ */
308
+ declare function ComboboxValue<T = unknown>({ children }: ComboboxValueProps<T>): react.JSX.Element;
309
+
276
310
  declare namespace index_parts {
277
- export { ComboboxChip as Chip, ComboboxChipRemove as ChipRemove, ComboboxClear as Clear, ComboboxEmpty as Empty, ComboboxInput as Input, ComboboxItem as Item, ComboboxList as List, ComboboxPopup as Popup, ComboboxRoot as Root, ComboboxStatus as Status, ComboboxTrigger as Trigger };
311
+ export { ComboboxChip as Chip, ComboboxChipRemove as ChipRemove, ComboboxClear as Clear, ComboboxEmpty as Empty, ComboboxInput as Input, ComboboxInputGroup as InputGroup, ComboboxItem as Item, ComboboxList as List, ComboboxPopup as Popup, ComboboxRoot as Root, ComboboxStatus as Status, ComboboxTrigger as Trigger, ComboboxValue as Value };
278
312
  }
279
313
 
280
314
  /**
@@ -285,4 +319,4 @@ declare namespace index_parts {
285
319
  */
286
320
  declare function defaultFilter<T>(items: readonly T[], query: string, itemToString: (item: T) => string): T[];
287
321
 
288
- export { index_parts as Combobox, type ComboboxAlign, type ComboboxChangeDetails, type ComboboxChangeReason, type ComboboxChipProps, type ComboboxChipRemoveProps, type ComboboxClearProps, type ComboboxEmptyProps, type ComboboxFilter, type ComboboxHighlightDetails, type ComboboxHighlightReason, type ComboboxInputProps, type ComboboxItemProps, type ComboboxListProps, type ComboboxPopupProps, type ComboboxRootProps, type ComboboxSide, type ComboboxState, type ComboboxStatusProps, type ComboboxTriggerProps, type ComboboxValue, defaultFilter };
322
+ export { index_parts as Combobox, type ComboboxAlign, type ComboboxChangeDetails, type ComboboxChangeReason, type ComboboxChipProps, type ComboboxChipRemoveProps, type ComboboxClearProps, type ComboboxEmptyProps, type ComboboxFilter, type ComboboxHighlightDetails, type ComboboxHighlightReason, type ComboboxInputGroupProps, type ComboboxInputProps, type ComboboxItemProps, type ComboboxListProps, type ComboboxPopupProps, type ComboboxRootProps, type ComboboxSide, type ComboboxState, type ComboboxStatusProps, type ComboboxTriggerProps, type ComboboxValue$1 as ComboboxValue, type ComboboxValueProps, defaultFilter };
@@ -21,7 +21,7 @@ type ComboboxState = {
21
21
  };
22
22
 
23
23
  /** The selection: one item or `null`, or an array with `multiple`. */
24
- type ComboboxValue<T, Multiple extends boolean> = Multiple extends true ? T[] : T | null;
24
+ type ComboboxValue$1<T, Multiple extends boolean> = Multiple extends true ? T[] : T | null;
25
25
  /** Takes the whole list, so a filter can rank as well as narrow — a fuzzy search, say. */
26
26
  type ComboboxFilter<T> = (items: readonly T[], query: string, itemToString: (item: T) => string) => T[];
27
27
  type ComboboxRootProps<T, Multiple extends boolean = false> = {
@@ -51,10 +51,10 @@ type ComboboxRootProps<T, Multiple extends boolean = false> = {
51
51
  * Never `undefined` once mounted: the mode is latched at mount. Use `null` — or `[]` with
52
52
  * `multiple` — for nothing selected.
53
53
  */
54
- value?: ComboboxValue<T, Multiple>;
54
+ value?: ComboboxValue$1<T, Multiple>;
55
55
  /** Initial selection when uncontrolled. Read once, at mount. */
56
- defaultValue?: ComboboxValue<T, Multiple>;
57
- onValueChange?: (value: ComboboxValue<T, Multiple>, details: ComboboxChangeDetails) => void;
56
+ defaultValue?: ComboboxValue$1<T, Multiple>;
57
+ onValueChange?: (value: ComboboxValue$1<T, Multiple>, details: ComboboxChangeDetails) => void;
58
58
  /** Controlled text of the input. Provide `onInputValueChange` alongside it. */
59
59
  inputValue?: string;
60
60
  /** Initial text when uncontrolled. Defaults to the selected item's label, without `multiple`. */
@@ -107,7 +107,7 @@ type ComboboxInputOwnProps = {
107
107
  type ComboboxInputProps = ComboboxInputOwnProps & Omit<ComponentPropsWithRef<'input'>, keyof ComboboxInputOwnProps | 'value' | 'defaultValue'>;
108
108
  /**
109
109
  * The text input: `role="combobox"`, and the element focus stays on. Its text is the Root's
110
- * `inputValue`. It is what the popup is anchored to.
110
+ * `inputValue`. It is what the popup is anchored to, unless an InputGroup wraps it.
111
111
  *
112
112
  * Typing opens the list and filters it. Up and Down open it, or move the highlight, wrapping;
113
113
  * Home and End jump to the ends while it is open; Alt+Down opens it without highlighting.
@@ -119,6 +119,22 @@ type ComboboxInputProps = ComboboxInputOwnProps & Omit<ComponentPropsWithRef<'in
119
119
  */
120
120
  declare function ComboboxInput({ className, render, ...rest }: ComboboxInputProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
121
121
 
122
+ type ComboboxInputGroupOwnProps = {
123
+ /** Element to render instead of the default `<div>`. Props and ref are merged onto it. */
124
+ render?: ReactElement;
125
+ /** Ref to the rendered element. Merged with any ref on the `render` element. */
126
+ ref?: Ref<HTMLElement>;
127
+ };
128
+ type ComboboxInputGroupProps = ComboboxInputGroupOwnProps & Omit<ComponentPropsWithRef<'div'>, keyof ComboboxInputGroupOwnProps>;
129
+ /**
130
+ * The field around the Input — chips, Clear and Trigger with it — when they are drawn as one
131
+ * box. It takes over as the popup's anchor, so the list lines up with the whole field rather
132
+ * than the text alone, and a press on it is not a press outside.
133
+ *
134
+ * A press on the box itself, between its children, focuses the input.
135
+ */
136
+ declare function ComboboxInputGroup({ className, children, render, ...rest }: ComboboxInputGroupProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
137
+
122
138
  type ComboboxTriggerOwnProps = {
123
139
  /** Element to render instead of the default `<button>`. Props and ref are merged onto it. */
124
140
  render?: ReactElement;
@@ -273,8 +289,26 @@ type ComboboxChipRemoveProps = ComboboxChipRemoveOwnProps & Omit<ComponentPropsW
273
289
  */
274
290
  declare function ComboboxChipRemove({ className, children, render, ...rest }: ComboboxChipRemoveProps): ReactElement<unknown, string | react.JSXElementConstructor<any>>;
275
291
 
292
+ type ComboboxValueProps<T = unknown> = {
293
+ /**
294
+ * Renders the selection: `T | null`, or `T[]` with `multiple`. The Root's `itemToString` is
295
+ * passed along for labels — a chip's text, say.
296
+ */
297
+ children: (value: T, details: {
298
+ itemToString: (item: unknown) => string;
299
+ }) => ReactNode;
300
+ };
301
+ /**
302
+ * The current selection, for whatever shows it outside the input — chips, most often. Renders
303
+ * no element of its own.
304
+ *
305
+ * It exists so that a separate styling package can show the selection without owning a copy
306
+ * of it (decision 9): the value stays the Root's.
307
+ */
308
+ declare function ComboboxValue<T = unknown>({ children }: ComboboxValueProps<T>): react.JSX.Element;
309
+
276
310
  declare namespace index_parts {
277
- export { ComboboxChip as Chip, ComboboxChipRemove as ChipRemove, ComboboxClear as Clear, ComboboxEmpty as Empty, ComboboxInput as Input, ComboboxItem as Item, ComboboxList as List, ComboboxPopup as Popup, ComboboxRoot as Root, ComboboxStatus as Status, ComboboxTrigger as Trigger };
311
+ export { ComboboxChip as Chip, ComboboxChipRemove as ChipRemove, ComboboxClear as Clear, ComboboxEmpty as Empty, ComboboxInput as Input, ComboboxInputGroup as InputGroup, ComboboxItem as Item, ComboboxList as List, ComboboxPopup as Popup, ComboboxRoot as Root, ComboboxStatus as Status, ComboboxTrigger as Trigger, ComboboxValue as Value };
278
312
  }
279
313
 
280
314
  /**
@@ -285,4 +319,4 @@ declare namespace index_parts {
285
319
  */
286
320
  declare function defaultFilter<T>(items: readonly T[], query: string, itemToString: (item: T) => string): T[];
287
321
 
288
- export { index_parts as Combobox, type ComboboxAlign, type ComboboxChangeDetails, type ComboboxChangeReason, type ComboboxChipProps, type ComboboxChipRemoveProps, type ComboboxClearProps, type ComboboxEmptyProps, type ComboboxFilter, type ComboboxHighlightDetails, type ComboboxHighlightReason, type ComboboxInputProps, type ComboboxItemProps, type ComboboxListProps, type ComboboxPopupProps, type ComboboxRootProps, type ComboboxSide, type ComboboxState, type ComboboxStatusProps, type ComboboxTriggerProps, type ComboboxValue, defaultFilter };
322
+ export { index_parts as Combobox, type ComboboxAlign, type ComboboxChangeDetails, type ComboboxChangeReason, type ComboboxChipProps, type ComboboxChipRemoveProps, type ComboboxClearProps, type ComboboxEmptyProps, type ComboboxFilter, type ComboboxHighlightDetails, type ComboboxHighlightReason, type ComboboxInputGroupProps, type ComboboxInputProps, type ComboboxItemProps, type ComboboxListProps, type ComboboxPopupProps, type ComboboxRootProps, type ComboboxSide, type ComboboxState, type ComboboxStatusProps, type ComboboxTriggerProps, type ComboboxValue$1 as ComboboxValue, type ComboboxValueProps, defaultFilter };
@@ -23,12 +23,14 @@ __export(index_parts_exports, {
23
23
  Clear: () => ComboboxClear,
24
24
  Empty: () => ComboboxEmpty,
25
25
  Input: () => ComboboxInput,
26
+ InputGroup: () => ComboboxInputGroup,
26
27
  Item: () => ComboboxItem,
27
28
  List: () => ComboboxList,
28
29
  Popup: () => ComboboxPopup,
29
30
  Root: () => ComboboxRoot,
30
31
  Status: () => ComboboxStatus,
31
- Trigger: () => ComboboxTrigger
32
+ Trigger: () => ComboboxTrigger,
33
+ Value: () => ComboboxValue
32
34
  });
33
35
 
34
36
  // src/combobox/ComboboxRoot.tsx
@@ -248,6 +250,11 @@ function ComboboxRoot({
248
250
  },
249
251
  [insideRefs]
250
252
  );
253
+ const [hasGroup, setHasGroup] = useState(false);
254
+ const registerGroup = useCallback(() => {
255
+ setHasGroup(true);
256
+ return () => setHasGroup(false);
257
+ }, []);
251
258
  const inputRef = useRef(null);
252
259
  const popupRef = useRef(null);
253
260
  const { current: initial } = useRef({ value, inputValue });
@@ -298,6 +305,8 @@ function ComboboxRoot({
298
305
  chips,
299
306
  registerInside,
300
307
  isInside,
308
+ hasGroup,
309
+ registerGroup,
301
310
  onLoadMore,
302
311
  inputId: `${id}-input`,
303
312
  listId: `${id}-list`,
@@ -332,6 +341,8 @@ function ComboboxRoot({
332
341
  chips,
333
342
  registerInside,
334
343
  isInside,
344
+ hasGroup,
345
+ registerGroup,
335
346
  onLoadMore,
336
347
  id
337
348
  ]
@@ -403,6 +414,7 @@ function ComboboxInput({ className, render, ...rest }) {
403
414
  listId,
404
415
  optionId,
405
416
  anchorName,
417
+ hasGroup,
406
418
  inputRef,
407
419
  popupRef
408
420
  } = useComboboxRootContext("Input");
@@ -450,7 +462,8 @@ function ComboboxInput({ className, render, ...rest }) {
450
462
  disabled: disabled || void 0,
451
463
  value: inputValue,
452
464
  ...comboboxDataAttributes({ open, multiple, disabled }),
453
- style: { anchorName },
465
+ // The anchor, unless an InputGroup around it is.
466
+ style: hasGroup ? void 0 : { anchorName },
454
467
  className,
455
468
  ref: inputRef,
456
469
  onChange(event) {
@@ -517,12 +530,43 @@ function ComboboxInput({ className, render, ...rest }) {
517
530
  });
518
531
  }
519
532
 
520
- // src/combobox/ComboboxTrigger.tsx
533
+ // src/combobox/ComboboxInputGroup.tsx
521
534
  import { useLayoutEffect, useRef as useRef2 } from "react";
522
- function ComboboxTrigger({ className, children, render, ...rest }) {
523
- const { open, multiple, disabled, setOpen, close, listId, inputRef, registerInside } = useComboboxRootContext("Trigger");
535
+ function ComboboxInputGroup({
536
+ className,
537
+ children,
538
+ render,
539
+ ...rest
540
+ }) {
541
+ const { open, multiple, disabled, anchorName, registerGroup, registerInside, inputRef } = useComboboxRootContext("InputGroup");
524
542
  const elementRef = useRef2(null);
543
+ useLayoutEffect(() => registerGroup(), [registerGroup]);
525
544
  useLayoutEffect(() => registerInside(elementRef), [registerInside]);
545
+ return useRender({
546
+ render,
547
+ defaultTagName: "div",
548
+ props: {
549
+ ...comboboxDataAttributes({ open, multiple, disabled }),
550
+ style: { anchorName },
551
+ className,
552
+ children,
553
+ ref: elementRef,
554
+ onMouseDown(event) {
555
+ if (event.target !== event.currentTarget || disabled) return;
556
+ event.preventDefault();
557
+ inputRef.current?.focus();
558
+ }
559
+ },
560
+ consumerProps: rest
561
+ });
562
+ }
563
+
564
+ // src/combobox/ComboboxTrigger.tsx
565
+ import { useLayoutEffect as useLayoutEffect2, useRef as useRef3 } from "react";
566
+ function ComboboxTrigger({ className, children, render, ...rest }) {
567
+ const { open, multiple, disabled, setOpen, close, listId, inputRef, registerInside } = useComboboxRootContext("Trigger");
568
+ const elementRef = useRef3(null);
569
+ useLayoutEffect2(() => registerInside(elementRef), [registerInside]);
526
570
  return useRender({
527
571
  render,
528
572
  defaultTagName: "button",
@@ -550,11 +594,11 @@ function ComboboxTrigger({ className, children, render, ...rest }) {
550
594
  }
551
595
 
552
596
  // src/combobox/ComboboxClear.tsx
553
- import { useLayoutEffect as useLayoutEffect2, useRef as useRef3 } from "react";
597
+ import { useLayoutEffect as useLayoutEffect3, useRef as useRef4 } from "react";
554
598
  function ComboboxClear({ className, children, render, ...rest }) {
555
599
  const { disabled, selectedItems, clear, inputRef, registerInside } = useComboboxRootContext("Clear");
556
- const elementRef = useRef3(null);
557
- useLayoutEffect2(() => registerInside(elementRef), [registerInside]);
600
+ const elementRef = useRef4(null);
601
+ useLayoutEffect3(() => registerInside(elementRef), [registerInside]);
558
602
  return useRender({
559
603
  render,
560
604
  defaultTagName: "button",
@@ -637,7 +681,7 @@ function ComboboxPopup({
637
681
  }
638
682
 
639
683
  // src/combobox/ComboboxList.tsx
640
- import { useEffect as useEffect3, useRef as useRef4 } from "react";
684
+ import { useEffect as useEffect3, useRef as useRef5 } from "react";
641
685
  import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
642
686
  function ComboboxList({
643
687
  className,
@@ -666,8 +710,8 @@ function ComboboxList({
666
710
  }
667
711
  function LoadMoreSentinel() {
668
712
  const { open, loading, filteredItems, onLoadMore } = useComboboxRootContext("List");
669
- const ref = useRef4(null);
670
- const onLoadMoreRef = useRef4(onLoadMore);
713
+ const ref = useRef5(null);
714
+ const onLoadMoreRef = useRef5(onLoadMore);
671
715
  onLoadMoreRef.current = onLoadMore;
672
716
  useEffect3(() => {
673
717
  const element = ref.current;
@@ -682,7 +726,7 @@ function LoadMoreSentinel() {
682
726
  }
683
727
 
684
728
  // src/combobox/ComboboxItem.tsx
685
- import { useEffect as useEffect4, useLayoutEffect as useLayoutEffect3, useRef as useRef5, useSyncExternalStore as useSyncExternalStore2 } from "react";
729
+ import { useEffect as useEffect4, useLayoutEffect as useLayoutEffect4, useRef as useRef6, useSyncExternalStore as useSyncExternalStore2 } from "react";
686
730
  function ComboboxItem({
687
731
  value,
688
732
  disabled = false,
@@ -709,8 +753,8 @@ function ComboboxItem({
709
753
  () => index >= 0 && highlight.get().index === index,
710
754
  () => false
711
755
  );
712
- const elementRef = useRef5(null);
713
- useLayoutEffect3(() => {
756
+ const elementRef = useRef6(null);
757
+ useLayoutEffect4(() => {
714
758
  if (!disabled) return;
715
759
  disabledKeys.add(key);
716
760
  return () => {
@@ -772,7 +816,7 @@ function ComboboxStatus({ className, children, render, ...rest }) {
772
816
  }
773
817
 
774
818
  // src/combobox/ComboboxChip.tsx
775
- import { createContext as createContext2, useContext as useContext2, useLayoutEffect as useLayoutEffect4, useMemo as useMemo2, useRef as useRef6 } from "react";
819
+ import { createContext as createContext2, useContext as useContext2, useLayoutEffect as useLayoutEffect5, useMemo as useMemo2, useRef as useRef7 } from "react";
776
820
  import { jsx as jsx3 } from "react/jsx-runtime";
777
821
  var ComboboxChipContext = createContext2(null);
778
822
  function useComboboxChipContext() {
@@ -791,8 +835,8 @@ function ComboboxChip({
791
835
  }) {
792
836
  const { disabled, itemToKey, remove, registerChip, chips, inputRef } = useComboboxRootContext("Chip");
793
837
  const key = itemToKey(value);
794
- const elementRef = useRef6(null);
795
- useLayoutEffect4(() => registerChip({ key, ref: elementRef }), [registerChip, key]);
838
+ const elementRef = useRef7(null);
839
+ useLayoutEffect5(() => registerChip({ key, ref: elementRef }), [registerChip, key]);
796
840
  const chipContext = useMemo2(() => ({ value }), [value]);
797
841
  function focusAt(index) {
798
842
  const chip = chips[index];
@@ -867,6 +911,14 @@ function ComboboxChipRemove({
867
911
  consumerProps: rest
868
912
  });
869
913
  }
914
+
915
+ // src/combobox/ComboboxValue.tsx
916
+ import { Fragment as Fragment2, jsx as jsx4 } from "react/jsx-runtime";
917
+ function ComboboxValue({ children }) {
918
+ const { multiple, selectedItems, itemToString } = useComboboxRootContext("Value");
919
+ const value = multiple ? selectedItems : selectedItems[0] ?? null;
920
+ return /* @__PURE__ */ jsx4(Fragment2, { children: children(value, { itemToString }) });
921
+ }
870
922
  export {
871
923
  index_parts_exports as Combobox,
872
924
  defaultFilter
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arun-dev/headless",
3
- "version": "4.8.0",
3
+ "version": "4.9.0",
4
4
  "description": "Unstyled React behaviour primitives — render engine, controlled/uncontrolled state, and data-* state projection. Ships no CSS and no class names.",
5
5
  "license": "MIT",
6
6
  "type": "module",