Skip to content

Commit 928aa52

Browse files
committed
[combobox] Simplify keyed value mode bookkeeping
1 parent 0d0f2a9 commit 928aa52

6 files changed

Lines changed: 76 additions & 129 deletions

File tree

docs/src/app/(docs)/react/components/page.mdx

Lines changed: 39 additions & 79 deletions
Large diffs are not rendered by default.

docs/src/app/(docs)/react/handbook/page.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
## Styling
1616

17-
A guide to styling Base UI components with your preferred styling engine.
17+
Learn how to style Base UI components with your preferred styling engine.
1818

1919
<details>
2020

docs/src/app/(docs)/react/overview/page.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ Changelogs for each Base UI release.
7070

7171
## About Base UI
7272

73-
An open-source React component library for building accessible user interfaces.
73+
An overview of Base UI, providing information on its history, team, and goals.
7474

7575
<details>
7676

docs/src/app/(docs)/react/utils/page.mdx

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
## CSP Provider
1414

15-
Configures CSP-related behavior for inline tags rendered by Base UI components.
15+
A CSP provider component that applies a nonce to inline and tags rendered by Base UI components, and can disable inline elements.
1616

1717
<details>
1818

@@ -28,15 +28,14 @@ Configures CSP-related behavior for inline tags rendered by Base UI components.
2828
- Exports:
2929
- CSPProvider
3030
- Props: children, disableStyleElements, nonce
31-
- Types: CSPProvider.Props, CSPProvider.State
3231

3332
</details>
3433

3534
[Read more](./csp-provider/page.mdx)
3635

3736
## Direction Provider
3837

39-
Enables RTL behavior for Base UI components.
38+
A direction provider component that enables RTL behavior for Base UI components.
4039

4140
<details>
4241

@@ -52,15 +51,14 @@ Enables RTL behavior for Base UI components.
5251
- DirectionProvider
5352
- Props: children, direction
5453
- useDirection
55-
- Types: DirectionProvider.Props, DirectionProvider.State, TextDirection
5654

5755
</details>
5856

5957
[Read more](./direction-provider/page.mdx)
6058

6159
## mergeProps
6260

63-
A utility to merge multiple sets of React props.
61+
A utility to merge multiple sets of React props, handling event handlers, className, and style props intelligently.
6462

6563
<details>
6664

@@ -75,13 +73,9 @@ A utility to merge multiple sets of React props.
7573
- mergePropsN
7674
- Exports:
7775
- makeEventPreventable
78-
- Parameters: event
7976
- mergeClassNames
80-
- Parameters: ourClassName, theirClassName
8177
- mergeProps
82-
- Parameters: a, b, c, d, e
8378
- mergePropsN
84-
- Parameters: props
8579

8680
</details>
8781

@@ -107,16 +101,14 @@ Hook for enabling a render prop in custom components.
107101
- Exports:
108102
- ComponentRenderFn
109103
- useRender
110-
- Parameters: params
111-
- Types: HTMLProps, UseRenderComponentProps, UseRenderElementProps, UseRenderParameters, UseRenderRenderProp, UseRenderReturnValue, UseRenderState, useRender.ComponentProps, useRender.ElementProps, useRender.Parameters, useRender.RenderProp, useRender.ReturnValue, useRender.State
112104

113105
</details>
114106

115107
[Read more](./use-render/page.mdx)
116108

117109
## Localization Provider
118110

119-
Defines the locale to use in the temporal components.
111+
A localization provider component that defines the locale to use in the temporal components.
120112

121113
<details>
122114

@@ -133,7 +125,6 @@ Defines the locale to use in the temporal components.
133125
- LocalizationProvider
134126
- Props: children, temporalLocale
135127
- useTemporalLocale
136-
- Types: LocalizationProvider.Props
137128

138129
</details>
139130

packages/react/src/combobox/root/AriaCombobox.tsx

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ import { HTMLProps } from '../../utils/types';
4646
import { useValueChanged } from '../../utils/useValueChanged';
4747
import { NOOP } from '../../utils/noop';
4848
import {
49+
hasEmptySelectionValue,
50+
hasPrimitiveSelectionValue,
4951
inferItemValue,
5052
hasValueAndLabelItemsInput,
5153
mapItemValues,
@@ -64,24 +66,6 @@ import {
6466
} from '../../utils/itemEquality';
6567
import { INITIAL_LAST_HIGHLIGHT, NO_ACTIVE_VALUE } from './utils/constants';
6668

67-
function hasPrimitiveSelectionValue(value: any, multiple: boolean): boolean {
68-
if (multiple) {
69-
return (
70-
Array.isArray(value) && value.length > 0 && value.every((item) => typeof item !== 'object')
71-
);
72-
}
73-
74-
return value != null && typeof value !== 'object';
75-
}
76-
77-
function hasEmptySelectionValue(value: any, multiple: boolean): boolean {
78-
if (multiple) {
79-
return Array.isArray(value) && value.length === 0;
80-
}
81-
82-
return value == null;
83-
}
84-
8569
/**
8670
* @internal
8771
*/
@@ -205,19 +189,20 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
205189
name: 'Combobox',
206190
state: 'selectedValue',
207191
});
192+
208193
const hasPrimitiveSelectableItems =
209194
hasItems && selectionMode !== 'none' && hasValueAndLabelItemsInput(items);
210195
const hasEmptySelection = hasEmptySelectionValue(selectedValue, multiple);
211-
const shouldUsePrimitiveValuesForItems =
196+
const usePrimitiveValuesForItems =
212197
hasPrimitiveSelectableItems && hasPrimitiveSelectionValue(selectedValue, multiple);
213-
const shouldBypassItemToStringLabelForItems =
214-
hasPrimitiveSelectableItems && (shouldUsePrimitiveValuesForItems || hasEmptySelection);
215-
const shouldSkipPreMountItemRegistry =
216-
hasPrimitiveSelectableItems && !shouldUsePrimitiveValuesForItems && hasEmptySelection;
217-
const itemToStringLabelForItems = shouldBypassItemToStringLabelForItems
198+
const disableItemToStringLabelForItems =
199+
hasPrimitiveSelectableItems && (usePrimitiveValuesForItems || hasEmptySelection);
200+
const skipPreMountItemRegistry =
201+
hasPrimitiveSelectableItems && !usePrimitiveValuesForItems && hasEmptySelection;
202+
const itemToStringLabelForItems = disableItemToStringLabelForItems
218203
? undefined
219204
: itemToStringLabel;
220-
const getVisibleItemValue = shouldUsePrimitiveValuesForItems ? inferItemValue : undefined;
205+
const getVisibleItemValue = usePrimitiveValuesForItems ? inferItemValue : undefined;
221206

222207
const filter = React.useMemo(() => {
223208
if (filterProp === null) {
@@ -500,7 +485,7 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
500485

501486
const forceMount = useStableCallback(() => {
502487
if (items) {
503-
if (shouldSkipPreMountItemRegistry) {
488+
if (skipPreMountItemRegistry) {
504489
labelsRef.current = [];
505490
valuesRef.current = [];
506491
return;
@@ -868,22 +853,15 @@ export function AriaCombobox<Value = any, Mode extends SelectionMode = 'none'>(
868853
useIsoLayoutEffect(() => {
869854
if (items) {
870855
if (!open && !inlineProp) {
871-
if (shouldSkipPreMountItemRegistry) {
856+
if (skipPreMountItemRegistry) {
872857
valuesRef.current = [];
873858
} else {
874859
valuesRef.current = mapItemValues(flatFilteredItems, getVisibleItemValue);
875860
}
876861
}
877862
listRef.current.length = flatFilteredItems.length;
878863
}
879-
}, [
880-
items,
881-
flatFilteredItems,
882-
inlineProp,
883-
open,
884-
getVisibleItemValue,
885-
shouldSkipPreMountItemRegistry,
886-
]);
864+
}, [items, flatFilteredItems, inlineProp, open, getVisibleItemValue, skipPreMountItemRegistry]);
887865

888866
useIsoLayoutEffect(() => {
889867
const pendingHighlight = pendingQueryHighlightRef.current;

packages/react/src/utils/resolveValueLabel.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,24 @@ export function inferItemValue(item: any) {
112112
return item;
113113
}
114114

115+
export function hasPrimitiveSelectionValue(value: any, multiple: boolean): boolean {
116+
if (multiple) {
117+
return (
118+
Array.isArray(value) && value.length > 0 && value.every((item) => typeof item !== 'object')
119+
);
120+
}
121+
122+
return value != null && typeof value !== 'object';
123+
}
124+
125+
export function hasEmptySelectionValue(value: any, multiple: boolean): boolean {
126+
if (multiple) {
127+
return Array.isArray(value) && value.length === 0;
128+
}
129+
130+
return value == null;
131+
}
132+
115133
export function mapItemValues(
116134
items: readonly any[],
117135
getItemValue?: ((item: any) => any) | undefined,

0 commit comments

Comments
 (0)