@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.
- package/dist/combobox/index.cjs +79 -27
- package/dist/combobox/index.d.cts +41 -7
- package/dist/combobox/index.d.ts +41 -7
- package/dist/combobox/index.js +69 -17
- package/package.json +1 -1
package/dist/combobox/index.cjs
CHANGED
|
@@ -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
|
-
|
|
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/
|
|
716
|
+
// src/combobox/ComboboxInputGroup.tsx
|
|
704
717
|
var import_react6 = require("react");
|
|
705
|
-
function
|
|
706
|
-
|
|
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
|
|
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,
|
|
740
|
-
(0,
|
|
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
|
|
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,
|
|
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,
|
|
786
|
-
(0,
|
|
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
|
|
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,
|
|
853
|
-
const onLoadMoreRef = (0,
|
|
896
|
+
const ref = (0, import_react10.useRef)(null);
|
|
897
|
+
const onLoadMoreRef = (0, import_react10.useRef)(onLoadMore);
|
|
854
898
|
onLoadMoreRef.current = onLoadMore;
|
|
855
|
-
(0,
|
|
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
|
|
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,
|
|
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,
|
|
896
|
-
(0,
|
|
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,
|
|
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
|
|
1002
|
+
var import_react12 = require("react");
|
|
959
1003
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
960
|
-
var ComboboxChipContext = (0,
|
|
1004
|
+
var ComboboxChipContext = (0, import_react12.createContext)(null);
|
|
961
1005
|
function useComboboxChipContext() {
|
|
962
|
-
const context = (0,
|
|
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,
|
|
978
|
-
(0,
|
|
979
|
-
const chipContext = (0,
|
|
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 };
|
package/dist/combobox/index.d.ts
CHANGED
|
@@ -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 };
|
package/dist/combobox/index.js
CHANGED
|
@@ -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
|
-
|
|
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/
|
|
533
|
+
// src/combobox/ComboboxInputGroup.tsx
|
|
521
534
|
import { useLayoutEffect, useRef as useRef2 } from "react";
|
|
522
|
-
function
|
|
523
|
-
|
|
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
|
|
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 =
|
|
557
|
-
|
|
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
|
|
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 =
|
|
670
|
-
const onLoadMoreRef =
|
|
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
|
|
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 =
|
|
713
|
-
|
|
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
|
|
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 =
|
|
795
|
-
|
|
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.
|
|
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",
|