@coinbase/cds-mobile 9.9.0 → 9.10.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 (101) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dts/alpha/combobox/Combobox.d.ts.map +1 -1
  3. package/dts/alpha/select/DefaultSelectControl.d.ts +1 -0
  4. package/dts/alpha/select/DefaultSelectControl.d.ts.map +1 -1
  5. package/dts/alpha/select/DefaultSelectDropdown.d.ts.map +1 -1
  6. package/dts/alpha/select/Select.d.ts +1 -0
  7. package/dts/alpha/select/Select.d.ts.map +1 -1
  8. package/dts/alpha/select/types.d.ts +27 -2
  9. package/dts/alpha/select/types.d.ts.map +1 -1
  10. package/dts/alpha/select-chip/SelectChip.d.ts +8 -2
  11. package/dts/alpha/select-chip/SelectChip.d.ts.map +1 -1
  12. package/dts/alpha/select-chip/SelectChipControl.d.ts +3 -4
  13. package/dts/alpha/select-chip/SelectChipControl.d.ts.map +1 -1
  14. package/dts/alpha/tabbed-chips/TabbedChips.d.ts +8 -1
  15. package/dts/alpha/tabbed-chips/TabbedChips.d.ts.map +1 -1
  16. package/dts/buttons/AvatarButton.d.ts +7 -3
  17. package/dts/buttons/AvatarButton.d.ts.map +1 -1
  18. package/dts/buttons/Button.d.ts +21 -2
  19. package/dts/buttons/Button.d.ts.map +1 -1
  20. package/dts/buttons/DefaultSlideButtonBackground.d.ts +2 -2
  21. package/dts/buttons/DefaultSlideButtonBackground.d.ts.map +1 -1
  22. package/dts/buttons/DefaultSlideButtonHandle.d.ts +20 -10
  23. package/dts/buttons/DefaultSlideButtonHandle.d.ts.map +1 -1
  24. package/dts/buttons/IconButton.d.ts +18 -5
  25. package/dts/buttons/IconButton.d.ts.map +1 -1
  26. package/dts/buttons/SlideButton.d.ts +47 -9
  27. package/dts/buttons/SlideButton.d.ts.map +1 -1
  28. package/dts/cards/CardBody.d.ts.map +1 -1
  29. package/dts/cards/FeedCard.d.ts.map +1 -1
  30. package/dts/cards/NudgeCard.d.ts +1 -1
  31. package/dts/cards/UpsellCard.d.ts +1 -1
  32. package/dts/carousel/DefaultCarouselNavigation.d.ts +8 -0
  33. package/dts/carousel/DefaultCarouselNavigation.d.ts.map +1 -1
  34. package/dts/chips/Chip.d.ts +3 -2
  35. package/dts/chips/Chip.d.ts.map +1 -1
  36. package/dts/chips/ChipProps.d.ts +11 -1
  37. package/dts/chips/ChipProps.d.ts.map +1 -1
  38. package/dts/chips/MediaChip.d.ts +1 -0
  39. package/dts/chips/MediaChip.d.ts.map +1 -1
  40. package/dts/controls/InputIcon.d.ts +6 -0
  41. package/dts/controls/InputIcon.d.ts.map +1 -1
  42. package/dts/controls/InputIconButton.d.ts +3 -1
  43. package/dts/controls/InputIconButton.d.ts.map +1 -1
  44. package/dts/controls/InputStack.d.ts.map +1 -1
  45. package/dts/controls/NativeInput.d.ts +7 -2
  46. package/dts/controls/NativeInput.d.ts.map +1 -1
  47. package/dts/controls/SearchInput.d.ts +4 -1
  48. package/dts/controls/SearchInput.d.ts.map +1 -1
  49. package/dts/controls/TextInput.d.ts +24 -2
  50. package/dts/controls/TextInput.d.ts.map +1 -1
  51. package/dts/dates/DateInput.d.ts +18 -3
  52. package/dts/dates/DateInput.d.ts.map +1 -1
  53. package/dts/dates/DatePicker.d.ts +13 -0
  54. package/dts/dates/DatePicker.d.ts.map +1 -1
  55. package/dts/navigation/BrowserBarSearchInput.d.ts +1 -1
  56. package/dts/navigation/BrowserBarSearchInput.d.ts.map +1 -1
  57. package/esm/__stories__/SizeComposition.stories.js +237 -0
  58. package/esm/alpha/combobox/Combobox.js +1 -1
  59. package/esm/alpha/combobox/__stories__/Combobox.stories.js +34 -0
  60. package/esm/alpha/select/DefaultSelectControl.js +71 -22
  61. package/esm/alpha/select/DefaultSelectDropdown.js +5 -2
  62. package/esm/alpha/select/Select.js +9 -3
  63. package/esm/alpha/select/__stories__/AlphaSelect.stories.js +31 -0
  64. package/esm/alpha/select/types.js +8 -0
  65. package/esm/alpha/select-chip/SelectChip.js +14 -7
  66. package/esm/alpha/select-chip/SelectChipControl.js +2 -0
  67. package/esm/alpha/select-chip/__stories__/AlphaSelectChip.stories.js +31 -1
  68. package/esm/alpha/tabbed-chips/TabbedChips.js +8 -5
  69. package/esm/alpha/tabbed-chips/__stories__/AlphaTabbedChips.stories.js +23 -12
  70. package/esm/buttons/Button.js +52 -7
  71. package/esm/buttons/DefaultSlideButtonBackground.js +3 -2
  72. package/esm/buttons/DefaultSlideButtonHandle.js +13 -12
  73. package/esm/buttons/IconButton.js +38 -4
  74. package/esm/buttons/SlideButton.js +34 -6
  75. package/esm/buttons/__stories__/Button.stories.js +46 -16
  76. package/esm/buttons/__stories__/IconButton.stories.js +58 -11
  77. package/esm/buttons/__stories__/SlideButton.stories.js +18 -9
  78. package/esm/cards/CardBody.js +7 -2
  79. package/esm/cards/FeedCard.js +23 -8
  80. package/esm/cards/MessagingCard/MessagingCardLayout.js +2 -2
  81. package/esm/cards/NudgeCard.js +1 -1
  82. package/esm/cards/UpsellCard.js +2 -2
  83. package/esm/carousel/DefaultCarouselNavigation.js +11 -3
  84. package/esm/chips/Chip.js +24 -6
  85. package/esm/chips/MediaChip.js +7 -3
  86. package/esm/chips/__stories__/Chip.stories.js +9 -3
  87. package/esm/chips/__stories__/InputChip.stories.js +19 -1
  88. package/esm/controls/InputStack.js +6 -2
  89. package/esm/controls/NativeInput.js +7 -2
  90. package/esm/controls/SearchInput.js +2 -0
  91. package/esm/controls/TextInput.js +42 -16
  92. package/esm/controls/__stories__/SearchInput.stories.js +28 -1
  93. package/esm/controls/__stories__/TextInput.stories.js +82 -0
  94. package/esm/dates/DateInput.js +2 -1
  95. package/esm/dates/DatePicker.js +4 -2
  96. package/esm/dates/__stories__/DateInput.stories.js +28 -0
  97. package/esm/dates/__stories__/DatePicker.stories.js +23 -0
  98. package/esm/navigation/BrowserBarSearchInput.js +8 -4
  99. package/esm/overlays/Toast.js +1 -1
  100. package/esm/visualizations/ProgressContainerWithButtons.js +2 -2
  101. package/package.json +2 -2
@@ -0,0 +1,237 @@
1
+ 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); }
2
+ import { useMemo, useState } from 'react';
3
+ import { useWindowDimensions } from 'react-native';
4
+ import { sampleTabs } from '@coinbase/cds-common/internal/data/tabs';
5
+ import { gutter } from '@coinbase/cds-common/tokens/sizing';
6
+ import { Combobox } from '../alpha/combobox/Combobox';
7
+ import { Select } from '../alpha/select/Select';
8
+ import { SelectChip } from '../alpha/select-chip/SelectChip';
9
+ import { TabbedChips } from '../alpha/tabbed-chips/TabbedChips';
10
+ import { Button } from '../buttons/Button';
11
+ import { IconButton } from '../buttons/IconButton';
12
+ import { InputChip } from '../chips/InputChip';
13
+ import { SearchInput } from '../controls/SearchInput';
14
+ import { TextInput } from '../controls/TextInput';
15
+ import { DatePicker } from '../dates/DatePicker';
16
+ import { Example, ExampleScreen } from '../examples/ExampleScreen';
17
+ import { useTheme } from '../hooks/useTheme';
18
+ import { HStack } from '../layout/HStack';
19
+ import { VStack } from '../layout/VStack';
20
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
21
+ const dateSharedProps = {
22
+ invalidDateError: 'Please enter a valid date',
23
+ disabledDateError: 'Date unavailable',
24
+ requiredError: 'This field is required'
25
+ };
26
+ const selectOptions = [{
27
+ value: '1',
28
+ label: 'Apple'
29
+ }, {
30
+ value: '2',
31
+ label: 'Banana'
32
+ }, {
33
+ value: '3',
34
+ label: 'Cherry'
35
+ }];
36
+ const chipSelectOptions = [{
37
+ value: 'usd',
38
+ label: 'USD'
39
+ }, {
40
+ value: 'eur',
41
+ label: 'EUR'
42
+ }, {
43
+ value: 'btc',
44
+ label: 'BTC'
45
+ }];
46
+ const tabbedChipTabs = sampleTabs.slice(0, 4);
47
+
48
+ /** Spacing token used for the gap between every composed control. */
49
+ const compositionGap = 1;
50
+
51
+ /**
52
+ * Minimum controls per wrapped row, so each row always pairs at least two
53
+ * controls and their natural heights can be compared against each other.
54
+ */
55
+ const controlsPerRow = 2;
56
+
57
+ /**
58
+ * Derives control widths from the live screen width minus the horizontal
59
+ * padding `ExampleScreen` applies, so fixed-width controls always wrap into
60
+ * rows of `controlsPerRow` instead of overflowing narrow devices.
61
+ */
62
+ const useCompositionWidths = () => {
63
+ const {
64
+ width: screenWidth
65
+ } = useWindowDimensions();
66
+ const theme = useTheme();
67
+ return useMemo(() => {
68
+ const contentWidth = screenWidth - theme.space[gutter] * 2;
69
+ const totalGap = theme.space[compositionGap] * (controlsPerRow - 1);
70
+ return {
71
+ contentWidth,
72
+ controlWidth: Math.floor((contentWidth - totalGap) / controlsPerRow)
73
+ };
74
+ }, [screenWidth, theme]);
75
+ };
76
+ const InputRow = _ref => {
77
+ let {
78
+ size
79
+ } = _ref;
80
+ const {
81
+ controlWidth
82
+ } = useCompositionWidths();
83
+ const [selectValue, setSelectValue] = useState('1');
84
+ const [comboboxValue, setComboboxValue] = useState('1');
85
+ const [text, setText] = useState('');
86
+ const [search, setSearch] = useState('');
87
+ const [pickerDate, setPickerDate] = useState(null);
88
+ const [pickerError, setPickerError] = useState(null);
89
+ const selectStyle = useMemo(() => ({
90
+ width: controlWidth
91
+ }), [controlWidth]);
92
+ return /*#__PURE__*/_jsxs(HStack, {
93
+ alignItems: "flex-end",
94
+ flexWrap: "wrap",
95
+ gap: compositionGap,
96
+ children: [/*#__PURE__*/_jsx(Select, {
97
+ label: "Select",
98
+ onChange: setSelectValue,
99
+ options: selectOptions,
100
+ placeholder: "Choose",
101
+ size: size,
102
+ style: selectStyle,
103
+ value: selectValue
104
+ }), /*#__PURE__*/_jsx(Combobox, {
105
+ label: "Combobox",
106
+ onChange: setComboboxValue,
107
+ options: selectOptions,
108
+ placeholder: "Search",
109
+ size: size,
110
+ style: selectStyle,
111
+ type: "single",
112
+ value: comboboxValue
113
+ }), /*#__PURE__*/_jsx(TextInput, {
114
+ label: "Text",
115
+ onChangeText: setText,
116
+ placeholder: "Name",
117
+ size: size,
118
+ value: text,
119
+ width: controlWidth
120
+ }), /*#__PURE__*/_jsx(DatePicker, _extends({}, dateSharedProps, {
121
+ date: pickerDate,
122
+ error: pickerError,
123
+ helperText: "",
124
+ label: "Date",
125
+ onChangeDate: setPickerDate,
126
+ onErrorDate: setPickerError,
127
+ openCalendarAccessibilityLabel: "Open date picker calendar",
128
+ size: size,
129
+ width: controlWidth
130
+ })), /*#__PURE__*/_jsx(SearchInput, {
131
+ accessibilityLabel: "Search",
132
+ onChangeText: setSearch,
133
+ onClear: () => setSearch(''),
134
+ placeholder: "Search",
135
+ size: size,
136
+ value: search,
137
+ width: controlWidth
138
+ })]
139
+ });
140
+ };
141
+ const ChipMixRow = _ref2 => {
142
+ let {
143
+ chipSize,
144
+ controlSize
145
+ } = _ref2;
146
+ const {
147
+ contentWidth,
148
+ controlWidth
149
+ } = useCompositionWidths();
150
+ const [activeTab, setActiveTab] = useState(tabbedChipTabs[0]);
151
+ const [chipSelectValue, setChipSelectValue] = useState('usd');
152
+ const [text, setText] = useState('');
153
+ const [search, setSearch] = useState('');
154
+ const includeTextInputs = chipSize === 's';
155
+ return /*#__PURE__*/_jsxs(VStack, {
156
+ gap: compositionGap,
157
+ children: [/*#__PURE__*/_jsx(TabbedChips, {
158
+ activeTab: activeTab,
159
+ onChange: setActiveTab,
160
+ size: chipSize,
161
+ tabs: tabbedChipTabs,
162
+ width: contentWidth
163
+ }), /*#__PURE__*/_jsxs(HStack, {
164
+ alignItems: "center",
165
+ flexWrap: "wrap",
166
+ gap: compositionGap,
167
+ children: [/*#__PURE__*/_jsx(InputChip, {
168
+ onPress: () => {},
169
+ size: chipSize,
170
+ children: "USD"
171
+ }), /*#__PURE__*/_jsx(SelectChip, {
172
+ onChange: setChipSelectValue,
173
+ options: chipSelectOptions,
174
+ placeholder: "Asset",
175
+ size: chipSize,
176
+ value: chipSelectValue
177
+ }), /*#__PURE__*/_jsx(Button, {
178
+ size: controlSize,
179
+ children: "Action"
180
+ }), /*#__PURE__*/_jsx(IconButton, {
181
+ accessibilityLabel: "Settings",
182
+ name: "gear",
183
+ size: controlSize
184
+ }), includeTextInputs && /*#__PURE__*/_jsxs(_Fragment, {
185
+ children: [/*#__PURE__*/_jsx(TextInput, {
186
+ accessibilityLabel: "Filter",
187
+ onChangeText: setText,
188
+ placeholder: "Filter",
189
+ size: "s",
190
+ value: text,
191
+ width: controlWidth
192
+ }), /*#__PURE__*/_jsx(SearchInput, {
193
+ accessibilityLabel: "Search",
194
+ onChangeText: setSearch,
195
+ onClear: () => setSearch(''),
196
+ placeholder: "Search",
197
+ size: "s",
198
+ value: search,
199
+ width: controlWidth
200
+ })]
201
+ })]
202
+ })]
203
+ });
204
+ };
205
+
206
+ /**
207
+ * Side-by-side composition of sized controls to verify natural height alignment in a row.
208
+ */
209
+ const SizeCompositionScreen = () => /*#__PURE__*/_jsxs(ExampleScreen, {
210
+ children: [/*#__PURE__*/_jsx(Example, {
211
+ title: "Inputs \u2014 size=\"s\"",
212
+ children: /*#__PURE__*/_jsx(InputRow, {
213
+ size: "s"
214
+ })
215
+ }), /*#__PURE__*/_jsx(Example, {
216
+ title: "Inputs \u2014 size=\"m\"",
217
+ children: /*#__PURE__*/_jsx(InputRow, {
218
+ size: "m"
219
+ })
220
+ }), /*#__PURE__*/_jsx(Example, {
221
+ title: "Inputs \u2014 default (size l)",
222
+ children: /*#__PURE__*/_jsx(InputRow, {})
223
+ }), /*#__PURE__*/_jsx(Example, {
224
+ title: "Chips + small controls \u2014 size=\"s\"",
225
+ children: /*#__PURE__*/_jsx(ChipMixRow, {
226
+ chipSize: "s",
227
+ controlSize: "s"
228
+ })
229
+ }), /*#__PURE__*/_jsx(Example, {
230
+ title: "Chips + small controls \u2014 size=\"xs\"",
231
+ children: /*#__PURE__*/_jsx(ChipMixRow, {
232
+ chipSize: "xs",
233
+ controlSize: "xs"
234
+ })
235
+ })]
236
+ });
237
+ export default SizeCompositionScreen;
@@ -154,8 +154,8 @@ const ComboboxBase = /*#__PURE__*/memo(_ref2 => {
154
154
  paddingBottom: safeBottomPadding
155
155
  },
156
156
  children: /*#__PURE__*/_jsx(Button, {
157
- compact: true,
158
157
  onPress: handleClose,
158
+ size: "s",
159
159
  children: closeButtonLabel
160
160
  })
161
161
  })
@@ -592,6 +592,37 @@ const DisabledExample = () => {
592
592
  value: value
593
593
  });
594
594
  };
595
+ const SizesExample = () => {
596
+ const [value, setValue] = useState('1');
597
+ return /*#__PURE__*/_jsxs(VStack, {
598
+ gap: 4,
599
+ children: [/*#__PURE__*/_jsx(Combobox, {
600
+ label: "Small (s)",
601
+ onChange: setValue,
602
+ options: singleSelectOptions,
603
+ placeholder: "Search fruits...",
604
+ size: "s",
605
+ type: "single",
606
+ value: value
607
+ }), /*#__PURE__*/_jsx(Combobox, {
608
+ label: "Medium (m)",
609
+ onChange: setValue,
610
+ options: singleSelectOptions,
611
+ placeholder: "Search fruits...",
612
+ size: "m",
613
+ type: "single",
614
+ value: value
615
+ }), /*#__PURE__*/_jsx(Combobox, {
616
+ label: "Large (l) - default",
617
+ onChange: setValue,
618
+ options: singleSelectOptions,
619
+ placeholder: "Search fruits...",
620
+ size: "l",
621
+ type: "single",
622
+ value: value
623
+ })]
624
+ });
625
+ };
595
626
  const CompactExample = () => {
596
627
  const {
597
628
  value,
@@ -1232,6 +1263,9 @@ const Default = () => {
1232
1263
  }), /*#__PURE__*/_jsx(Example, {
1233
1264
  title: "Disabled state",
1234
1265
  children: /*#__PURE__*/_jsx(DisabledExample, {})
1266
+ }), /*#__PURE__*/_jsx(Example, {
1267
+ title: "Sizes",
1268
+ children: /*#__PURE__*/_jsx(SizesExample, {})
1235
1269
  }), /*#__PURE__*/_jsx(Example, {
1236
1270
  title: "Compact size",
1237
1271
  children: /*#__PURE__*/_jsx(CompactExample, {})
@@ -1,4 +1,4 @@
1
- const _excluded = ["ref", "type", "options", "value", "onChange", "open", "placeholder", "disabled", "readOnly", "setOpen", "variant", "helperText", "label", "labelVariant", "contentNode", "startNode", "endNode", "compact", "align", "font", "labelColor", "labelFont", "bordered", "borderWidth", "focusedBorderWidth", "inputBackground", "borderRadius", "maxSelectedOptionsToShow", "accessibilityHint", "accessibilityLabel", "hiddenSelectedOptionsLabel", "removeSelectedOptionAccessibilityLabel", "style", "styles", "onBlur", "onFocus"];
1
+ const _excluded = ["ref", "type", "options", "value", "onChange", "open", "placeholder", "disabled", "readOnly", "setOpen", "variant", "helperText", "label", "labelVariant", "contentNode", "startNode", "endNode", "compact", "size", "align", "font", "labelColor", "labelFont", "bordered", "borderWidth", "focusedBorderWidth", "inputBackground", "borderRadius", "maxSelectedOptionsToShow", "accessibilityHint", "accessibilityLabel", "hiddenSelectedOptionsLabel", "removeSelectedOptionAccessibilityLabel", "style", "styles", "onBlur", "onFocus"];
2
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
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
4
  import { memo, useCallback, useMemo } from 'react';
@@ -15,7 +15,25 @@ import { VStack } from '../../layout/VStack';
15
15
  import { AnimatedCaret } from '../../motion/AnimatedCaret';
16
16
  import { Text } from '../../typography/Text';
17
17
  import { isSelectOptionGroup } from './Select';
18
+ import { defaultSelectSize } from './types';
18
19
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
20
+ const selectSizeVerticalSpace = {
21
+ s: 1,
22
+ m: 1.5,
23
+ l: 2
24
+ };
25
+
26
+ // A multi-select's selected-value chips add their own height, so the vertical space is
27
+ // tightened per size to keep the overall field height aligned with the single-select scale.
28
+ const multiSelectVerticalSpace = {
29
+ s: 0.5,
30
+ m: 1,
31
+ l: 1.5
32
+ };
33
+
34
+ // Multi-select value chips always use the compact `xs` size (even at size `l`) so several chips —
35
+ // and a stacked inside label — stay within the field's natural height.
36
+ const multiSelectValueChipSize = 'xs';
19
37
  const variantColor = {
20
38
  foreground: 'fg',
21
39
  positive: 'fgPositive',
@@ -44,6 +62,7 @@ export const DefaultSelectControlComponent = /*#__PURE__*/memo(_ref => {
44
62
  startNode,
45
63
  endNode: customEndNode,
46
64
  compact,
65
+ size,
47
66
  align = 'start',
48
67
  font = 'body',
49
68
  labelColor = 'fg',
@@ -70,11 +89,24 @@ export const DefaultSelectControlComponent = /*#__PURE__*/memo(_ref => {
70
89
  setOpen(currentOpen => !currentOpen);
71
90
  }, [isInteractionBlocked, setOpen]);
72
91
  const theme = useTheme();
73
- // When compact, labelVariant is ignored
74
- const labelVariant = compact ? undefined : labelVariantProp;
75
92
  const isMultiSelect = type === 'multi';
76
- const shouldShowCompactLabel = compact && label && !isMultiSelect;
77
- const shouldShowInsideLabel = labelVariant === 'inside' && !compact && label;
93
+ // `size` wins over the deprecated `compact` for geometry.
94
+ const resolvedSize = size != null ? size : compact ? 's' : defaultSelectSize;
95
+ // The deprecated `compact` only forces label placement when the caller did NOT set an explicit
96
+ // `size`; once `size` is provided, placement follows the normal `labelVariant` rules.
97
+ const useLegacyCompact = Boolean(compact) && size === undefined;
98
+ const labelVariant = useLegacyCompact ? undefined : labelVariantProp;
99
+ const hasLabel = !!label;
100
+
101
+ // Label placement mirrors TextInput: `compact` (set alone) forces an inside label; otherwise
102
+ // placement follows `labelVariant`. An inside label sits horizontally in the start slot at every
103
+ // size EXCEPT `l`, where it stacks vertically above the value. Multi-select is the one exception:
104
+ // its value chips can't share a row with an inline label, so legacy compact multi-select keeps
105
+ // its label outside (only an explicit `inside` labelVariant places a multi-select's label inside).
106
+ const isCompactLabel = useLegacyCompact && !isMultiSelect;
107
+ const wantsInsideLabel = hasLabel && (isCompactLabel || labelVariant === 'inside');
108
+ const insideVerticalLabel = wantsInsideLabel && !isCompactLabel && resolvedSize === 'l';
109
+ const insideHorizontalLabel = wantsInsideLabel && !insideVerticalLabel;
78
110
  const hasValue = value !== null && !(Array.isArray(value) && value.length === 0);
79
111
 
80
112
  // Map of options to their values
@@ -149,7 +181,7 @@ export const DefaultSelectControlComponent = /*#__PURE__*/memo(_ref => {
149
181
  children: helperText
150
182
  }) : helperText, [helperText, variant, styles == null ? void 0 : styles.controlHelperTextNode]);
151
183
  const labelNode = useMemo(() => {
152
- if (shouldShowInsideLabel || shouldShowCompactLabel) return null;
184
+ if (insideVerticalLabel || insideHorizontalLabel) return null;
153
185
  if (typeof label === 'string') {
154
186
  return /*#__PURE__*/_jsx(InputLabel, {
155
187
  color: labelColor,
@@ -160,20 +192,24 @@ export const DefaultSelectControlComponent = /*#__PURE__*/memo(_ref => {
160
192
  });
161
193
  }
162
194
  return label;
163
- }, [shouldShowInsideLabel, shouldShowCompactLabel, label, labelColor, labelFont, styles == null ? void 0 : styles.controlLabelNode]);
195
+ }, [insideVerticalLabel, insideHorizontalLabel, label, labelColor, labelFont, styles == null ? void 0 : styles.controlLabelNode]);
164
196
  const inlineLabelNode = useMemo(() => {
165
- if (!shouldShowInsideLabel && !shouldShowCompactLabel) return null;
197
+ if (!insideVerticalLabel && !insideHorizontalLabel) return null;
166
198
  if (typeof label === 'string') {
167
199
  return /*#__PURE__*/_jsx(InputLabel, {
168
200
  color: labelColor,
169
- font: labelFont,
201
+ font: labelFont
202
+ // An inside label — horizontal or vertically stacked — is kept to a single line so a
203
+ // long label can't wrap and stretch the field past the height its size defines.
204
+ ,
205
+ numberOfLines: 1,
170
206
  paddingY: 0,
171
207
  style: styles == null ? void 0 : styles.controlLabelNode,
172
208
  children: label
173
209
  });
174
210
  }
175
211
  return label;
176
- }, [shouldShowInsideLabel, shouldShowCompactLabel, label, labelColor, labelFont, styles == null ? void 0 : styles.controlLabelNode]);
212
+ }, [insideVerticalLabel, insideHorizontalLabel, label, labelColor, labelFont, styles == null ? void 0 : styles.controlLabelNode]);
177
213
  const valueAlignment = useMemo(() => align === 'end' ? 'flex-end' : align === 'center' ? 'center' : 'flex-start', [align]);
178
214
  const valueNode = useMemo(() => {
179
215
  if (hasValue && isMultiSelect) {
@@ -190,7 +226,6 @@ export const DefaultSelectControlComponent = /*#__PURE__*/memo(_ref => {
190
226
  var _option$value, _ref6, _ref7, _option$label2;
191
227
  const accessibilityLabel = typeof option.label === 'string' ? option.label : typeof option.description === 'string' ? option.description : (_option$value = option.value) != null ? _option$value : '';
192
228
  return /*#__PURE__*/_jsx(InputChip, {
193
- compact: true,
194
229
  accessibilityLabel: removeSelectedOptionAccessibilityLabel + " " + accessibilityLabel,
195
230
  borderWidth: 0,
196
231
  disabled: disabled || option.disabled,
@@ -200,13 +235,14 @@ export const DefaultSelectControlComponent = /*#__PURE__*/memo(_ref => {
200
235
  event == null || event.stopPropagation();
201
236
  onChange == null || onChange(option.value);
202
237
  },
238
+ size: multiSelectValueChipSize,
203
239
  children: (_ref6 = (_ref7 = (_option$label2 = option.label) != null ? _option$label2 : option.description) != null ? _ref7 : option.value) != null ? _ref6 : ''
204
240
  }, option.value);
205
241
  }), value.length - maxSelectedOptionsToShow > 0 && /*#__PURE__*/_jsx(InputChip, {
206
- compact: true,
207
242
  borderWidth: 0,
208
243
  end: null,
209
244
  invertColorScheme: false,
245
+ size: multiSelectValueChipSize,
210
246
  children: "+" + (value.length - maxSelectedOptionsToShow) + " " + hiddenSelectedOptionsLabel
211
247
  })]
212
248
  });
@@ -251,12 +287,12 @@ export const DefaultSelectControlComponent = /*#__PURE__*/memo(_ref => {
251
287
  paddingEnd: 2,
252
288
  style: styles == null ? void 0 : styles.controlStartNode,
253
289
  children: startNode
254
- }), shouldShowCompactLabel ? /*#__PURE__*/_jsx(HStack, {
290
+ }), insideHorizontalLabel ? /*#__PURE__*/_jsx(HStack, {
255
291
  alignItems: "center",
256
292
  maxWidth: "40%",
257
293
  paddingEnd: 1,
258
294
  children: inlineLabelNode
259
- }) : null, shouldShowInsideLabel ? /*#__PURE__*/_jsxs(VStack, {
295
+ }) : null, insideVerticalLabel ? /*#__PURE__*/_jsxs(VStack, {
260
296
  flexGrow: 1,
261
297
  minWidth: 0,
262
298
  width: "100%",
@@ -278,7 +314,7 @@ export const DefaultSelectControlComponent = /*#__PURE__*/memo(_ref => {
278
314
  })]
279
315
  })
280
316
  })
281
- }), [ref, accessibilityHint, computedControlAccessibilityLabel, disabled, onBlur, onFocus, styles == null ? void 0 : styles.controlInputNode, styles == null ? void 0 : styles.controlStartNode, styles == null ? void 0 : styles.controlValueNode, startNode, shouldShowCompactLabel, shouldShowInsideLabel, inlineLabelNode, valueAlignment, valueNode, contentNode, handleToggleOpen]);
317
+ }), [ref, accessibilityHint, computedControlAccessibilityLabel, disabled, onBlur, onFocus, styles == null ? void 0 : styles.controlInputNode, styles == null ? void 0 : styles.controlStartNode, styles == null ? void 0 : styles.controlValueNode, startNode, insideHorizontalLabel, insideVerticalLabel, inlineLabelNode, valueAlignment, valueNode, contentNode, handleToggleOpen]);
282
318
  const endNode = useMemo(() => /*#__PURE__*/_jsx(Pressable, {
283
319
  accessible: customEndNode ? true : false,
284
320
  disabled: disabled,
@@ -294,12 +330,25 @@ export const DefaultSelectControlComponent = /*#__PURE__*/memo(_ref => {
294
330
  })
295
331
  })
296
332
  }), [styles == null ? void 0 : styles.controlEndNode, disabled, customEndNode, open, handleToggleOpen]);
297
- const inputStackStyles = useMemo(() => ({
298
- paddingTop: compact || labelVariant === 'inside' ? theme.space[1] : theme.space[2],
299
- paddingBottom: compact || labelVariant === 'inside' ? theme.space[1] : theme.space[2],
300
- paddingLeft: theme.space[2],
301
- paddingRight: theme.space[2]
302
- }), [compact, labelVariant, theme.space]);
333
+ const inputStackStyles = useMemo(() => {
334
+ let verticalSpace;
335
+ if (insideVerticalLabel) {
336
+ // A vertically-stacked inside label (size `l`) tightens the padding so the stacked label +
337
+ // value fit the same field height an outside label produces. A multi-select with chips
338
+ // (taller than a single text line) tightens further.
339
+ verticalSpace = isMultiSelect && hasValue ? 0.25 : 0.75;
340
+ } else if (isMultiSelect && hasValue) {
341
+ verticalSpace = multiSelectVerticalSpace[resolvedSize];
342
+ } else {
343
+ verticalSpace = selectSizeVerticalSpace[resolvedSize];
344
+ }
345
+ return {
346
+ paddingTop: theme.space[verticalSpace],
347
+ paddingBottom: theme.space[verticalSpace],
348
+ paddingLeft: theme.space[2],
349
+ paddingRight: theme.space[2]
350
+ };
351
+ }, [insideVerticalLabel, resolvedSize, isMultiSelect, hasValue, theme.space]);
303
352
  return /*#__PURE__*/_jsx(InputStack, _extends({
304
353
  borderFocusedStyle: borderFocusedStyle,
305
354
  borderRadius: borderRadius,
@@ -313,7 +362,7 @@ export const DefaultSelectControlComponent = /*#__PURE__*/memo(_ref => {
313
362
  inputBackground: inputBackground,
314
363
  inputNode: inputNode,
315
364
  labelNode: labelNode,
316
- labelVariant: labelVariant,
365
+ labelVariant: insideVerticalLabel ? 'inside' : 'outside',
317
366
  onBlur: onBlur,
318
367
  onFocus: onFocus,
319
368
  style: style,
@@ -121,7 +121,6 @@ const DefaultSelectDropdownComponent = /*#__PURE__*/memo(_ref => {
121
121
  compact: compact,
122
122
  disabled: disabled,
123
123
  end: end != null ? end : /*#__PURE__*/_jsx(Button, {
124
- compact: true,
125
124
  transparent: true
126
125
  // This button is not accessible to users using screen readers since it's
127
126
  // the child of a button. Clearing the a11y label ensures it isn't announced
@@ -130,7 +129,11 @@ const DefaultSelectDropdownComponent = /*#__PURE__*/memo(_ref => {
130
129
  accessibilityLabel: "",
131
130
  accessible: false,
132
131
  flush: "end",
133
- onPress: handleClearAll,
132
+ onPress: handleClearAll
133
+ // Intentionally always the densest size: the dropdown's inline affordances stay
134
+ // compact regardless of the control's size.
135
+ ,
136
+ size: "s",
134
137
  children: clearAllLabel
135
138
  }),
136
139
  indeterminate: indeterminate,
@@ -1,5 +1,5 @@
1
1
  const _excluded = ["ref"],
2
- _excluded2 = ["value", "type", "options", "onChange", "open", "setOpen", "disabled", "readOnly", "disableClickOutsideClose", "placeholder", "helperText", "compact", "label", "labelVariant", "accessibilityLabel", "accessibilityHint", "accessibilityRoles", "selectAllLabel", "emptyOptionsLabel", "clearAllLabel", "hideSelectAll", "defaultOpen", "startNode", "endNode", "variant", "maxSelectedOptionsToShow", "hiddenSelectedOptionsLabel", "removeSelectedOptionAccessibilityLabel", "accessory", "media", "end", "align", "font", "bordered", "borderWidth", "focusedBorderWidth", "inputBackground", "labelColor", "labelFont", "borderRadius", "SelectOptionComponent", "SelectAllOptionComponent", "SelectDropdownComponent", "SelectControlComponent", "SelectEmptyDropdownContentsComponent", "SelectOptionGroupComponent", "style", "styles", "testID"];
2
+ _excluded2 = ["value", "type", "options", "onChange", "open", "setOpen", "disabled", "readOnly", "disableClickOutsideClose", "placeholder", "helperText", "compact", "size", "label", "labelVariant", "accessibilityLabel", "accessibilityHint", "accessibilityRoles", "selectAllLabel", "emptyOptionsLabel", "clearAllLabel", "hideSelectAll", "defaultOpen", "startNode", "endNode", "variant", "maxSelectedOptionsToShow", "hiddenSelectedOptionsLabel", "removeSelectedOptionAccessibilityLabel", "accessory", "media", "end", "align", "font", "bordered", "borderWidth", "focusedBorderWidth", "inputBackground", "labelColor", "labelFont", "borderRadius", "SelectOptionComponent", "SelectAllOptionComponent", "SelectDropdownComponent", "SelectControlComponent", "SelectEmptyDropdownContentsComponent", "SelectOptionGroupComponent", "style", "styles", "testID"];
3
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
4
  import React, { memo, useImperativeHandle, useMemo, useRef, useState } from 'react';
5
5
  import { View } from 'react-native';
@@ -10,7 +10,7 @@ import { DefaultSelectDropdown } from './DefaultSelectDropdown';
10
10
  import { DefaultSelectEmptyDropdownContents } from './DefaultSelectEmptyDropdownContents';
11
11
  import { DefaultSelectOption } from './DefaultSelectOption';
12
12
  import { DefaultSelectOptionGroup } from './DefaultSelectOptionGroup';
13
- import { isSelectOptionGroup } from './types';
13
+ import { defaultSelectSize, isSelectOptionGroup } from './types';
14
14
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
15
  export const defaultAccessibilityRoles = {
16
16
  option: 'menuitem'
@@ -37,6 +37,7 @@ const SelectBase = /*#__PURE__*/memo(_ref => {
37
37
  placeholder,
38
38
  helperText,
39
39
  compact,
40
+ size,
40
41
  label,
41
42
  labelVariant,
42
43
  accessibilityLabel = typeof label === 'string' ? label : 'Select control',
@@ -76,6 +77,10 @@ const SelectBase = /*#__PURE__*/memo(_ref => {
76
77
  testID
77
78
  } = mergedProps,
78
79
  props = _objectWithoutPropertiesLoose(mergedProps, _excluded2);
80
+ // The dropdown keeps a binary density toggle instead of the t-shirt scale, so Select owns the
81
+ // translation: only the smallest control size renders a compact dropdown. The control still
82
+ // receives the raw `compact` because it needs it for legacy label placement.
83
+ const dropdownCompact = (size != null ? size : compact ? 's' : defaultSelectSize) === 's';
79
84
  const [openInternal, setOpenInternal] = useState(defaultOpen != null ? defaultOpen : false);
80
85
  const open = openProp != null ? openProp : openInternal;
81
86
  const setOpen = setOpenProp != null ? setOpenProp : setOpenInternal;
@@ -145,6 +150,7 @@ const SelectBase = /*#__PURE__*/memo(_ref => {
145
150
  readOnly: readOnly,
146
151
  removeSelectedOptionAccessibilityLabel: removeSelectedOptionAccessibilityLabel,
147
152
  setOpen: setOpen,
153
+ size: size,
148
154
  startNode: startNode,
149
155
  style: styles == null ? void 0 : styles.control,
150
156
  styles: controlStyles,
@@ -159,7 +165,7 @@ const SelectBase = /*#__PURE__*/memo(_ref => {
159
165
  accessibilityRoles: accessibilityRoles,
160
166
  accessory: accessory,
161
167
  clearAllLabel: clearAllLabel,
162
- compact: compact,
168
+ compact: dropdownCompact,
163
169
  controlRef: containerRef,
164
170
  disabled: disabled,
165
171
  emptyOptionsLabel: emptyOptionsLabel,
@@ -350,6 +350,34 @@ const CompactExample = () => {
350
350
  value: value
351
351
  });
352
352
  };
353
+ const SizesExample = () => {
354
+ const [value, setValue] = useState('1');
355
+ return /*#__PURE__*/_jsxs(VStack, {
356
+ gap: 2,
357
+ children: [/*#__PURE__*/_jsx(Select, {
358
+ label: "Small (s)",
359
+ onChange: setValue,
360
+ options: exampleOptions,
361
+ placeholder: "Empty value",
362
+ size: "s",
363
+ value: value
364
+ }), /*#__PURE__*/_jsx(Select, {
365
+ label: "Medium (m)",
366
+ onChange: setValue,
367
+ options: exampleOptions,
368
+ placeholder: "Empty value",
369
+ size: "m",
370
+ value: value
371
+ }), /*#__PURE__*/_jsx(Select, {
372
+ label: "Large (l) - default",
373
+ onChange: setValue,
374
+ options: exampleOptions,
375
+ placeholder: "Empty value",
376
+ size: "l",
377
+ value: value
378
+ })]
379
+ });
380
+ };
353
381
  const InsideLabelVariantExample = () => {
354
382
  const [value, setValue] = useState('1');
355
383
  return /*#__PURE__*/_jsx(Select, {
@@ -1391,6 +1419,9 @@ const SelectV3Screen = () => {
1391
1419
  }), /*#__PURE__*/_jsx(Example, {
1392
1420
  title: "Compact",
1393
1421
  children: /*#__PURE__*/_jsx(CompactExample, {})
1422
+ }), /*#__PURE__*/_jsx(Example, {
1423
+ title: "Sizes",
1424
+ children: /*#__PURE__*/_jsx(SizesExample, {})
1394
1425
  }), /*#__PURE__*/_jsx(Example, {
1395
1426
  title: "Inside label variant",
1396
1427
  children: /*#__PURE__*/_jsx(InsideLabelVariantExample, {})
@@ -1,3 +1,11 @@
1
+ /**
2
+ * T-shirt size scale for the Select control. The Figma Select component set exposes
3
+ * three sizes (`s | m | l`); there is intentionally no `xs`.
4
+ */
5
+
6
+ /** Size the Select control resolves to when neither `size` nor the deprecated `compact` is set. */
7
+ export const defaultSelectSize = 'l';
8
+
1
9
  /**
2
10
  * Configuration for a single option in the Select component
3
11
  */
@@ -1,5 +1,5 @@
1
1
  const _excluded = ["ref"],
2
- _excluded2 = ["invertColorScheme", "numberOfLines", "maxWidth", "displayValue"];
2
+ _excluded2 = ["invertColorScheme", "numberOfLines", "maxWidth", "displayValue", "size", "compact"];
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 React, { memo, useCallback } from 'react';
@@ -21,17 +21,24 @@ const SelectChipComponent = /*#__PURE__*/memo(_ref => {
21
21
  invertColorScheme,
22
22
  numberOfLines,
23
23
  maxWidth,
24
- displayValue
24
+ displayValue,
25
+ size,
26
+ compact
25
27
  } = mergedProps,
26
28
  props = _objectWithoutPropertiesLoose(mergedProps, _excluded2);
27
- const SelectChipControlComponent = useCallback(props => {
28
- return /*#__PURE__*/_jsx(SelectChipControl, _extends({
29
+ // Select doesn't pass the chip-specific props (size/compact/displayValue/etc.) down to the
30
+ // control, so they're injected here. They're listed AFTER the `controlProps` spread so the
31
+ // chip-level values win — matching the web SelectChip wrapper.
32
+ const SelectChipControlComponent = useCallback(controlProps => {
33
+ return /*#__PURE__*/_jsx(SelectChipControl, _extends({}, controlProps, {
34
+ compact: compact,
29
35
  displayValue: displayValue,
30
36
  invertColorScheme: invertColorScheme,
31
37
  maxWidth: maxWidth,
32
- numberOfLines: numberOfLines
33
- }, props));
34
- }, [displayValue, invertColorScheme, maxWidth, numberOfLines]);
38
+ numberOfLines: numberOfLines,
39
+ size: size
40
+ }));
41
+ }, [displayValue, invertColorScheme, maxWidth, numberOfLines, size, compact]);
35
42
  return /*#__PURE__*/_jsx(Select, _extends({
36
43
  ref: ref,
37
44
  SelectControlComponent: SelectChipControlComponent