@cdx-ui/primitives 0.3.3 → 0.3.4

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.
Files changed (140) hide show
  1. package/lib/commonjs/select/SelectContent.js +89 -45
  2. package/lib/commonjs/select/SelectContent.js.map +1 -1
  3. package/lib/commonjs/select/SelectDismissButton.js +14 -0
  4. package/lib/commonjs/select/SelectDismissButton.js.map +1 -0
  5. package/lib/commonjs/select/SelectDismissButton.web.js +28 -0
  6. package/lib/commonjs/select/SelectDismissButton.web.js.map +1 -0
  7. package/lib/commonjs/select/SelectEmpty.js +53 -0
  8. package/lib/commonjs/select/SelectEmpty.js.map +1 -0
  9. package/lib/commonjs/select/SelectItem.js +30 -9
  10. package/lib/commonjs/select/SelectItem.js.map +1 -1
  11. package/lib/commonjs/select/SelectListbox.js +47 -0
  12. package/lib/commonjs/select/SelectListbox.js.map +1 -0
  13. package/lib/commonjs/select/SelectRoot.js +110 -21
  14. package/lib/commonjs/select/SelectRoot.js.map +1 -1
  15. package/lib/commonjs/select/SelectSearch.js +81 -0
  16. package/lib/commonjs/select/SelectSearch.js.map +1 -0
  17. package/lib/commonjs/select/SelectSearchStatus.js +14 -0
  18. package/lib/commonjs/select/SelectSearchStatus.js.map +1 -0
  19. package/lib/commonjs/select/SelectSearchStatus.web.js +24 -0
  20. package/lib/commonjs/select/SelectSearchStatus.web.js.map +1 -0
  21. package/lib/commonjs/select/SelectSheetContent.js +67 -0
  22. package/lib/commonjs/select/SelectSheetContent.js.map +1 -0
  23. package/lib/commonjs/select/context.js +5 -1
  24. package/lib/commonjs/select/context.js.map +1 -1
  25. package/lib/commonjs/select/filter.js +16 -0
  26. package/lib/commonjs/select/filter.js.map +1 -0
  27. package/lib/commonjs/select/index.js +34 -0
  28. package/lib/commonjs/select/index.js.map +1 -1
  29. package/lib/commonjs/select/popupRole.js +27 -0
  30. package/lib/commonjs/select/popupRole.js.map +1 -0
  31. package/lib/commonjs/select/useContentFocus.js +10 -21
  32. package/lib/commonjs/select/useContentFocus.js.map +1 -1
  33. package/lib/commonjs/select/useListboxNavigation.js +21 -8
  34. package/lib/commonjs/select/useListboxNavigation.js.map +1 -1
  35. package/lib/commonjs/select/useQueryHighlight.js +50 -0
  36. package/lib/commonjs/select/useQueryHighlight.js.map +1 -0
  37. package/lib/commonjs/select/useSelectTrigger.js +4 -1
  38. package/lib/commonjs/select/useSelectTrigger.js.map +1 -1
  39. package/lib/commonjs/select/useSelectTrigger.web.js +4 -1
  40. package/lib/commonjs/select/useSelectTrigger.web.js.map +1 -1
  41. package/lib/module/select/SelectContent.js +94 -48
  42. package/lib/module/select/SelectContent.js.map +1 -1
  43. package/lib/module/select/SelectDismissButton.js +10 -0
  44. package/lib/module/select/SelectDismissButton.js.map +1 -0
  45. package/lib/module/select/SelectDismissButton.web.js +24 -0
  46. package/lib/module/select/SelectDismissButton.web.js.map +1 -0
  47. package/lib/module/select/SelectEmpty.js +49 -0
  48. package/lib/module/select/SelectEmpty.js.map +1 -0
  49. package/lib/module/select/SelectItem.js +32 -11
  50. package/lib/module/select/SelectItem.js.map +1 -1
  51. package/lib/module/select/SelectListbox.js +43 -0
  52. package/lib/module/select/SelectListbox.js.map +1 -0
  53. package/lib/module/select/SelectRoot.js +111 -22
  54. package/lib/module/select/SelectRoot.js.map +1 -1
  55. package/lib/module/select/SelectSearch.js +77 -0
  56. package/lib/module/select/SelectSearch.js.map +1 -0
  57. package/lib/module/select/SelectSearchStatus.js +10 -0
  58. package/lib/module/select/SelectSearchStatus.js.map +1 -0
  59. package/lib/module/select/SelectSearchStatus.web.js +20 -0
  60. package/lib/module/select/SelectSearchStatus.web.js.map +1 -0
  61. package/lib/module/select/SelectSheetContent.js +63 -0
  62. package/lib/module/select/SelectSheetContent.js.map +1 -0
  63. package/lib/module/select/context.js +2 -0
  64. package/lib/module/select/context.js.map +1 -1
  65. package/lib/module/select/filter.js +11 -0
  66. package/lib/module/select/filter.js.map +1 -0
  67. package/lib/module/select/index.js +5 -1
  68. package/lib/module/select/index.js.map +1 -1
  69. package/lib/module/select/popupRole.js +23 -0
  70. package/lib/module/select/popupRole.js.map +1 -0
  71. package/lib/module/select/useContentFocus.js +11 -22
  72. package/lib/module/select/useContentFocus.js.map +1 -1
  73. package/lib/module/select/useListboxNavigation.js +21 -8
  74. package/lib/module/select/useListboxNavigation.js.map +1 -1
  75. package/lib/module/select/useQueryHighlight.js +46 -0
  76. package/lib/module/select/useQueryHighlight.js.map +1 -0
  77. package/lib/module/select/useSelectTrigger.js +4 -1
  78. package/lib/module/select/useSelectTrigger.js.map +1 -1
  79. package/lib/module/select/useSelectTrigger.web.js +4 -1
  80. package/lib/module/select/useSelectTrigger.web.js.map +1 -1
  81. package/lib/typescript/select/SelectContent.d.ts.map +1 -1
  82. package/lib/typescript/select/SelectDismissButton.d.ts +10 -0
  83. package/lib/typescript/select/SelectDismissButton.d.ts.map +1 -0
  84. package/lib/typescript/select/SelectDismissButton.web.d.ts +9 -0
  85. package/lib/typescript/select/SelectDismissButton.web.d.ts.map +1 -0
  86. package/lib/typescript/select/SelectEmpty.d.ts +12 -0
  87. package/lib/typescript/select/SelectEmpty.d.ts.map +1 -0
  88. package/lib/typescript/select/SelectItem.d.ts.map +1 -1
  89. package/lib/typescript/select/SelectListbox.d.ts +12 -0
  90. package/lib/typescript/select/SelectListbox.d.ts.map +1 -0
  91. package/lib/typescript/select/SelectRoot.d.ts +2 -2
  92. package/lib/typescript/select/SelectRoot.d.ts.map +1 -1
  93. package/lib/typescript/select/SelectSearch.d.ts +18 -0
  94. package/lib/typescript/select/SelectSearch.d.ts.map +1 -0
  95. package/lib/typescript/select/SelectSearchStatus.d.ts +9 -0
  96. package/lib/typescript/select/SelectSearchStatus.d.ts.map +1 -0
  97. package/lib/typescript/select/SelectSearchStatus.web.d.ts +8 -0
  98. package/lib/typescript/select/SelectSearchStatus.web.d.ts.map +1 -0
  99. package/lib/typescript/select/SelectSheetContent.d.ts +14 -0
  100. package/lib/typescript/select/SelectSheetContent.d.ts.map +1 -0
  101. package/lib/typescript/select/context.d.ts +9 -0
  102. package/lib/typescript/select/context.d.ts.map +1 -1
  103. package/lib/typescript/select/filter.d.ts +4 -0
  104. package/lib/typescript/select/filter.d.ts.map +1 -0
  105. package/lib/typescript/select/index.d.ts +6 -2
  106. package/lib/typescript/select/index.d.ts.map +1 -1
  107. package/lib/typescript/select/popupRole.d.ts +18 -0
  108. package/lib/typescript/select/popupRole.d.ts.map +1 -0
  109. package/lib/typescript/select/types.d.ts +129 -8
  110. package/lib/typescript/select/types.d.ts.map +1 -1
  111. package/lib/typescript/select/useContentFocus.d.ts +4 -7
  112. package/lib/typescript/select/useContentFocus.d.ts.map +1 -1
  113. package/lib/typescript/select/useListboxNavigation.d.ts +16 -3
  114. package/lib/typescript/select/useListboxNavigation.d.ts.map +1 -1
  115. package/lib/typescript/select/useQueryHighlight.d.ts +21 -0
  116. package/lib/typescript/select/useQueryHighlight.d.ts.map +1 -0
  117. package/lib/typescript/select/useSelectTrigger.d.ts.map +1 -1
  118. package/lib/typescript/select/useSelectTrigger.web.d.ts.map +1 -1
  119. package/package.json +3 -3
  120. package/src/select/SelectContent.tsx +89 -54
  121. package/src/select/SelectDismissButton.tsx +14 -0
  122. package/src/select/SelectDismissButton.web.tsx +26 -0
  123. package/src/select/SelectEmpty.tsx +61 -0
  124. package/src/select/SelectItem.tsx +42 -10
  125. package/src/select/SelectListbox.tsx +43 -0
  126. package/src/select/SelectRoot.tsx +184 -12
  127. package/src/select/SelectSearch.tsx +98 -0
  128. package/src/select/SelectSearchStatus.tsx +11 -0
  129. package/src/select/SelectSearchStatus.web.tsx +18 -0
  130. package/src/select/SelectSheetContent.tsx +54 -0
  131. package/src/select/context.tsx +9 -0
  132. package/src/select/filter.ts +12 -0
  133. package/src/select/index.ts +14 -1
  134. package/src/select/popupRole.ts +26 -0
  135. package/src/select/types.ts +142 -9
  136. package/src/select/useContentFocus.ts +12 -31
  137. package/src/select/useListboxNavigation.ts +41 -9
  138. package/src/select/useQueryHighlight.ts +61 -0
  139. package/src/select/useSelectTrigger.ts +5 -1
  140. package/src/select/useSelectTrigger.web.ts +5 -1
@@ -0,0 +1,18 @@
1
+ import type { SelectContextValue } from './types';
2
+ type PopupRoleSource = Pick<SelectContextValue, 'hasSearch' | 'labelId' | 'accessibilityLabel' | 'triggerId'>;
3
+ /**
4
+ * Role and name of the popup surface. With `Select.Search` the popup is a dialog holding a
5
+ * searchbox and a listbox (a listbox may only own options), named by the field label, then
6
+ * `accessibilityLabel`. Without it the popup is the listbox.
7
+ */
8
+ export declare function getPopupRoleProps({ hasSearch, labelId, accessibilityLabel, triggerId, }: PopupRoleSource): {
9
+ role: "dialog";
10
+ 'aria-labelledby': string | undefined;
11
+ 'aria-label': string | undefined;
12
+ } | {
13
+ role: "listbox";
14
+ 'aria-labelledby': string;
15
+ readonly 'aria-label'?: undefined;
16
+ };
17
+ export {};
18
+ //# sourceMappingURL=popupRole.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"popupRole.d.ts","sourceRoot":"","sources":["../../../src/select/popupRole.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,KAAK,eAAe,GAAG,IAAI,CACzB,kBAAkB,EAClB,WAAW,GAAG,SAAS,GAAG,oBAAoB,GAAG,WAAW,CAC7D,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,SAAS,EACT,OAAO,EACP,kBAAkB,EAClB,SAAS,GACV,EAAE,eAAe;;;;;;;;EAQjB"}
@@ -1,5 +1,5 @@
1
1
  import type { ReactNode, RefObject } from 'react';
2
- import type { PressableProps, ViewProps } from 'react-native';
2
+ import type { PressableProps, TextInputProps, ViewProps } from 'react-native';
3
3
  import type { EntryOrExitLayoutType } from 'react-native-reanimated';
4
4
  import type { OverlayPlacement } from '../overlay';
5
5
  import type { InteractionState } from '../types';
@@ -7,15 +7,19 @@ export interface SelectItemInteractionState extends InteractionState {
7
7
  highlighted: boolean;
8
8
  checked: boolean;
9
9
  }
10
- export interface ISelectProps {
10
+ /** Selection mode for `Select`. */
11
+ export type SelectType = 'single' | 'multiple';
12
+ /**
13
+ * Decides whether an option stays visible for the current search query. Receives the option's
14
+ * `label` from `items` and the query.
15
+ */
16
+ export type SelectFilter = (textValue: string, inputValue: string) => boolean;
17
+ interface ISelectBaseProps {
11
18
  /**
12
19
  * Options used to resolve `Select.Value` labels and drive keyboard navigation.
13
20
  * Render matching `Select.Item` elements inside `Select.Content`.
14
21
  */
15
22
  items: SelectItemData[];
16
- value?: string;
17
- defaultValue?: string;
18
- onValueChange?: (value: string) => void;
19
23
  /** Controlled open state. Use `defaultOpen` for the uncontrolled variant. */
20
24
  isOpen?: boolean;
21
25
  defaultOpen?: boolean;
@@ -27,8 +31,54 @@ export interface ISelectProps {
27
31
  /** Prevents `Select.Content` from rendering. Reserved for native picker integration. */
28
32
  native?: boolean;
29
33
  accessibilityLabel?: string;
34
+ /** Controlled search query typed into `Select.Search`. */
35
+ inputValue?: string;
36
+ /** Uncontrolled initial search query. */
37
+ defaultInputValue?: string;
38
+ /**
39
+ * Called when the search query changes, including the reset to `''` when the popup closes.
40
+ * Pair with `inputValue` to replace `items` with server results.
41
+ */
42
+ onInputChange?: (inputValue: string) => void;
43
+ /**
44
+ * Decides which options stay visible for a non-empty query. Every option is visible while the
45
+ * query is empty.
46
+ * @default case- and accent-insensitive `contains`
47
+ */
48
+ filter?: SelectFilter;
30
49
  children?: ReactNode;
31
50
  }
51
+ export interface ISelectSingleProps extends ISelectBaseProps {
52
+ /**
53
+ * Selection mode. `single` selects one option and closes the popup.
54
+ * @default 'single'
55
+ */
56
+ type?: 'single';
57
+ /** Controlled selected value. */
58
+ value?: string;
59
+ /** Uncontrolled initial value. */
60
+ defaultValue?: string;
61
+ /** Called with the newly selected value. */
62
+ onValueChange?: (value: string) => void;
63
+ }
64
+ export interface ISelectMultipleProps extends ISelectBaseProps {
65
+ /**
66
+ * Selection mode. `multiple` toggles options on press and keeps the popup open.
67
+ * Each toggle commits immediately.
68
+ */
69
+ type: 'multiple';
70
+ /** Controlled selected values. An empty array means no selection. */
71
+ value?: string[];
72
+ /** Uncontrolled initial values. */
73
+ defaultValue?: string[];
74
+ /** Called with the full array of selected values after each toggle. */
75
+ onValueChange?: (value: string[]) => void;
76
+ }
77
+ /**
78
+ * Discriminated union: `type` narrows `value` / `defaultValue` / `onValueChange` to `string`
79
+ * (single, the default) or `string[]` (multiple).
80
+ */
81
+ export type ISelectProps = ISelectSingleProps | ISelectMultipleProps;
32
82
  export interface ISelectTriggerProps extends PressableProps {
33
83
  isHovered?: boolean;
34
84
  isActive?: boolean;
@@ -38,9 +88,22 @@ export interface ISelectTriggerProps extends PressableProps {
38
88
  /** @platform web — merged with form field helper/error ids */
39
89
  'aria-describedby'?: string;
40
90
  }
91
+ /** State passed to a `Select.Value` function child. */
92
+ export interface SelectValueRenderProps {
93
+ /** `true` when nothing is selected. */
94
+ isPlaceholder: boolean;
95
+ /** Selected options, in `items` order. Values missing from `items` are omitted. */
96
+ selectedItems: SelectItemData[];
97
+ /** Labels of `selectedItems` joined with `', '`. Empty when nothing is selected. */
98
+ selectedText: string;
99
+ }
41
100
  export interface ISelectValueProps {
42
101
  placeholder?: string;
43
- children?: ReactNode;
102
+ /**
103
+ * Replaces the default display. A function child receives the selection state; return
104
+ * `null` or `undefined` to fall back to the default (placeholder or `selectedText`).
105
+ */
106
+ children?: ReactNode | ((state: SelectValueRenderProps) => ReactNode);
44
107
  }
45
108
  export interface ISelectContentProps extends ViewProps {
46
109
  /** Preferred dropdown placement relative to the trigger. Defaults to `bottom-start`. */
@@ -53,6 +116,31 @@ export interface ISelectContentProps extends ViewProps {
53
116
  enteringAnimation?: EntryOrExitLayoutType;
54
117
  /** Exit animation for the dropdown. Defaults to a 100ms fade. */
55
118
  exitingAnimation?: EntryOrExitLayoutType;
119
+ /**
120
+ * Accessible name of the visually hidden button that lets screen-reader users close the
121
+ * popup on web.
122
+ * @default 'Dismiss'
123
+ */
124
+ dismissAccessibilityLabel?: string;
125
+ children?: ReactNode;
126
+ }
127
+ export interface ISelectSheetContentProps extends ViewProps {
128
+ children?: ReactNode;
129
+ }
130
+ export interface ISelectSearchProps extends Omit<TextInputProps, 'value' | 'defaultValue' | 'onChangeText' | 'children'> {
131
+ /** Accessible name of the search input. Recommended, because a placeholder is not a label. */
132
+ accessibilityLabel?: string;
133
+ /**
134
+ * Text the polite live region announces when the result count changes, such as
135
+ * `"3 results"` (the English default). When nothing matches, the `Select.Empty` text is
136
+ * announced instead.
137
+ */
138
+ getResultCountLabel?: (count: number) => string;
139
+ }
140
+ export interface ISelectListboxProps extends ViewProps {
141
+ children?: ReactNode;
142
+ }
143
+ export interface ISelectEmptyProps extends ViewProps {
56
144
  children?: ReactNode;
57
145
  }
58
146
  export interface ISelectItemProps extends PressableProps {
@@ -70,6 +158,7 @@ export interface ISelectItemLabelProps {
70
158
  export interface SelectItemData {
71
159
  value: string;
72
160
  label: string;
161
+ /** Prevents the option from being selected or highlighted by keyboard. */
73
162
  disabled?: boolean;
74
163
  }
75
164
  export interface SelectItemCollectionContextValue {
@@ -81,8 +170,22 @@ export interface SelectContextValue {
81
170
  open: boolean;
82
171
  items: SelectItemData[];
83
172
  setOpen: (open: boolean) => void;
84
- value: string | undefined;
85
- onValueChange: (value: string) => void;
173
+ type: SelectType;
174
+ /** Raw selection: `string` in single mode, `string[]` in multiple mode. */
175
+ value: string | string[] | undefined;
176
+ /** Selection normalized to an array for both modes. */
177
+ selectedValues: string[];
178
+ isSelected: (value: string) => boolean;
179
+ /** Adds or removes `value` from the selection without closing. */
180
+ toggleValue: (value: string) => void;
181
+ /** Replaces the selection with `value` and closes the popup. */
182
+ selectValueAndClose: (value: string) => void;
183
+ /** Commits an option press: `toggleValue` in multiple mode, `selectValueAndClose` in single. */
184
+ commitValue: (value: string) => void;
185
+ /** Disabled via the root, `SelectItemData.disabled`, or a mounted `Select.Item disabled`. */
186
+ isItemDisabled: (value: string) => boolean;
187
+ /** Called by `Select.Item` so item-level `disabled` reaches keyboard navigation. */
188
+ registerDisabledItem: (value: string) => () => void;
86
189
  disabled: boolean;
87
190
  required: boolean;
88
191
  invalid: boolean;
@@ -91,6 +194,23 @@ export interface SelectContextValue {
91
194
  triggerRef: RefObject<any>;
92
195
  contentId: string;
93
196
  triggerId: string;
197
+ /** Current search query. Reset to `''` when the popup closes. */
198
+ inputValue: string;
199
+ setInputValue: (inputValue: string) => void;
200
+ /** `items` that pass `filter` for the current query, in `items` order. */
201
+ visibleItems: SelectItemData[];
202
+ isItemVisible: (value: string) => boolean;
203
+ /** `true` while a `Select.Search` is mounted: the popup becomes a dialog around the listbox. */
204
+ hasSearch: boolean;
205
+ /** Called by `Select.Search` on mount. Returns the unregister function. */
206
+ registerSearch: () => () => void;
207
+ /** Plain text of `Select.Empty`, announced when nothing matches. */
208
+ emptyText: string | undefined;
209
+ setEmptyText: (text: string | undefined) => void;
210
+ /** Id of the listbox inside the dialog when `hasSearch` is `true`. */
211
+ listboxId: string;
212
+ /** `Field.Label` id when the select is inside a `Field`. */
213
+ labelId?: string;
94
214
  /** Currently highlighted value during keyboard navigation */
95
215
  activeValue: string | undefined;
96
216
  setActiveValue: (value: string | undefined) => void;
@@ -98,4 +218,5 @@ export interface SelectContextValue {
98
218
  /** From `Form.Field` via `useFormControl` on the select root (helper/error `aria-describedby`). */
99
219
  ariaDescribedBy?: string;
100
220
  }
221
+ export {};
101
222
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/select/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/select/types.ts"],"names":[],"mappings":"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"}
@@ -1,19 +1,16 @@
1
1
  import type { RefObject } from 'react';
2
- import type { SelectItemData } from './types';
3
2
  export interface UseContentFocusOptions {
4
3
  open: boolean;
5
4
  triggerRef: RefObject<any>;
6
- items: SelectItemData[];
7
- value: string | undefined;
8
5
  setActiveValue: (value: string | undefined) => void;
9
6
  }
10
7
  /**
11
8
  * Manages focus lifecycle for overlay content:
9
+ * - Clears `activeValue` when the overlay closes
12
10
  * - Restores focus to the trigger when the overlay closes (web)
13
- * - Initializes `activeValue` to the current value or first enabled item
14
11
  *
15
- * Initial focus on open is handled via a ref callback in SelectContent
16
- * to avoid timing issues with deferred portal rendering.
12
+ * Initial focus on open is handled by `FocusScope autoFocus` in SelectContent, and the
13
+ * highlight while open by `useQueryHighlight`.
17
14
  */
18
- export declare function useContentFocus({ open, triggerRef, items, value, setActiveValue, }: UseContentFocusOptions): void;
15
+ export declare function useContentFocus({ open, triggerRef, setActiveValue }: UseContentFocusOptions): void;
19
16
  //# sourceMappingURL=useContentFocus.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useContentFocus.d.ts","sourceRoot":"","sources":["../../../src/select/useContentFocus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAI9C,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;IAC3B,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;CACrD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,UAAU,EACV,KAAK,EACL,KAAK,EACL,cAAc,GACf,EAAE,sBAAsB,QAwBxB"}
1
+ {"version":3,"file":"useContentFocus.d.ts","sourceRoot":"","sources":["../../../src/select/useContentFocus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;IAC3B,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;CACrD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,EAAE,sBAAsB,QAc3F"}
@@ -1,12 +1,25 @@
1
- import type { SelectItemData } from './types';
1
+ import type { SelectItemData, SelectType } from './types';
2
2
  export interface UseListboxNavigationOptions {
3
+ /** Options to navigate, in display order. Pass the filtered list when a search is present. */
3
4
  items: SelectItemData[];
4
5
  activeValue: string | undefined;
5
6
  setActiveValue: (value: string | undefined) => void;
6
- onSelect: (value: string) => void;
7
+ type: SelectType;
8
+ /** Commit for `type="multiple"`: toggles the highlighted option and keeps the listbox open. */
9
+ onToggle: (value: string) => void;
10
+ /** Commit for `type="single"`: selects the highlighted option and closes the listbox. */
11
+ onSelectAndClose: (value: string) => void;
12
+ /** Excludes options from keyboard highlight and commit. */
13
+ isItemDisabled: (value: string) => boolean;
7
14
  onDismiss: () => void;
15
+ /**
16
+ * Keys arrive from the `Select.Search` input (virtual focus). Space types instead of
17
+ * committing, Home/End move the caret, and Enter is always consumed so the input neither
18
+ * blurs nor submits an enclosing form.
19
+ */
20
+ isSearch?: boolean;
8
21
  }
9
- export declare function useListboxNavigation({ items, activeValue, setActiveValue, onSelect, onDismiss, }: UseListboxNavigationOptions): {
22
+ export declare function useListboxNavigation({ items, activeValue, setActiveValue, type, onToggle, onSelectAndClose, isItemDisabled, onDismiss, isSearch, }: UseListboxNavigationOptions): {
10
23
  handleKeyDown: (e: any) => void;
11
24
  getEnabledItems: () => SelectItemData[];
12
25
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useListboxNavigation.d.ts","sourceRoot":"","sources":["../../../src/select/useListboxNavigation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAI9C,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACpD,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,WAAW,EACX,cAAc,EACd,QAAQ,EACR,SAAS,GACV,EAAE,2BAA2B;uBAUtB,GAAG;;EA0DV"}
1
+ {"version":3,"file":"useListboxNavigation.d.ts","sourceRoot":"","sources":["../../../src/select/useListboxNavigation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAI1D,MAAM,WAAW,2BAA2B;IAC1C,8FAA8F;IAC9F,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACpD,IAAI,EAAE,UAAU,CAAC;IACjB,+FAA+F;IAC/F,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,yFAAyF;IACzF,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,2DAA2D;IAC3D,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3C,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAKD,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,WAAW,EACX,cAAc,EACd,IAAI,EACJ,QAAQ,EACR,gBAAgB,EAChB,cAAc,EACd,SAAS,EACT,QAAgB,GACjB,EAAE,2BAA2B;uBAatB,GAAG;;EAmEV"}
@@ -0,0 +1,21 @@
1
+ import type { SelectItemData } from './types';
2
+ export interface UseQueryHighlightOptions {
3
+ open: boolean;
4
+ inputValue: string;
5
+ visibleItems: SelectItemData[];
6
+ selectedValues: string[];
7
+ isItemDisabled: (value: string) => boolean;
8
+ activeValue: string | undefined;
9
+ setActiveValue: (value: string | undefined) => void;
10
+ }
11
+ /**
12
+ * Owns the highlight while open:
13
+ * - Unset (on open, or once async `items` arrive): the first enabled selected item, or the
14
+ * first enabled item.
15
+ * - Query change: the first enabled match.
16
+ * - Hidden by the filter or a swapped `items` list: the first enabled item.
17
+ *
18
+ * Otherwise the highlight stays put, so a multiple-mode toggle does not move it.
19
+ */
20
+ export declare function useQueryHighlight({ open, inputValue, visibleItems, selectedValues, isItemDisabled, activeValue, setActiveValue, }: UseQueryHighlightOptions): void;
21
+ //# sourceMappingURL=useQueryHighlight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useQueryHighlight.d.ts","sourceRoot":"","sources":["../../../src/select/useQueryHighlight.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3C,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;CACrD;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,UAAU,EACV,YAAY,EACZ,cAAc,EACd,cAAc,EACd,WAAW,EACX,cAAc,GACf,EAAE,wBAAwB,QA8B1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"useSelectTrigger.d.ts","sourceRoot":"","sources":["../../../src/select/useSelectTrigger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useSelectTrigger.d.ts","sourceRoot":"","sources":["../../../src/select/useSelectTrigger.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useSelectTrigger.web.d.ts","sourceRoot":"","sources":["../../../src/select/useSelectTrigger.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,YAAY,EAElB,MAAM,OAAO,CAAC;AAaf,OAAO,KAAK,EACV,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAS5B;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,YAAY,CAAC,OAAO,CAAC,EACnC,OAAO,EAAE,uBAAuB,GAC/B,sBAAsB,CA6NxB;AAED,YAAY,EACV,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,GACvB,CAAC"}
1
+ {"version":3,"file":"useSelectTrigger.web.d.ts","sourceRoot":"","sources":["../../../src/select/useSelectTrigger.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,YAAY,EAElB,MAAM,OAAO,CAAC;AAaf,OAAO,KAAK,EACV,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAS5B;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,YAAY,CAAC,OAAO,CAAC,EACnC,OAAO,EAAE,uBAAuB,GAC/B,sBAAsB,CAiOxB;AAED,YAAY,EACV,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,GACvB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdx-ui/primitives",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -69,8 +69,8 @@
69
69
  "@react-stately/disclosure": "3.1.1",
70
70
  "@react-stately/radio": "3.12.0",
71
71
  "@react-stately/toggle": "3.9.4",
72
- "@cdx-ui/formatters": "0.3.3",
73
- "@cdx-ui/utils": "0.3.3"
72
+ "@cdx-ui/formatters": "0.3.4",
73
+ "@cdx-ui/utils": "0.3.4"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@types/react": "19.2.14",
@@ -1,14 +1,29 @@
1
- import React, { forwardRef, useCallback, useRef, type ComponentRef } from 'react';
1
+ import React, { forwardRef, useCallback, useMemo, useRef, type ComponentRef } from 'react';
2
2
  import { Platform, View } from 'react-native';
3
3
  import { FadeIn, FadeOut } from 'react-native-reanimated';
4
4
  import { composeEventHandlers, mergeRefs } from '@cdx-ui/utils';
5
+ import { FocusScope } from '@react-native-aria/focus';
5
6
  import { OverlayContainer, useAnchoredOverlay, useDismissOverlay, useEscapeKey } from '../overlay';
6
7
  import { Slot } from '../slot';
7
8
  import { dataAttributes, mergeDataAttributes } from '../utils/dataAttributes';
8
- import { useSelectContext } from './context';
9
+ import { SelectContentProvider, useSelectContext } from './context';
10
+ import { getPopupRoleProps } from './popupRole';
11
+ import { SelectDismissButton } from './SelectDismissButton';
9
12
  import type { ISelectContentProps } from './types';
10
13
  import { useContentFocus } from './useContentFocus';
11
14
  import { useListboxNavigation } from './useListboxNavigation';
15
+ import { useQueryHighlight } from './useQueryHighlight';
16
+
17
+ /**
18
+ * Keeps DOM focus in the search input when the pointer goes down elsewhere in the popup, so
19
+ * typing continues after an option is clicked (virtual focus).
20
+ */
21
+ function keepSearchInputFocus(e: { target: unknown; preventDefault: () => void }) {
22
+ const target = e.target as { closest?: (selector: string) => unknown } | null;
23
+ if (target?.closest?.('input, textarea') == null) {
24
+ e.preventDefault();
25
+ }
26
+ }
12
27
 
13
28
  export interface SelectContentProps extends ISelectContentProps {
14
29
  asChild?: boolean;
@@ -27,6 +42,7 @@ export const SelectContent = forwardRef<ComponentRef<typeof View>, SelectContent
27
42
  placement = 'bottom-start',
28
43
  offset = 4,
29
44
  crossOffset = 0,
45
+ dismissAccessibilityLabel = 'Dismiss',
30
46
  ...props
31
47
  },
32
48
  ref,
@@ -38,25 +54,22 @@ export const SelectContent = forwardRef<ComponentRef<typeof View>, SelectContent
38
54
  triggerId,
39
55
  activeValue,
40
56
  setActiveValue,
41
- value,
42
- onValueChange,
57
+ type,
58
+ selectedValues,
59
+ toggleValue,
60
+ selectValueAndClose,
61
+ isItemDisabled,
43
62
  native,
44
63
  triggerRef,
45
- items,
64
+ inputValue,
65
+ visibleItems,
66
+ hasSearch,
67
+ labelId,
68
+ accessibilityLabel,
46
69
  } = useSelectContext();
47
70
 
48
71
  const contentRef = useRef<HTMLElement | null>(null);
49
-
50
- const focusOnMount = useCallback((node: unknown) => {
51
- contentRef.current = node as HTMLElement | null;
52
- if (node && Platform.OS === 'web') {
53
- requestAnimationFrame(() => {
54
- setTimeout(() => {
55
- (node as HTMLElement).focus?.();
56
- }, 100);
57
- });
58
- }
59
- }, []);
72
+ const dismissButtonRef = useRef<HTMLButtonElement | null>(null);
60
73
 
61
74
  const dismiss = useCallback(() => setOpen(false), [setOpen]);
62
75
 
@@ -73,35 +86,42 @@ export const SelectContent = forwardRef<ComponentRef<typeof View>, SelectContent
73
86
  collisionBehavior: 'flip-shift',
74
87
  width: 'anchor',
75
88
  });
76
- const mergedRef = mergeRefs(ref, focusOnMount as unknown as React.Ref<View>, floatingRef);
89
+ const mergedRef = mergeRefs(ref, contentRef as unknown as React.Ref<View>, floatingRef);
77
90
 
78
- useDismissOverlay(overlayOpen, contentRef, [triggerRef], dismiss);
91
+ useDismissOverlay(overlayOpen, contentRef, [triggerRef, dismissButtonRef], dismiss);
79
92
  useEscapeKey(overlayOpen, dismiss);
80
93
 
81
94
  const { handleKeyDown } = useListboxNavigation({
82
- items,
95
+ items: visibleItems,
83
96
  activeValue,
84
97
  setActiveValue,
85
- onSelect: (val) => {
86
- onValueChange(val);
87
- setOpen(false);
88
- },
98
+ type,
99
+ onToggle: toggleValue,
100
+ onSelectAndClose: selectValueAndClose,
101
+ isItemDisabled,
89
102
  onDismiss: dismiss,
103
+ isSearch: hasSearch,
90
104
  });
91
105
 
92
- useContentFocus({
106
+ useContentFocus({ open, triggerRef, setActiveValue });
107
+
108
+ useQueryHighlight({
93
109
  open,
94
- triggerRef,
95
- items,
96
- value,
110
+ inputValue,
111
+ visibleItems,
112
+ selectedValues,
113
+ isItemDisabled,
114
+ activeValue,
97
115
  setActiveValue,
98
116
  });
99
117
 
118
+ const contentContext = useMemo(() => ({ handleKeyDown }), [handleKeyDown]);
119
+
100
120
  if (!overlayOpen) {
101
121
  return null;
102
122
  }
103
123
 
104
- const webProps =
124
+ const platformProps =
105
125
  Platform.OS === 'web'
106
126
  ? {
107
127
  onKeyDown: composeEventHandlers(
@@ -109,38 +129,53 @@ export const SelectContent = forwardRef<ComponentRef<typeof View>, SelectContent
109
129
  handleKeyDown,
110
130
  ),
111
131
  tabIndex: -1,
132
+ 'aria-multiselectable': (!hasSearch && type === 'multiple') || undefined,
133
+ ...(hasSearch ? { onMouseDown: keepSearchInputFocus } : {}),
112
134
  }
113
- : {};
135
+ : { onAccessibilityEscape: composeEventHandlers(props.onAccessibilityEscape, dismiss) };
136
+
137
+ const roleProps = getPopupRoleProps({ hasSearch, labelId, accessibilityLabel, triggerId });
114
138
 
115
139
  const Comp = asChild ? Slot : View;
116
140
 
117
141
  return (
118
- <OverlayContainer
119
- onDismiss={dismiss}
120
- entering={enteringAnimation ?? FadeIn.duration(150)}
121
- exiting={exitingAnimation ?? FadeOut.duration(100)}
122
- >
123
- <Comp
124
- ref={mergedRef}
125
- onLayout={composeEventHandlers(onLayout, onFloatingLayout)}
126
- style={[style, overlayStyle]}
127
- role="listbox"
128
- id={contentId}
129
- aria-labelledby={triggerId}
130
- {...props}
131
- {...mergeDataAttributes(
132
- props,
133
- dataAttributes({
134
- state: 'open',
135
- side: position?.resolvedPlacement.side,
136
- align: position?.resolvedPlacement.align,
137
- }),
138
- )}
139
- {...webProps}
142
+ <SelectContentProvider value={contentContext}>
143
+ <OverlayContainer
144
+ onDismiss={dismiss}
145
+ entering={enteringAnimation ?? FadeIn.duration(150)}
146
+ exiting={exitingAnimation ?? FadeOut.duration(100)}
140
147
  >
141
- {children}
142
- </Comp>
143
- </OverlayContainer>
148
+ {/* With search, autofocus lands on the input; otherwise options are not tabbable, so it
149
+ falls back to the focusable popup. Nested in the React tree, the scope also lets focus
150
+ enter this body-portalled popup from inside a `Drawer`'s contained scope. */}
151
+ <FocusScope autoFocus>
152
+ <Comp
153
+ ref={mergedRef}
154
+ onLayout={composeEventHandlers(onLayout, onFloatingLayout)}
155
+ style={[style, overlayStyle]}
156
+ id={contentId}
157
+ {...roleProps}
158
+ {...props}
159
+ {...mergeDataAttributes(
160
+ props,
161
+ dataAttributes({
162
+ state: 'open',
163
+ side: position?.resolvedPlacement.side,
164
+ align: position?.resolvedPlacement.align,
165
+ }),
166
+ )}
167
+ {...platformProps}
168
+ >
169
+ {children}
170
+ </Comp>
171
+ <SelectDismissButton
172
+ ref={dismissButtonRef}
173
+ onDismiss={dismiss}
174
+ accessibilityLabel={dismissAccessibilityLabel}
175
+ />
176
+ </FocusScope>
177
+ </OverlayContainer>
178
+ </SelectContentProvider>
144
179
  );
145
180
  },
146
181
  );
@@ -0,0 +1,14 @@
1
+ import { forwardRef } from 'react';
2
+
3
+ export interface SelectDismissButtonProps {
4
+ onDismiss: () => void;
5
+ accessibilityLabel: string;
6
+ }
7
+
8
+ /**
9
+ * Web-only. Native screen readers close the popup with the iOS escape gesture
10
+ * (`onAccessibilityEscape` on `SelectContent`) and Android back (`onRequestClose`).
11
+ */
12
+ export const SelectDismissButton = forwardRef<unknown, SelectDismissButtonProps>(() => null);
13
+
14
+ SelectDismissButton.displayName = 'SelectDismissButton';
@@ -0,0 +1,26 @@
1
+ import { forwardRef } from 'react';
2
+ import { VisuallyHidden } from '@react-aria/visually-hidden';
3
+ import type { SelectDismissButtonProps } from './SelectDismissButton';
4
+
5
+ export type { SelectDismissButtonProps };
6
+
7
+ /**
8
+ * Visually hidden close control for screen-reader users, who cannot press Escape or click
9
+ * outside while browsing the popup. Out of the Tab order (Tab already closes the listbox).
10
+ * Rendered as a sibling of the listbox because a listbox may only own options.
11
+ */
12
+ export const SelectDismissButton = forwardRef<HTMLButtonElement, SelectDismissButtonProps>(
13
+ ({ onDismiss, accessibilityLabel }, ref) => (
14
+ <VisuallyHidden>
15
+ <button
16
+ ref={ref}
17
+ type="button"
18
+ tabIndex={-1}
19
+ aria-label={accessibilityLabel}
20
+ onClick={onDismiss}
21
+ />
22
+ </VisuallyHidden>
23
+ ),
24
+ );
25
+
26
+ SelectDismissButton.displayName = 'SelectDismissButton';