@coinbase/cds-mobile 8.25.1 → 8.26.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.
Files changed (77) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dts/alpha/select/DefaultSelectControl.d.ts +2 -8
  3. package/dts/alpha/select/DefaultSelectControl.d.ts.map +1 -1
  4. package/dts/alpha/select/DefaultSelectDropdown.d.ts.map +1 -1
  5. package/dts/alpha/select/DefaultSelectOptionGroup.d.ts +8 -0
  6. package/dts/alpha/select/DefaultSelectOptionGroup.d.ts.map +1 -0
  7. package/dts/alpha/select/Select.d.ts +23 -366
  8. package/dts/alpha/select/Select.d.ts.map +1 -1
  9. package/dts/alpha/select/index.d.ts +2 -0
  10. package/dts/alpha/select/index.d.ts.map +1 -1
  11. package/dts/alpha/select/types.d.ts +486 -0
  12. package/dts/alpha/select/types.d.ts.map +1 -0
  13. package/dts/alpha/select-chip/SelectChip.d.ts +26 -0
  14. package/dts/alpha/select-chip/SelectChip.d.ts.map +1 -0
  15. package/dts/alpha/select-chip/SelectChipControl.d.ts +14 -0
  16. package/dts/alpha/select-chip/SelectChipControl.d.ts.map +1 -0
  17. package/dts/alpha/select-chip/index.d.ts +3 -0
  18. package/dts/alpha/select-chip/index.d.ts.map +1 -0
  19. package/dts/chips/Chip.d.ts.map +1 -1
  20. package/dts/chips/SelectChip.d.ts +8 -0
  21. package/dts/chips/SelectChip.d.ts.map +1 -1
  22. package/dts/examples/ExampleScreen.d.ts +26 -1
  23. package/dts/examples/ExampleScreen.d.ts.map +1 -1
  24. package/esm/alpha/select/DefaultSelectControl.js +46 -8
  25. package/esm/alpha/select/DefaultSelectDropdown.js +100 -31
  26. package/esm/alpha/select/DefaultSelectOption.js +1 -1
  27. package/esm/alpha/select/DefaultSelectOptionGroup.js +90 -0
  28. package/esm/alpha/select/Select.js +10 -26
  29. package/esm/alpha/select/index.js +3 -1
  30. package/esm/alpha/select/types.js +50 -0
  31. package/esm/alpha/select-chip/SelectChip.js +31 -0
  32. package/esm/alpha/select-chip/SelectChipControl.js +111 -0
  33. package/esm/alpha/select-chip/__stories__/SelectChip.stories.js +538 -0
  34. package/esm/alpha/select-chip/index.js +2 -0
  35. package/esm/banner/__stories__/Banner.stories.js +133 -294
  36. package/esm/banner/__stories__/BannerActions.stories.js +276 -0
  37. package/esm/banner/__stories__/BannerLayout.stories.js +329 -0
  38. package/esm/cells/__stories__/ListCell.stories.js +1 -17
  39. package/esm/cells/__stories__/ListCellFallback.stories.js +1 -0
  40. package/esm/chips/Chip.js +4 -1
  41. package/esm/chips/SelectChip.js +9 -0
  42. package/esm/examples/ExampleScreen.js +79 -58
  43. package/esm/icons/__stories__/IconSheet.js +35 -13
  44. package/esm/illustrations/__stories__/HeroSquare.stories.js +70 -2
  45. package/esm/illustrations/__stories__/Pictogram.stories.js +70 -2
  46. package/esm/illustrations/__stories__/SpotIcon.stories.js +70 -2
  47. package/esm/illustrations/__stories__/SpotRectangle.stories.js +68 -2
  48. package/esm/illustrations/__stories__/SpotSquare.stories.js +68 -2
  49. package/esm/media/__stories__/CarouselMedia.stories.js +2 -5
  50. package/esm/numpad/__stories__/Numpad.stories.js +8 -5
  51. package/esm/page/__stories__/PageFooter.stories.js +5 -4
  52. package/esm/page/__stories__/PageFooterInPage.stories.js +20 -19
  53. package/esm/page/__stories__/PageHeader.stories.js +4 -4
  54. package/esm/page/__stories__/PageHeaderInErrorEmptyState.stories.js +6 -4
  55. package/esm/page/__stories__/PageHeaderInPage.stories.js +20 -18
  56. package/esm/section-header/__stories__/SectionHeader.stories.js +4 -4
  57. package/esm/sticky-footer/__stories__/StickyFooter.stories.js +6 -8
  58. package/esm/tour/__stories__/Tour.stories.js +13 -166
  59. package/esm/typography/__stories__/TextBody.stories.js +2 -0
  60. package/esm/typography/__stories__/TextCaption.stories.js +2 -0
  61. package/esm/typography/__stories__/TextCore.stories.js +2 -0
  62. package/esm/typography/__stories__/TextDisplay1.stories.js +2 -0
  63. package/esm/typography/__stories__/TextDisplay2.stories.js +2 -0
  64. package/esm/typography/__stories__/TextDisplay3.stories.js +2 -0
  65. package/esm/typography/__stories__/TextHeadline.stories.js +2 -0
  66. package/esm/typography/__stories__/TextLabel1.stories.js +2 -0
  67. package/esm/typography/__stories__/TextLabel2.stories.js +2 -0
  68. package/esm/typography/__stories__/TextLegal.stories.js +2 -0
  69. package/esm/typography/__stories__/TextTitle1.stories.js +2 -0
  70. package/esm/typography/__stories__/TextTitle2.stories.js +2 -0
  71. package/esm/typography/__stories__/TextTitle3.stories.js +2 -0
  72. package/esm/typography/__stories__/TextTitle4.stories.js +2 -0
  73. package/package.json +2 -2
  74. package/esm/icons/__stories__/Icon.stories.js +0 -98
  75. package/esm/illustrations/__stories__/getIllustrationSheet.js +0 -164
  76. /package/esm/alpha/select/__stories__/{Select.stories.js → AlphaSelect.stories.js} +0 -0
  77. /package/esm/alpha/tabbed-chips/__stories__/{TabbedChips.stories.js → AlphaTabbedChips.stories.js} +0 -0
@@ -13,9 +13,11 @@ import { HStack } from '../../layout/HStack';
13
13
  import { VStack } from '../../layout/VStack';
14
14
  import { AnimatedCaret } from '../../motion/AnimatedCaret';
15
15
  import { Text } from '../../typography/Text';
16
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
16
+ import { isSelectOptionGroup } from './Select';
17
+
17
18
  // The height is smaller for the inside label variant since the label takes
18
19
  // up space above the input.
20
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
19
21
  const LABEL_VARIANT_INSIDE_HEIGHT = 24;
20
22
  const COMPACT_HEIGHT = 40;
21
23
  const DEFAULT_HEIGHT = 56;
@@ -56,6 +58,42 @@ export const DefaultSelectControlComponent = /*#__PURE__*/memo(/*#__PURE__*/forw
56
58
  const shouldShowCompactLabel = compact && label && !isMultiSelect;
57
59
  const hasValue = value !== null && !(Array.isArray(value) && value.length === 0);
58
60
 
61
+ // Map of options to their values
62
+ // If multiple options share the same value, the first occurrence wins (matches native HTML select behavior)
63
+ const optionsMap = useMemo(() => {
64
+ const map = new Map();
65
+ const isDev = process.env.NODE_ENV !== 'production';
66
+ options.forEach((option, optionIndex) => {
67
+ if (isSelectOptionGroup(option)) {
68
+ option.options.forEach((groupOption, groupOptionIndex) => {
69
+ if (groupOption.value !== null) {
70
+ const optionValue = groupOption.value;
71
+ // Only set if not already present (first wins)
72
+ if (!map.has(optionValue)) {
73
+ map.set(optionValue, groupOption);
74
+ } else if (isDev) {
75
+ console.warn("[Select] Duplicate option value detected: \"" + optionValue + "\". " + "The first occurrence will be used for display. " + ("Found duplicate in group \"" + option.label + "\" at index " + groupOptionIndex + ". ") + ("First occurrence was at option index " + optionIndex + "."));
76
+ }
77
+ }
78
+ });
79
+ } else {
80
+ const singleOption = option;
81
+ if (singleOption.value !== null) {
82
+ const optionValue = singleOption.value;
83
+ // Only set if not already present (first wins)
84
+ if (!map.has(optionValue)) {
85
+ map.set(optionValue, singleOption);
86
+ } else if (isDev) {
87
+ var _ref2, _existingOption$label;
88
+ const existingOption = map.get(optionValue);
89
+ console.warn("[Select] Duplicate option value detected: \"" + optionValue + "\". " + "The first occurrence will be used for display. " + ("Found duplicate at option index " + optionIndex + ". ") + ("First occurrence label: \"" + ((_ref2 = (_existingOption$label = existingOption == null ? void 0 : existingOption.label) != null ? _existingOption$label : existingOption == null ? void 0 : existingOption.value) != null ? _ref2 : 'unknown') + "\"."));
90
+ }
91
+ }
92
+ }
93
+ });
94
+ return map;
95
+ }, [options]);
96
+
59
97
  // Prop value doesn't have default value because it affects the color of the
60
98
  // animated caret
61
99
  const focusedVariant = useInputVariant(!!open, variant != null ? variant : 'foregroundMuted');
@@ -83,15 +121,15 @@ export const DefaultSelectControlComponent = /*#__PURE__*/memo(/*#__PURE__*/forw
83
121
  })
84
122
  }) : label, [disabled, label, labelVariant, setOpen, shouldShowCompactLabel, styles == null ? void 0 : styles.controlLabelNode]);
85
123
  const valueNode = useMemo(() => {
86
- var _ref4, _ref5, _option$label2;
124
+ var _ref5, _ref6, _option$label2;
87
125
  if (hasValue && isMultiSelect) {
88
126
  const valuesToShow = value.length <= maxSelectedOptionsToShow ? value : value.slice(0, maxSelectedOptionsToShow);
89
- const optionsToShow = valuesToShow.map(value => options.find(option => option.value === value)).filter(Boolean);
127
+ const optionsToShow = valuesToShow.map(value => optionsMap.get(value)).filter(option => option !== undefined);
90
128
  return /*#__PURE__*/_jsxs(HStack, {
91
129
  flexWrap: "wrap",
92
130
  gap: 1,
93
131
  children: [optionsToShow.map(option => {
94
- var _option$value, _ref2, _ref3, _option$label;
132
+ var _option$value, _ref3, _ref4, _option$label;
95
133
  const accessibilityLabel = typeof option.label === 'string' ? option.label : typeof option.description === 'string' ? option.description : (_option$value = option.value) != null ? _option$value : '';
96
134
  return /*#__PURE__*/_jsx(InputChip, {
97
135
  compact: true,
@@ -104,7 +142,7 @@ export const DefaultSelectControlComponent = /*#__PURE__*/memo(/*#__PURE__*/forw
104
142
  event == null || event.stopPropagation();
105
143
  onChange == null || onChange(option.value);
106
144
  },
107
- children: (_ref2 = (_ref3 = (_option$label = option.label) != null ? _option$label : option.description) != null ? _ref3 : option.value) != null ? _ref2 : ''
145
+ children: (_ref3 = (_ref4 = (_option$label = option.label) != null ? _option$label : option.description) != null ? _ref4 : option.value) != null ? _ref3 : ''
108
146
  }, option.value);
109
147
  }), value.length - maxSelectedOptionsToShow > 0 && /*#__PURE__*/_jsx(InputChip, {
110
148
  compact: true,
@@ -115,8 +153,8 @@ export const DefaultSelectControlComponent = /*#__PURE__*/memo(/*#__PURE__*/forw
115
153
  })]
116
154
  });
117
155
  }
118
- const option = options.find(option => option.value === value);
119
- const label = (_ref4 = (_ref5 = (_option$label2 = option == null ? void 0 : option.label) != null ? _option$label2 : option == null ? void 0 : option.description) != null ? _ref5 : option == null ? void 0 : option.value) != null ? _ref4 : placeholder;
156
+ const option = !isMultiSelect ? optionsMap.get(value) : undefined;
157
+ const label = (_ref5 = (_ref6 = (_option$label2 = option == null ? void 0 : option.label) != null ? _option$label2 : option == null ? void 0 : option.description) != null ? _ref6 : option == null ? void 0 : option.value) != null ? _ref5 : placeholder;
120
158
  const content = hasValue ? label : placeholder;
121
159
  return typeof content === 'string' ? /*#__PURE__*/_jsx(Text, {
122
160
  color: hasValue ? 'fg' : 'fgMuted',
@@ -125,7 +163,7 @@ export const DefaultSelectControlComponent = /*#__PURE__*/memo(/*#__PURE__*/forw
125
163
  textAlign: shouldShowCompactLabel ? 'right' : 'left',
126
164
  children: content
127
165
  }) : content;
128
- }, [hasValue, isMultiSelect, options, placeholder, shouldShowCompactLabel, value, maxSelectedOptionsToShow, hiddenSelectedOptionsLabel, removeSelectedOptionAccessibilityLabel, onChange]);
166
+ }, [hasValue, isMultiSelect, optionsMap, placeholder, shouldShowCompactLabel, value, maxSelectedOptionsToShow, hiddenSelectedOptionsLabel, removeSelectedOptionAccessibilityLabel, onChange]);
129
167
  const inputNode = useMemo(() => /*#__PURE__*/_jsx(TouchableOpacity, _extends({
130
168
  ref: ref,
131
169
  accessibilityHint: accessibilityHint,
@@ -1,5 +1,5 @@
1
- const _excluded = ["type", "options", "value", "onChange", "open", "setOpen", "controlRef", "disabled", "style", "styles", "compact", "label", "end", "selectAllLabel", "emptyOptionsLabel", "clearAllLabel", "hideSelectAll", "accessory", "media", "SelectOptionComponent", "SelectAllOptionComponent", "SelectEmptyDropdownContentsComponent", "accessibilityRoles"],
2
- _excluded2 = ["Component", "media", "accessory", "end"];
1
+ const _excluded = ["type", "options", "value", "onChange", "open", "setOpen", "controlRef", "disabled", "style", "styles", "compact", "label", "end", "selectAllLabel", "emptyOptionsLabel", "clearAllLabel", "hideSelectAll", "accessory", "media", "SelectOptionComponent", "SelectAllOptionComponent", "SelectEmptyDropdownContentsComponent", "SelectOptionGroupComponent", "accessibilityRoles"],
2
+ _excluded2 = ["Component", "media", "accessory", "end", "disabled"];
3
3
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
4
4
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
5
5
  import { forwardRef, memo, useCallback, useMemo } from 'react';
@@ -12,7 +12,8 @@ import { Tray } from '../../overlays/tray/Tray';
12
12
  import { DefaultSelectAllOption } from './DefaultSelectAllOption';
13
13
  import { DefaultSelectEmptyDropdownContents } from './DefaultSelectEmptyDropdownContents';
14
14
  import { DefaultSelectOption } from './DefaultSelectOption';
15
- import { defaultAccessibilityRoles } from './Select';
15
+ import { DefaultSelectOptionGroup } from './DefaultSelectOptionGroup';
16
+ import { defaultAccessibilityRoles, isSelectOptionGroup } from './Select';
16
17
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
17
18
  const DefaultSelectDropdownComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
18
19
  let {
@@ -37,6 +38,7 @@ const DefaultSelectDropdownComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef
37
38
  SelectOptionComponent = DefaultSelectOption,
38
39
  SelectAllOptionComponent = DefaultSelectAllOption,
39
40
  SelectEmptyDropdownContentsComponent = DefaultSelectEmptyDropdownContents,
41
+ SelectOptionGroupComponent = DefaultSelectOptionGroup,
40
42
  accessibilityRoles = defaultAccessibilityRoles
41
43
  } = _ref,
42
44
  props = _objectWithoutPropertiesLoose(_ref, _excluded);
@@ -48,20 +50,65 @@ const DefaultSelectDropdownComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef
48
50
  optionDescription: styles == null ? void 0 : styles.optionDescription,
49
51
  selectAllDivider: styles == null ? void 0 : styles.selectAllDivider
50
52
  }), [styles == null ? void 0 : styles.optionCell, styles == null ? void 0 : styles.optionContent, styles == null ? void 0 : styles.optionLabel, styles == null ? void 0 : styles.optionDescription, styles == null ? void 0 : styles.selectAllDivider]);
53
+ const optionGroupStyles = useMemo(() => ({
54
+ optionGroup: styles == null ? void 0 : styles.optionGroup,
55
+ option: styles == null ? void 0 : styles.option,
56
+ optionBlendStyles: styles == null ? void 0 : styles.optionBlendStyles,
57
+ optionCell: styles == null ? void 0 : styles.optionCell,
58
+ optionContent: styles == null ? void 0 : styles.optionContent,
59
+ optionLabel: styles == null ? void 0 : styles.optionLabel,
60
+ optionDescription: styles == null ? void 0 : styles.optionDescription,
61
+ selectAllDivider: styles == null ? void 0 : styles.selectAllDivider
62
+ }), [styles == null ? void 0 : styles.optionGroup, styles == null ? void 0 : styles.option, styles == null ? void 0 : styles.optionBlendStyles, styles == null ? void 0 : styles.optionCell, styles == null ? void 0 : styles.optionContent, styles == null ? void 0 : styles.optionLabel, styles == null ? void 0 : styles.optionDescription, styles == null ? void 0 : styles.selectAllDivider]);
51
63
  const emptyDropdownContentsStyles = useMemo(() => ({
52
64
  emptyContentsContainer: styles == null ? void 0 : styles.emptyContentsContainer,
53
65
  emptyContentsText: styles == null ? void 0 : styles.emptyContentsText
54
66
  }), [styles == null ? void 0 : styles.emptyContentsContainer, styles == null ? void 0 : styles.emptyContentsText]);
67
+
68
+ // Flatten options for Select All logic, excluding disabled options and options from disabled groups
69
+ const flatOptionsForSelectAll = useMemo(() => {
70
+ if (disabled) return [];
71
+ const result = [];
72
+ options.forEach(option => {
73
+ if (isSelectOptionGroup(option)) {
74
+ // It's a group, add its enabled options if the group itself is not disabled
75
+ if (!option.disabled) {
76
+ option.options.forEach(groupOption => {
77
+ if (!groupOption.disabled) {
78
+ result.push(groupOption);
79
+ }
80
+ });
81
+ }
82
+ } else {
83
+ // It's a single option, add if not disabled
84
+ if (!option.disabled) {
85
+ result.push(option);
86
+ }
87
+ }
88
+ });
89
+ return result;
90
+ }, [options, disabled]);
55
91
  const isMultiSelect = type === 'multi';
56
92
  const isSomeOptionsSelected = isMultiSelect ? value.length > 0 : false;
57
- const isAllOptionsSelected = isMultiSelect ? value.length === options.filter(o => o.value !== null).length : false;
93
+ // Only count non-disabled options when determining if all are selected
94
+ const enabledOptionsCount = flatOptionsForSelectAll.filter(o => o.value !== null).length;
95
+ const isAllOptionsSelected = isMultiSelect ? enabledOptionsCount > 0 && value.length === enabledOptionsCount : false;
58
96
  const toggleSelectAll = useCallback(() => {
59
- if (isAllOptionsSelected) onChange(null);else onChange(options.map(o => o.value).filter(o => o !== null && !(value != null && value.includes(o))));
60
- }, [isAllOptionsSelected, onChange, options, value]);
97
+ if (isAllOptionsSelected) onChange(null);else onChange(flatOptionsForSelectAll.map(_ref2 => {
98
+ let {
99
+ value
100
+ } = _ref2;
101
+ return value;
102
+ }).filter(optionValue => optionValue !== null && !(value != null && value.includes(optionValue))));
103
+ }, [isAllOptionsSelected, onChange, flatOptionsForSelectAll, value]);
61
104
  const handleClearAll = useCallback(event => {
62
105
  event.stopPropagation();
63
106
  onChange(null);
64
107
  }, [onChange]);
108
+ const handleOptionPress = useCallback(newValue => {
109
+ onChange(newValue);
110
+ if (!isMultiSelect) setOpen(false);
111
+ }, [onChange, isMultiSelect, setOpen]);
65
112
  const indeterminate = !isAllOptionsSelected && isSomeOptionsSelected ? true : false;
66
113
  const SelectAllOption = useMemo(() => /*#__PURE__*/_jsx(SelectAllOptionComponent, {
67
114
  accessibilityRole: accessibilityRoles == null ? void 0 : accessibilityRoles.option,
@@ -83,10 +130,11 @@ const DefaultSelectDropdownComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef
83
130
  children: clearAllLabel
84
131
  }),
85
132
  indeterminate: indeterminate,
86
- label: selectAllLabel + " (" + options.filter(o => o.value !== null).length + ")",
133
+ label: selectAllLabel + " (" + flatOptionsForSelectAll.filter(o => o.value !== null).length + ")",
87
134
  media: media != null ? media : /*#__PURE__*/_jsx(Checkbox, {
88
135
  checked: isAllOptionsSelected,
89
136
  indeterminate: indeterminate,
137
+ onPress: toggleSelectAll,
90
138
  tabIndex: -1
91
139
  }),
92
140
  onPress: toggleSelectAll,
@@ -95,7 +143,7 @@ const DefaultSelectDropdownComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef
95
143
  styles: optionStyles,
96
144
  type: type,
97
145
  value: 'select-all'
98
- }, "select-all"), [SelectAllOptionComponent, accessibilityRoles == null ? void 0 : accessibilityRoles.option, accessory, styles == null ? void 0 : styles.optionBlendStyles, styles == null ? void 0 : styles.option, compact, end, handleClearAll, clearAllLabel, disabled, indeterminate, selectAllLabel, options, media, isAllOptionsSelected, toggleSelectAll, optionStyles, type]);
146
+ }, "select-all"), [SelectAllOptionComponent, accessibilityRoles == null ? void 0 : accessibilityRoles.option, accessory, styles == null ? void 0 : styles.optionBlendStyles, styles == null ? void 0 : styles.option, compact, disabled, end, handleClearAll, clearAllLabel, indeterminate, selectAllLabel, flatOptionsForSelectAll, media, isAllOptionsSelected, toggleSelectAll, optionStyles, type]);
99
147
  if (!open) return null;
100
148
  return /*#__PURE__*/_jsx(Tray, {
101
149
  ref: ref,
@@ -109,51 +157,72 @@ const DefaultSelectDropdownComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef
109
157
  children: /*#__PURE__*/_jsx(ScrollView, {
110
158
  showsVerticalScrollIndicator: true,
111
159
  children: /*#__PURE__*/_jsxs(VStack, {
112
- children: [!hideSelectAll && isMultiSelect && options.length > 0 && SelectAllOption, options.length > 0 ? options.map(_ref2 => {
113
- var _ref3;
114
- let {
115
- Component,
160
+ children: [!hideSelectAll && isMultiSelect && options.length > 0 && SelectAllOption, options.length > 0 ? options.map(optionOrGroup => {
161
+ var _optionProps$value, _optionProps$value2, _ref3;
162
+ // Check if it's a group (has 'options' property and 'label')
163
+ if (isSelectOptionGroup(optionOrGroup)) {
164
+ var _group$disabled;
165
+ const group = optionOrGroup;
166
+ return /*#__PURE__*/_jsx(SelectOptionGroupComponent, {
167
+ SelectOptionComponent: SelectOptionComponent,
168
+ accessibilityRole: accessibilityRoles == null ? void 0 : accessibilityRoles.option,
169
+ accessory: accessory,
170
+ compact: compact,
171
+ disabled: (_group$disabled = group.disabled) != null ? _group$disabled : disabled,
172
+ end: end,
173
+ label: group.label,
174
+ media: media,
175
+ onChange: onChange,
176
+ options: group.options,
177
+ setOpen: setOpen,
178
+ styles: optionGroupStyles,
179
+ type: type,
180
+ value: value
181
+ }, "group-" + group.label);
182
+ }
183
+ const option = optionOrGroup;
184
+ const {
185
+ Component: optionComponent,
116
186
  media: optionMedia,
117
187
  accessory: optionAccessory,
118
- end: optionEnd
119
- } = _ref2,
120
- option = _objectWithoutPropertiesLoose(_ref2, _excluded2);
121
- const RenderedSelectOption = Component != null ? Component : SelectOptionComponent;
122
- const selected = option.value !== null && isMultiSelect ? value.includes(option.value) : value === option.value;
188
+ end: optionEnd,
189
+ disabled: optionDisabled
190
+ } = option,
191
+ optionProps = _objectWithoutPropertiesLoose(option, _excluded2);
192
+ const RenderedComponent = optionComponent != null ? optionComponent : SelectOptionComponent;
193
+ const selected = optionProps.value !== null && isMultiSelect ? value.includes(optionProps.value) : value === optionProps.value;
123
194
  /** onPress handlers are passed so that when the media is pressed,
124
195
  * the onChange handler is called. Since the <RenderedSelectOption>
125
196
  * has an accessibilityRole, the inner media won't be detected by a screen reader
126
197
  * so this behavior matches web
127
198
  * */
128
199
  const defaultMedia = isMultiSelect ? /*#__PURE__*/_jsx(Checkbox, {
200
+ "aria-hidden": true,
129
201
  checked: selected,
130
- onPress: () => {
131
- onChange(option.value);
132
- }
202
+ onChange: () => handleOptionPress(optionProps.value),
203
+ tabIndex: -1,
204
+ value: (_optionProps$value = optionProps.value) == null ? void 0 : _optionProps$value.toString()
133
205
  }) : /*#__PURE__*/_jsx(Radio, {
206
+ "aria-hidden": true,
134
207
  checked: selected,
135
- onPress: () => {
136
- onChange(option.value);
137
- setOpen(false);
138
- }
208
+ onChange: () => handleOptionPress(optionProps.value),
209
+ tabIndex: -1,
210
+ value: (_optionProps$value2 = optionProps.value) == null ? void 0 : _optionProps$value2.toString()
139
211
  });
140
- return /*#__PURE__*/_jsx(RenderedSelectOption, _extends({
212
+ return /*#__PURE__*/_jsx(RenderedComponent, _extends({
141
213
  accessibilityRole: accessibilityRoles == null ? void 0 : accessibilityRoles.option,
142
214
  accessory: optionAccessory != null ? optionAccessory : accessory,
143
215
  blendStyles: styles == null ? void 0 : styles.optionBlendStyles,
144
216
  compact: compact,
145
- disabled: option.disabled || disabled,
217
+ disabled: optionDisabled || disabled,
146
218
  end: optionEnd != null ? optionEnd : end,
147
219
  media: (_ref3 = optionMedia != null ? optionMedia : media) != null ? _ref3 : defaultMedia,
148
- onPress: newValue => {
149
- onChange(newValue);
150
- if (!isMultiSelect) setOpen(false);
151
- },
220
+ onPress: handleOptionPress,
152
221
  selected: selected,
153
222
  style: styles == null ? void 0 : styles.option,
154
223
  styles: optionStyles,
155
224
  type: type
156
- }, option), option.value);
225
+ }, optionProps), optionProps.value);
157
226
  }) : /*#__PURE__*/_jsx(SelectEmptyDropdownContentsComponent, {
158
227
  label: emptyOptionsLabel,
159
228
  styles: emptyDropdownContentsStyles
@@ -21,7 +21,7 @@ const DefaultSelectOptionComponent = (_ref, ref) => {
21
21
  styles,
22
22
  type,
23
23
  accessibilityRole,
24
- background = type === 'single' && selected && value !== null ? 'bgAlternate' : 'bg'
24
+ background = 'transparent'
25
25
  } = _ref,
26
26
  props = _objectWithoutPropertiesLoose(_ref, _excluded);
27
27
  const labelNode = useMemo(() => typeof label === 'string' ? /*#__PURE__*/_jsx(Text, {
@@ -0,0 +1,90 @@
1
+ const _excluded = ["Component", "media", "accessory", "end", "disabled"];
2
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
3
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
4
+ import { memo, useCallback, useMemo } from 'react';
5
+ import { Checkbox } from '../../controls/Checkbox';
6
+ import { Radio } from '../../controls/Radio';
7
+ import { VStack } from '../../layout/VStack';
8
+ import { Text } from '../../typography/Text';
9
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
+ const DefaultSelectOptionGroupComponent = /*#__PURE__*/memo(_ref => {
11
+ let {
12
+ label,
13
+ options,
14
+ SelectOptionComponent,
15
+ value,
16
+ onChange,
17
+ setOpen,
18
+ type,
19
+ accessibilityRole,
20
+ accessory,
21
+ media,
22
+ end,
23
+ disabled,
24
+ compact,
25
+ styles
26
+ } = _ref;
27
+ const optionStyles = useMemo(() => ({
28
+ optionCell: styles == null ? void 0 : styles.optionCell,
29
+ optionContent: styles == null ? void 0 : styles.optionContent,
30
+ optionLabel: styles == null ? void 0 : styles.optionLabel,
31
+ optionDescription: styles == null ? void 0 : styles.optionDescription,
32
+ selectAllDivider: styles == null ? void 0 : styles.selectAllDivider
33
+ }), [styles == null ? void 0 : styles.optionCell, styles == null ? void 0 : styles.optionContent, styles == null ? void 0 : styles.optionLabel, styles == null ? void 0 : styles.optionDescription, styles == null ? void 0 : styles.selectAllDivider]);
34
+ const isMultiSelect = type === 'multi';
35
+ const handleOptionPress = useCallback(newValue => {
36
+ onChange(newValue);
37
+ if (!isMultiSelect) setOpen(false);
38
+ }, [onChange, isMultiSelect, setOpen]);
39
+ if (options.length === 0) {
40
+ return null;
41
+ }
42
+ return /*#__PURE__*/_jsxs(VStack, {
43
+ role: "group",
44
+ style: styles == null ? void 0 : styles.optionGroup,
45
+ children: [/*#__PURE__*/_jsx(Text, {
46
+ color: "fgMuted",
47
+ font: "caption",
48
+ paddingX: 2,
49
+ paddingY: 2,
50
+ children: label
51
+ }), options.map(option => {
52
+ var _ref2;
53
+ const {
54
+ Component: optionComponent,
55
+ media: optionMedia,
56
+ accessory: optionAccessory,
57
+ end: optionEnd,
58
+ disabled: optionDisabled
59
+ } = option,
60
+ optionProps = _objectWithoutPropertiesLoose(option, _excluded);
61
+ const RenderedComponent = optionComponent != null ? optionComponent : SelectOptionComponent;
62
+ const selected = optionProps.value !== null && isMultiSelect ? value.includes(optionProps.value) : value === optionProps.value;
63
+ const defaultMedia = isMultiSelect ? /*#__PURE__*/_jsx(Checkbox, {
64
+ checked: selected,
65
+ onChange: () => handleOptionPress(optionProps.value),
66
+ tabIndex: -1
67
+ }) : /*#__PURE__*/_jsx(Radio, {
68
+ checked: selected,
69
+ onChange: () => handleOptionPress(optionProps.value),
70
+ tabIndex: -1
71
+ });
72
+ return /*#__PURE__*/_jsx(RenderedComponent, _extends({
73
+ accessibilityRole: accessibilityRole,
74
+ accessory: optionAccessory != null ? optionAccessory : accessory,
75
+ blendStyles: styles == null ? void 0 : styles.optionBlendStyles,
76
+ compact: compact,
77
+ disabled: optionDisabled || disabled,
78
+ end: optionEnd != null ? optionEnd : end,
79
+ media: (_ref2 = optionMedia != null ? optionMedia : media) != null ? _ref2 : defaultMedia,
80
+ onPress: handleOptionPress,
81
+ selected: selected,
82
+ style: styles == null ? void 0 : styles.option,
83
+ styles: optionStyles,
84
+ type: type
85
+ }, optionProps), optionProps.value);
86
+ })]
87
+ });
88
+ });
89
+ DefaultSelectOptionGroupComponent.displayName = 'DefaultSelectOptionGroup';
90
+ export const DefaultSelectOptionGroup = DefaultSelectOptionGroupComponent;
@@ -1,4 +1,4 @@
1
- const _excluded = ["value", "type", "options", "onChange", "open", "setOpen", "disabled", "disableClickOutsideClose", "placeholder", "helperText", "compact", "label", "labelVariant", "accessibilityLabel", "accessibilityHint", "accessibilityRoles", "selectAllLabel", "emptyOptionsLabel", "clearAllLabel", "hideSelectAll", "defaultOpen", "startNode", "endNode", "variant", "maxSelectedOptionsToShow", "hiddenSelectedOptionsLabel", "removeSelectedOptionAccessibilityLabel", "accessory", "media", "end", "SelectOptionComponent", "SelectAllOptionComponent", "SelectDropdownComponent", "SelectControlComponent", "SelectEmptyDropdownContentsComponent", "style", "styles", "testID"];
1
+ const _excluded = ["value", "type", "options", "onChange", "open", "setOpen", "disabled", "disableClickOutsideClose", "placeholder", "helperText", "compact", "label", "labelVariant", "accessibilityLabel", "accessibilityHint", "accessibilityRoles", "selectAllLabel", "emptyOptionsLabel", "clearAllLabel", "hideSelectAll", "defaultOpen", "startNode", "endNode", "variant", "maxSelectedOptionsToShow", "hiddenSelectedOptionsLabel", "removeSelectedOptionAccessibilityLabel", "accessory", "media", "end", "SelectOptionComponent", "SelectAllOptionComponent", "SelectDropdownComponent", "SelectControlComponent", "SelectEmptyDropdownContentsComponent", "SelectOptionGroupComponent", "style", "styles", "testID"];
2
2
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
3
3
  import React, { forwardRef, memo, useImperativeHandle, useMemo, useRef, useState } from 'react';
4
4
  import { View } from 'react-native';
@@ -7,35 +7,16 @@ import { DefaultSelectControl } from './DefaultSelectControl';
7
7
  import { DefaultSelectDropdown } from './DefaultSelectDropdown';
8
8
  import { DefaultSelectEmptyDropdownContents } from './DefaultSelectEmptyDropdownContents';
9
9
  import { DefaultSelectOption } from './DefaultSelectOption';
10
+ import { DefaultSelectOptionGroup } from './DefaultSelectOptionGroup';
11
+ import { isSelectOptionGroup } from './types';
10
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
13
  export const defaultAccessibilityRoles = {
12
14
  option: 'menuitem'
13
15
  };
14
16
 
15
- /**
16
- * Configuration for a single option in the Select component
17
- */
18
-
19
- /**
20
- * Props for individual option components within the Select dropdown
21
- */
22
-
23
- /**
24
- * Custom UI to render for an option in the Select component options array
25
- */
26
-
27
- /**
28
- * Props for the select control component (the clickable input that opens the dropdown)
29
- */
30
-
31
- /**
32
- * Props for the dropdown component that contains the list of options
33
- */
34
-
35
- /**
36
- * Props for the Select component
37
- */
17
+ // Re-export all types for backward compatibility
38
18
 
19
+ export { isSelectOptionGroup };
39
20
  const SelectBase = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
40
21
  let {
41
22
  value,
@@ -72,6 +53,7 @@ const SelectBase = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
72
53
  SelectDropdownComponent = DefaultSelectDropdown,
73
54
  SelectControlComponent = DefaultSelectControl,
74
55
  SelectEmptyDropdownContentsComponent = DefaultSelectEmptyDropdownContents,
56
+ SelectOptionGroupComponent = DefaultSelectOptionGroup,
75
57
  style,
76
58
  styles,
77
59
  testID
@@ -102,8 +84,9 @@ const SelectBase = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
102
84
  optionDescription: styles == null ? void 0 : styles.optionDescription,
103
85
  selectAllDivider: styles == null ? void 0 : styles.selectAllDivider,
104
86
  emptyContentsContainer: styles == null ? void 0 : styles.emptyContentsContainer,
105
- emptyContentsText: styles == null ? void 0 : styles.emptyContentsText
106
- }), [styles == null ? void 0 : styles.dropdown, styles == null ? void 0 : styles.option, styles == null ? void 0 : styles.optionBlendStyles, styles == null ? void 0 : styles.optionCell, styles == null ? void 0 : styles.optionContent, styles == null ? void 0 : styles.optionLabel, styles == null ? void 0 : styles.optionDescription, styles == null ? void 0 : styles.selectAllDivider, styles == null ? void 0 : styles.emptyContentsContainer, styles == null ? void 0 : styles.emptyContentsText]);
87
+ emptyContentsText: styles == null ? void 0 : styles.emptyContentsText,
88
+ optionGroup: styles == null ? void 0 : styles.optionGroup
89
+ }), [styles == null ? void 0 : styles.dropdown, styles == null ? void 0 : styles.option, styles == null ? void 0 : styles.optionBlendStyles, styles == null ? void 0 : styles.optionCell, styles == null ? void 0 : styles.optionContent, styles == null ? void 0 : styles.optionLabel, styles == null ? void 0 : styles.optionDescription, styles == null ? void 0 : styles.selectAllDivider, styles == null ? void 0 : styles.emptyContentsContainer, styles == null ? void 0 : styles.emptyContentsText, styles == null ? void 0 : styles.optionGroup]);
107
90
  const containerRef = useRef(null);
108
91
  useImperativeHandle(ref, () => Object.assign(containerRef.current, {
109
92
  open,
@@ -145,6 +128,7 @@ const SelectBase = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
145
128
  SelectAllOptionComponent: SelectAllOptionComponent,
146
129
  SelectEmptyDropdownContentsComponent: SelectEmptyDropdownContentsComponent,
147
130
  SelectOptionComponent: SelectOptionComponent,
131
+ SelectOptionGroupComponent: SelectOptionGroupComponent,
148
132
  accessibilityRoles: accessibilityRoles,
149
133
  accessory: accessory,
150
134
  clearAllLabel: clearAllLabel,
@@ -3,4 +3,6 @@ export * from './DefaultSelectControl';
3
3
  export * from './DefaultSelectDropdown';
4
4
  export * from './DefaultSelectEmptyDropdownContents';
5
5
  export * from './DefaultSelectOption';
6
- export * from './Select';
6
+ export * from './DefaultSelectOptionGroup';
7
+ export * from './Select';
8
+ export * from './types';
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Configuration for a single option in the Select component
3
+ */
4
+
5
+ /**
6
+ * Props for individual option components within the Select dropdown
7
+ */
8
+
9
+ /**
10
+ * Custom UI to render for an option in the Select component options array
11
+ */
12
+
13
+ /**
14
+ * Configuration for a group of options in the Select component
15
+ */
16
+
17
+ /**
18
+ * Props for the option group component in the Select dropdown
19
+ */
20
+
21
+ /**
22
+ * Custom UI to render for an option group in the Select component options array
23
+ */
24
+
25
+ /**
26
+ * Array of options for the Select component. Can be individual options or groups with `label` and `options`
27
+ */
28
+
29
+ /**
30
+ * Type guard to check if an option is a group
31
+ */
32
+ export function isSelectOptionGroup(option) {
33
+ return 'options' in option && Array.isArray(option.options) && 'label' in option;
34
+ }
35
+
36
+ /**
37
+ * Props for the select control component (the clickable input that opens the dropdown)
38
+ */
39
+
40
+ /**
41
+ * Props for the dropdown component that contains the list of options
42
+ */
43
+
44
+ /**
45
+ * Props for the Select component
46
+ */
47
+
48
+ /**
49
+ * Type for the Select component function signature
50
+ */
@@ -0,0 +1,31 @@
1
+ const _excluded = ["invertColorScheme", "numberOfLines"];
2
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
3
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
4
+ import React, { forwardRef, memo, useCallback } from 'react';
5
+ import { Select } from '../select/Select';
6
+ import { SelectChipControl } from './SelectChipControl';
7
+
8
+ /**
9
+ * Chip-styled Select control built on top of the Alpha Select.
10
+ * Supports both single and multi selection via Select's `type` prop.
11
+ */
12
+ import { jsx as _jsx } from "react/jsx-runtime";
13
+ const SelectChipComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
14
+ let {
15
+ invertColorScheme,
16
+ numberOfLines
17
+ } = _ref,
18
+ props = _objectWithoutPropertiesLoose(_ref, _excluded);
19
+ const SelectChipControlComponent = useCallback(props => {
20
+ return /*#__PURE__*/_jsx(SelectChipControl, _extends({
21
+ invertColorScheme: invertColorScheme,
22
+ numberOfLines: numberOfLines
23
+ }, props));
24
+ }, [invertColorScheme, numberOfLines]);
25
+ return /*#__PURE__*/_jsx(Select, _extends({
26
+ ref: ref,
27
+ SelectControlComponent: SelectChipControlComponent
28
+ }, props));
29
+ }));
30
+ SelectChipComponent.displayName = 'SelectChip';
31
+ export const SelectChip = SelectChipComponent;