@telus-uds/components-base 3.21.0 → 3.23.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 (64) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/lib/cjs/Button/Button.js +12 -3
  3. package/lib/cjs/Button/ButtonBase.js +63 -10
  4. package/lib/cjs/Button/ButtonDropdown.js +2 -0
  5. package/lib/cjs/Button/ButtonGroup.js +45 -38
  6. package/lib/cjs/Button/propTypes.js +6 -0
  7. package/lib/cjs/Card/PressableCardBase.js +3 -1
  8. package/lib/cjs/Carousel/Carousel.js +63 -22
  9. package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +23 -3
  10. package/lib/cjs/Icon/Icon.js +8 -11
  11. package/lib/cjs/Icon/IconText.js +0 -1
  12. package/lib/cjs/Listbox/GroupControl.js +33 -39
  13. package/lib/cjs/Listbox/Listbox.js +22 -13
  14. package/lib/cjs/Listbox/ListboxGroup.js +2 -1
  15. package/lib/cjs/Listbox/ListboxOverlay.js +5 -2
  16. package/lib/cjs/Listbox/PressableItem.js +8 -4
  17. package/lib/cjs/TextInput/TextInputBase.js +5 -1
  18. package/lib/cjs/ThemeProvider/index.js +9 -1
  19. package/lib/cjs/ThemeProvider/useResponsiveThemeTokensCallback.js +124 -0
  20. package/lib/cjs/Validator/Validator.js +171 -135
  21. package/lib/cjs/index.js +7 -0
  22. package/lib/esm/Button/Button.js +13 -4
  23. package/lib/esm/Button/ButtonBase.js +64 -11
  24. package/lib/esm/Button/ButtonDropdown.js +2 -0
  25. package/lib/esm/Button/ButtonGroup.js +44 -39
  26. package/lib/esm/Button/propTypes.js +6 -0
  27. package/lib/esm/Card/PressableCardBase.js +3 -1
  28. package/lib/esm/Carousel/Carousel.js +63 -22
  29. package/lib/esm/Carousel/CarouselItem/CarouselItem.js +23 -3
  30. package/lib/esm/Icon/Icon.js +8 -11
  31. package/lib/esm/Icon/IconText.js +0 -1
  32. package/lib/esm/Listbox/GroupControl.js +33 -39
  33. package/lib/esm/Listbox/Listbox.js +23 -14
  34. package/lib/esm/Listbox/ListboxGroup.js +2 -1
  35. package/lib/esm/Listbox/ListboxOverlay.js +5 -2
  36. package/lib/esm/Listbox/PressableItem.js +8 -4
  37. package/lib/esm/TextInput/TextInputBase.js +5 -1
  38. package/lib/esm/ThemeProvider/index.js +1 -0
  39. package/lib/esm/ThemeProvider/useResponsiveThemeTokensCallback.js +117 -0
  40. package/lib/esm/Validator/Validator.js +171 -135
  41. package/lib/esm/index.js +1 -1
  42. package/lib/package.json +2 -2
  43. package/package.json +2 -2
  44. package/src/Button/Button.jsx +26 -5
  45. package/src/Button/ButtonBase.jsx +79 -16
  46. package/src/Button/ButtonDropdown.jsx +2 -0
  47. package/src/Button/ButtonGroup.jsx +62 -45
  48. package/src/Button/propTypes.js +6 -0
  49. package/src/Card/PressableCardBase.jsx +3 -1
  50. package/src/Carousel/Carousel.jsx +71 -7
  51. package/src/Carousel/CarouselItem/CarouselItem.jsx +31 -3
  52. package/src/Icon/Icon.jsx +11 -14
  53. package/src/Icon/IconText.jsx +0 -1
  54. package/src/Listbox/GroupControl.jsx +41 -47
  55. package/src/Listbox/Listbox.jsx +26 -9
  56. package/src/Listbox/ListboxGroup.jsx +2 -1
  57. package/src/Listbox/ListboxOverlay.jsx +7 -2
  58. package/src/Listbox/PressableItem.jsx +8 -4
  59. package/src/PriceLockup/utils/renderPrice.jsx +15 -17
  60. package/src/TextInput/TextInputBase.jsx +5 -1
  61. package/src/ThemeProvider/index.js +1 -0
  62. package/src/ThemeProvider/useResponsiveThemeTokensCallback.js +129 -0
  63. package/src/Validator/Validator.jsx +180 -159
  64. package/src/index.js +2 -1
@@ -44,11 +44,10 @@ const Validator = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
44
44
  supportsProps
45
45
  } = selectProps(rest);
46
46
  const strValidation = supportsProps.validation;
47
- const [, setIndividualCodes] = _react.default.useState({});
48
- const [text, setText] = _react.default.useState(value);
49
47
  const validatorsLength = 6;
50
48
  const prefix = 'code';
51
49
  const sufixValidation = 'Validation';
50
+ const [codes, setCodes] = _react.default.useState(() => Array(validatorsLength).fill(''));
52
51
  const [isHover, setIsHover] = _react.default.useState(false);
53
52
  const handleMouseOver = () => {
54
53
  setIsHover(true);
@@ -59,84 +58,87 @@ const Validator = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
59
58
  const themeTokens = (0, _ThemeProvider.useThemeTokens)('TextInput', tokens, variant, {
60
59
  hover: isHover
61
60
  });
62
- const [codeReferences, singleCodes] = _react.default.useMemo(() => {
63
- const codes = [];
64
- const valueCodes = {};
65
- Array.from({
61
+
62
+ // Create refs for input elements
63
+ const codeReferences = _react.default.useMemo(() => {
64
+ return Array.from({
66
65
  length: validatorsLength
67
- }, (_, i) => {
68
- codes[prefix + i] = /*#__PURE__*/_react.default.createRef();
69
- valueCodes[prefix + i] = '';
70
- valueCodes[prefix + i + sufixValidation] = '';
71
- return null;
66
+ }, () => /*#__PURE__*/_react.default.createRef());
67
+ }, [validatorsLength]);
68
+
69
+ // Keep onChange and mask in refs to avoid re-creating event listeners
70
+ const onChangeRef = _react.default.useRef(onChange);
71
+ const maskRef = _react.default.useRef(mask);
72
+ _react.default.useEffect(() => {
73
+ onChangeRef.current = onChange;
74
+ maskRef.current = mask;
75
+ }, [onChange, mask]);
76
+
77
+ // Update a single code digit
78
+ const updateCode = _react.default.useCallback((index, digit) => {
79
+ setCodes(prevCodes => {
80
+ const newCodes = [...prevCodes];
81
+ newCodes[index] = digit;
82
+ if (onChangeRef.current) {
83
+ const codeString = newCodes.join('');
84
+ const singleCodesObj = {};
85
+ newCodes.forEach((code, i) => {
86
+ singleCodesObj[prefix + i] = code;
87
+ singleCodesObj[prefix + i + sufixValidation] = code ? 'success' : '';
88
+ });
89
+ onChangeRef.current(codeString, singleCodesObj);
90
+ }
91
+ return newCodes;
72
92
  });
73
- return [codes, valueCodes];
74
- }, [validatorsLength, prefix, sufixValidation]);
75
- const handleSingleCodes = _react.default.useCallback((codeId, val, validation) => {
76
- singleCodes[codeId] = val;
77
- singleCodes[codeId + sufixValidation] = validation;
78
- setIndividualCodes(prev => ({
79
- ...prev,
80
- [codeId]: val
81
- }));
82
- }, [singleCodes, sufixValidation]);
83
- const changeDataMasking = _react.default.useCallback(boxElement => {
84
- let charMasking = '';
85
- const element = boxElement;
86
- if (mask && mask.length === 1) {
87
- charMasking = mask;
88
- } else if (mask && mask.length > 1) {
89
- charMasking = mask.substring(0, 1);
90
- }
91
- if (charMasking && element) {
92
- element.value = charMasking;
93
- }
94
- }, [mask]);
95
- const handleChangeCode = _react.default.useCallback(() => {
96
- const code = Array.from({
97
- length: validatorsLength
98
- }, (_, i) => singleCodes[prefix + i] || '').join('');
99
- if (typeof onChange === 'function') {
100
- onChange(code, singleCodes);
101
- }
102
- }, [validatorsLength, singleCodes, prefix, onChange]);
103
- const handleChangeCodeValues = _react.default.useCallback((event, codeId, nextIndex) => {
104
- const codeElement = codeReferences[codeId]?.current;
93
+ }, [prefix, sufixValidation]);
94
+
95
+ // Handle input change
96
+ const handleInputChange = _react.default.useCallback((index, event) => {
105
97
  const val = event.nativeEvent?.value || event.target?.value;
106
98
 
107
- // Only allow numeric characters and limit to single digit
108
- const numericOnly = val.replace(/\D/g, '').substring(0, 1);
109
- if (codeElement && codeElement.value) {
110
- codeElement.value = numericOnly;
111
- }
112
- handleSingleCodes(codeId, numericOnly, numericOnly ? 'success' : '');
113
- handleChangeCode();
114
- if (nextIndex === validatorsLength) {
115
- codeElement?.blur();
116
- changeDataMasking(codeElement);
99
+ // This prevents the infinite loop where setting element.value triggers another input event
100
+ if (maskRef.current && val === maskRef.current.substring(0, 1)) {
117
101
  return;
118
102
  }
119
- if (numericOnly.length > 0) {
120
- const nextElement = codeReferences[prefix + nextIndex]?.current;
103
+ const numericOnly = val.replace(/\D/g, '').substring(0, 1);
104
+
105
+ // Update state
106
+ updateCode(index, numericOnly);
107
+
108
+ // Update DOM element
109
+ const element = codeReferences[index]?.current;
110
+ if (element) {
111
+ if (maskRef.current && numericOnly) {
112
+ element.value = maskRef.current.substring(0, 1);
113
+ } else {
114
+ element.value = numericOnly;
115
+ }
116
+ }
117
+
118
+ // Move to next field if digit entered
119
+ if (numericOnly && index < validatorsLength - 1) {
120
+ const nextElement = codeReferences[index + 1]?.current;
121
121
  nextElement?.focus();
122
- changeDataMasking(codeElement);
122
+ } else if (index === validatorsLength - 1) {
123
+ element?.blur();
123
124
  }
124
- }, [codeReferences, handleSingleCodes, handleChangeCode, validatorsLength, changeDataMasking, prefix]);
125
- const handleKeyPress = (event, currentIndex, previousIndex) => {
125
+ }, [codeReferences, updateCode, validatorsLength]);
126
+
127
+ // Handle backspace
128
+ const handleKeyPress = _react.default.useCallback((index, event) => {
126
129
  if (!(event.keyCode === 8 || event.code === 'Backspace')) {
127
130
  return;
128
131
  }
129
- if (currentIndex > 0) {
130
- const currentElement = codeReferences[prefix + currentIndex]?.current;
131
- const previousElement = codeReferences[prefix + previousIndex]?.current;
132
- if (currentElement && currentElement.value) {
133
- currentElement.value = '';
134
- }
132
+ const currentElement = codeReferences[index]?.current;
133
+ if (currentElement) {
134
+ currentElement.value = '';
135
+ }
136
+ updateCode(index, '');
137
+ if (index > 0) {
138
+ const previousElement = codeReferences[index - 1]?.current;
135
139
  previousElement?.focus();
136
140
  }
137
- handleSingleCodes(prefix + currentIndex, '', '');
138
- handleChangeCode();
139
- };
141
+ }, [codeReferences, updateCode]);
140
142
  const getCodeComponents = () => {
141
143
  return Array.from({
142
144
  length: validatorsLength
@@ -145,11 +147,14 @@ const Validator = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
145
147
  const codeInputProps = {
146
148
  nativeID: codeId,
147
149
  keyboardType: 'numeric',
148
- ref: codeReferences[codeId] ?? null,
149
- validation: strValidation || singleCodes[codeId + sufixValidation],
150
+ ref: codeReferences[i] ?? null,
151
+ validation: strValidation || (codes[i] ? 'success' : ''),
150
152
  tokens: selectCodeTextInputTokens(themeTokens),
153
+ // Only use secureTextEntry in React Native, web handles mask differently
154
+ secureTextEntry: !!(_Platform.default.OS !== 'web' && mask),
155
+ selectTextOnFocus: _Platform.default.OS !== 'web',
151
156
  onFocus: () => {
152
- const element = codeReferences[codeId]?.current;
157
+ const element = codeReferences[i]?.current;
153
158
  if (_Platform.default.OS === 'web' && element?.select) {
154
159
  return element.select() ?? null;
155
160
  }
@@ -158,11 +163,35 @@ const Validator = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
158
163
  }
159
164
  return null;
160
165
  },
161
- onKeyPress: event => handleKeyPress(event, i, i - 1),
166
+ onKeyPress: event => handleKeyPress(i, event),
162
167
  onMouseOver: handleMouseOver,
163
168
  onMouseOut: handleMouseOut,
164
169
  inactive
165
170
  };
171
+
172
+ // For React Native, use onChangeText and maxLength
173
+ if (_Platform.default.OS !== 'web') {
174
+ codeInputProps.maxLength = 1;
175
+ codeInputProps.value = codes[i];
176
+ codeInputProps.onChange = () => {};
177
+ codeInputProps.onChangeText = text => {
178
+ if (text) {
179
+ updateCode(i, text);
180
+ if (i < validatorsLength - 1) {
181
+ setTimeout(() => {
182
+ codeReferences[i + 1]?.current?.focus();
183
+ }, 50);
184
+ }
185
+ } else {
186
+ updateCode(i, '');
187
+ if (i > 0) {
188
+ setTimeout(() => {
189
+ codeReferences[i - 1]?.current?.focus();
190
+ }, 50);
191
+ }
192
+ }
193
+ };
194
+ }
166
195
  if (!codeInputProps.validation) {
167
196
  delete codeInputProps.validation;
168
197
  }
@@ -174,89 +203,96 @@ const Validator = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
174
203
  }, codeId);
175
204
  });
176
205
  };
206
+
207
+ // Sync external value prop to internal state
177
208
  _react.default.useEffect(() => {
178
- if (Number(value).toString() !== 'NaN') {
179
- setText(value);
209
+ if (value && Number(value).toString() !== 'NaN') {
210
+ const digits = value.split('').slice(0, validatorsLength);
211
+ const newCodes = Array(validatorsLength).fill('');
212
+ digits.forEach((digit, i) => {
213
+ if (/\d/.test(digit)) {
214
+ newCodes[i] = digit;
215
+ }
216
+ });
217
+ setCodes(newCodes);
180
218
  }
181
- }, [value]);
219
+ }, [value, validatorsLength]);
220
+
221
+ // Sync codes state to DOM elements
182
222
  _react.default.useEffect(() => {
183
- Array.from({
184
- length: validatorsLength
185
- }, (_, i) => {
186
- const element = codeReferences[prefix + i]?.current;
223
+ codes.forEach((code, i) => {
224
+ const element = codeReferences[i]?.current;
187
225
  if (element && element.value !== undefined) {
188
- if (mask && text[i]) {
189
- element.value = mask;
226
+ if (mask && code) {
227
+ element.value = mask.substring(0, 1);
190
228
  } else {
191
- element.value = text[i] ?? '';
229
+ element.value = code;
192
230
  }
193
231
  }
194
- handleSingleCodes(prefix + i, text[i] ?? '', text[i] ? 'success' : '');
195
- return null;
196
232
  });
197
- }, [text, mask, validatorsLength, prefix, codeReferences, handleSingleCodes]);
233
+ }, [codes, codeReferences, mask]);
234
+
235
+ // Setup event listeners - only runs once on mount
198
236
  _react.default.useEffect(() => {
199
- const handlePasteCode = event => {
237
+ if (_Platform.default.OS !== 'web') {
238
+ return undefined;
239
+ }
240
+ const handlePaste = event => {
200
241
  event.preventDefault();
201
-
202
- // Clear current state first
203
- setText('');
204
-
205
- // Clear all individual input fields and their state
206
- Array.from({
207
- length: validatorsLength
208
- }, (_, i) => {
209
- const element = codeReferences[prefix + i]?.current;
210
- if (element && element.value !== undefined) {
211
- element.value = '';
212
- }
213
- handleSingleCodes(prefix + i, '', '');
214
- return null;
215
- });
216
242
  const clipBoardText = event.clipboardData.getData('text');
217
-
218
- // Validate that input contains only digits and truncate to 6 characters
219
243
  const numericOnly = clipBoardText.replace(/\D/g, '').substring(0, validatorsLength);
220
- if (numericOnly.length > 0) {
221
- setText(numericOnly);
244
+ const newCodes = Array(validatorsLength).fill('');
245
+ numericOnly.split('').forEach((digit, i) => {
246
+ newCodes[i] = digit;
247
+ });
248
+ setCodes(newCodes);
249
+ if (onChangeRef.current) {
250
+ const singleCodesObj = {};
251
+ newCodes.forEach((code, i) => {
252
+ singleCodesObj[prefix + i] = code;
253
+ singleCodesObj[prefix + i + sufixValidation] = code ? 'success' : '';
254
+ });
255
+ onChangeRef.current(numericOnly, singleCodesObj);
222
256
  }
223
257
  };
224
258
  const handleCopy = event => {
225
- const clipBoardText = Array.from({
226
- length: validatorsLength
227
- }, (_, i) => singleCodes[prefix + i] || '').join('');
228
- event.clipboardData.setData('text/plain', clipBoardText);
229
- event.preventDefault();
259
+ setCodes(currentCodes => {
260
+ const clipBoardText = currentCodes.join('');
261
+ event.clipboardData.setData('text/plain', clipBoardText);
262
+ event.preventDefault();
263
+ return currentCodes;
264
+ });
230
265
  };
231
- if (_Platform.default.OS === 'web') {
232
- Array.from({
233
- length: validatorsLength
234
- }, (_, i) => {
235
- const element = codeReferences[prefix + i]?.current;
236
- if (element && typeof element.addEventListener === 'function') {
237
- element.addEventListener('paste', handlePasteCode);
238
- element.addEventListener('copy', handleCopy);
239
- element.addEventListener('input', event => handleChangeCodeValues(event, prefix + i, i + 1));
266
+
267
+ // Add event listeners to each input
268
+ codeReferences.forEach((inputRef, i) => {
269
+ const element = inputRef?.current;
270
+ if (element && typeof element.addEventListener === 'function') {
271
+ element.addEventListener('paste', handlePaste);
272
+ element.addEventListener('copy', handleCopy);
273
+ element.addEventListener('input', event => handleInputChange(i, event));
274
+ }
275
+ });
276
+
277
+ // Cleanup
278
+ return () => {
279
+ codeReferences.forEach(inputRef => {
280
+ const element = inputRef?.current;
281
+ if (element && typeof element.removeEventListener === 'function') {
282
+ element.removeEventListener('paste', handlePaste);
283
+ element.removeEventListener('copy', handleCopy);
284
+ element.removeEventListener('input', event => handleInputChange(event.target.dataset.index, event));
240
285
  }
241
- return null;
242
286
  });
243
- }
244
- return () => {
245
- if (_Platform.default.OS === 'web') {
246
- Array.from({
247
- length: validatorsLength
248
- }, (_, i) => {
249
- const element = codeReferences[prefix + i]?.current;
250
- if (element && typeof element.removeEventListener === 'function') {
251
- element.removeEventListener('paste', handlePasteCode);
252
- element.removeEventListener('copy', handleCopy);
253
- element.removeEventListener('input', event => handleChangeCodeValues(event, prefix + i, i + 1));
254
- }
255
- return null;
256
- });
257
- }
258
287
  };
259
- }, [validatorsLength, prefix, codeReferences, handleChangeCodeValues, handleSingleCodes, singleCodes]);
288
+ /*
289
+ * codeReferences and handleInputChange are intentionally omitted from dependencies
290
+ * because we want event listeners to be registered ONLY ONCE when the component mounts.
291
+ * codeReferences is stable (created with useMemo) and handleInputChange is stable (useCallback).
292
+ * Including them would cause unnecessary re-registration of listeners on each render.
293
+ */
294
+ // eslint-disable-next-line react-hooks/exhaustive-deps
295
+ }, []);
260
296
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_InputSupports.default, {
261
297
  ...supportsProps,
262
298
  feedbackProps: {
package/lib/cjs/index.js CHANGED
@@ -93,6 +93,7 @@ var _exportNames = {
93
93
  applyTextStyles: true,
94
94
  applyShadowToken: true,
95
95
  useResponsiveThemeTokens: true,
96
+ useResponsiveThemeTokensCallback: true,
96
97
  Portal: true
97
98
  };
98
99
  Object.defineProperty(exports, "A11yInfoProvider", {
@@ -605,6 +606,12 @@ Object.defineProperty(exports, "useResponsiveThemeTokens", {
605
606
  return _ThemeProvider.useResponsiveThemeTokens;
606
607
  }
607
608
  });
609
+ Object.defineProperty(exports, "useResponsiveThemeTokensCallback", {
610
+ enumerable: true,
611
+ get: function () {
612
+ return _ThemeProvider.useResponsiveThemeTokensCallback;
613
+ }
614
+ });
608
615
  Object.defineProperty(exports, "useSetTheme", {
609
616
  enumerable: true,
610
617
  get: function () {
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import ButtonBase from './ButtonBase';
3
3
  import buttonPropTypes, { textAndA11yText } from './propTypes';
4
- import { useThemeTokensCallback } from '../ThemeProvider';
4
+ import { useThemeTokensCallback, useResponsiveThemeTokensCallback, useTheme } from '../ThemeProvider';
5
5
  import { a11yProps } from '../utils/props';
6
6
  import { useViewport } from '../ViewportProvider';
7
7
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -10,19 +10,28 @@ const Button = /*#__PURE__*/React.forwardRef((_ref, ref) => {
10
10
  accessibilityRole = 'button',
11
11
  tokens,
12
12
  variant,
13
+ heightFull = true,
13
14
  ...props
14
15
  } = _ref;
15
16
  const viewport = useViewport();
16
- const buttonVariant = {
17
+ const {
18
+ themeOptions: {
19
+ enableMediaQueryStyleSheet
20
+ }
21
+ } = useTheme();
22
+ const buttonVariant = enableMediaQueryStyleSheet ? variant : {
17
23
  viewport,
18
24
  ...variant
19
25
  };
20
- const getTokens = useThemeTokensCallback('Button', tokens, buttonVariant);
26
+ const useTokens = enableMediaQueryStyleSheet ? useResponsiveThemeTokensCallback : useThemeTokensCallback;
27
+ const getTokens = useTokens('Button', tokens, buttonVariant);
21
28
  return /*#__PURE__*/_jsx(ButtonBase, {
22
29
  ...props,
23
30
  tokens: getTokens,
31
+ heightFull: heightFull,
24
32
  accessibilityRole: accessibilityRole,
25
- ref: ref
33
+ ref: ref,
34
+ viewport: viewport
26
35
  });
27
36
  });
28
37
  Button.displayName = 'Button';
@@ -6,7 +6,7 @@ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
6
6
  import Platform from "react-native-web/dist/exports/Platform";
7
7
  import { applyTextStyles, applyShadowToken, applyOuterBorder, useTheme } from '../ThemeProvider';
8
8
  import buttonPropTypes from './propTypes';
9
- import { a11yProps, clickProps, focusHandlerProps, getCursorStyle, linkProps, resolvePressableState, resolvePressableTokens, selectSystemProps, viewProps, wrapStringsInText, withLinkRouter, contentfulProps } from '../utils';
9
+ import { a11yProps, clickProps, focusHandlerProps, getCursorStyle, linkProps, resolvePressableState, resolvePressableTokens, selectSystemProps, viewProps, wrapStringsInText, withLinkRouter, contentfulProps, createMediaQueryStyles, StyleSheet as StyleSheetUtils } from '../utils';
10
10
  import { IconText } from '../Icon';
11
11
  import { jsx as _jsx } from "react/jsx-runtime";
12
12
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, linkProps, viewProps, contentfulProps]);
@@ -33,23 +33,27 @@ const selectFlexAndWidthStyles = _ref2 => {
33
33
  }
34
34
  return styles;
35
35
  };
36
- const selectOuterContainerStyles = _ref3 => {
36
+ const selectOuterContainerStyles = (_ref3, heightFull) => {
37
37
  let {
38
38
  opacity,
39
39
  outerBorderColor,
40
40
  outerBorderWidth,
41
41
  outerBorderGap,
42
42
  borderRadius,
43
- outerBackgroundColor
43
+ outerBackgroundColor,
44
+ alignSelf
44
45
  } = _ref3;
45
46
  return {
47
+ backgroundColor: outerBackgroundColor,
48
+ opacity,
46
49
  ...Platform.select({
47
50
  native: {
51
+ alignSelf: alignSelf !== undefined ? alignSelf : 'flex-start'
52
+ },
53
+ web: heightFull ? {} : {
48
54
  alignSelf: 'flex-start'
49
55
  }
50
56
  }),
51
- backgroundColor: outerBackgroundColor,
52
- opacity,
53
57
  ...applyOuterBorder({
54
58
  outerBorderGap,
55
59
  outerBorderWidth,
@@ -248,8 +252,16 @@ const ButtonBase = /*#__PURE__*/React.forwardRef((_ref12, ref) => {
248
252
  icon,
249
253
  iconPosition = icon ? 'left' : undefined,
250
254
  iconProps,
255
+ heightFull = true,
251
256
  ...rawRest
252
257
  } = _ref12;
258
+ const {
259
+ themeOptions
260
+ } = useTheme();
261
+ const {
262
+ viewport
263
+ } = rawRest;
264
+ const enableMediaQueryStyleSheet = themeOptions.enableMediaQueryStyleSheet && viewport;
253
265
  const {
254
266
  onPress,
255
267
  ...rest
@@ -261,15 +273,55 @@ const ButtonBase = /*#__PURE__*/React.forwardRef((_ref12, ref) => {
261
273
  };
262
274
  const resolveButtonTokens = pressableState => resolvePressableTokens(tokens, pressableState, extraButtonState);
263
275
  const systemProps = selectProps(rest);
276
+ let layoutMediaQueryStyles;
277
+ let flexAndWidthStylesIds;
278
+ if (enableMediaQueryStyleSheet) {
279
+ const defaultPressableState = {
280
+ pressed: false,
281
+ hovered: false,
282
+ focused: false
283
+ };
284
+ const defaultTokensByViewport = resolveButtonTokens(defaultPressableState);
285
+ const layoutTokensByViewport = Object.entries(defaultTokensByViewport).reduce((acc, _ref13) => {
286
+ let [vp, viewportTokens] = _ref13;
287
+ const flexAndWidthStyles = viewportTokens.width === '100%' && viewportTokens.flex === 1 ? selectFlexAndWidthStyles(viewportTokens) : {};
288
+ acc[vp] = {
289
+ ...staticStyles.row,
290
+ ...selectWebOnlyStyles(inactive, viewportTokens, systemProps),
291
+ ...(Object.keys(flexAndWidthStyles).length > 0 ? flexAndWidthStyles : {}),
292
+ ...selectOuterSizeStyles(viewportTokens)
293
+ };
294
+ return acc;
295
+ }, {});
296
+ const mediaQueryStyles = createMediaQueryStyles(layoutTokensByViewport);
297
+ const {
298
+ ids,
299
+ styles
300
+ } = StyleSheetUtils.create({
301
+ layout: {
302
+ ...mediaQueryStyles
303
+ }
304
+ });
305
+ layoutMediaQueryStyles = styles.layout;
306
+ flexAndWidthStylesIds = ids.layout;
307
+ }
264
308
  const getPressableStyle = pressableState => {
309
+ if (enableMediaQueryStyleSheet) {
310
+ const themeTokens = resolveButtonTokens(pressableState)[viewport];
311
+ return [layoutMediaQueryStyles, selectOuterContainerStyles(themeTokens)];
312
+ }
265
313
  const themeTokens = resolveButtonTokens(pressableState);
266
- // Only apply flex and width styles when they are explicitly set (e.g., from ButtonGroup with width: 'equal') to not to affect other use cases
267
314
  const flexAndWidthStyles = themeTokens.width === '100%' && themeTokens.flex === 1 ? selectFlexAndWidthStyles(themeTokens) : {};
268
- return [staticStyles.row, selectWebOnlyStyles(inactive, themeTokens, systemProps), selectOuterContainerStyles(themeTokens), ...(Object.keys(flexAndWidthStyles).length > 0 ? [flexAndWidthStyles] : []), selectOuterSizeStyles(themeTokens)];
315
+ return [staticStyles.row, selectWebOnlyStyles(inactive, themeTokens, systemProps), selectOuterContainerStyles(themeTokens, heightFull), ...(Object.keys(flexAndWidthStyles).length > 0 ? [flexAndWidthStyles] : []), selectOuterSizeStyles(themeTokens)];
269
316
  };
270
- const {
271
- themeOptions
272
- } = useTheme();
317
+ const dataSetProp = flexAndWidthStylesIds || rawRest.dataSet ? {
318
+ dataSet: {
319
+ ...(flexAndWidthStylesIds ? {
320
+ media: flexAndWidthStylesIds
321
+ } : {}),
322
+ ...rawRest.dataSet
323
+ }
324
+ } : {};
273
325
  return /*#__PURE__*/_jsx(Pressable, {
274
326
  ref: ref,
275
327
  href: href,
@@ -281,8 +333,9 @@ const ButtonBase = /*#__PURE__*/React.forwardRef((_ref12, ref) => {
281
333
  disabled: inactive,
282
334
  hrefAttrs: hrefAttrs,
283
335
  ...systemProps,
336
+ ...dataSetProp,
284
337
  children: pressableState => {
285
- const themeTokens = resolveButtonTokens(pressableState);
338
+ const themeTokens = enableMediaQueryStyleSheet ? resolveButtonTokens(pressableState)[viewport] : resolveButtonTokens(pressableState);
286
339
  const containerStyles = selectInnerContainerStyles(themeTokens);
287
340
  const borderStyles = selectBorderStyles(themeTokens);
288
341
  const textStyles = [selectTextStyles(themeTokens, themeOptions), staticStyles.text, Platform.select({
@@ -103,6 +103,7 @@ const ButtonDropdown = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
103
103
  accessibilityRole = 'radio',
104
104
  description,
105
105
  singleOption,
106
+ heightFull = true,
106
107
  ...props
107
108
  } = _ref2;
108
109
  const isFullWidth = variant?.width === FULL_WIDTH_STYLE;
@@ -145,6 +146,7 @@ const ButtonDropdown = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
145
146
  ...pressHandlers,
146
147
  onPress: handlePress,
147
148
  tokens: getButtonTokens,
149
+ heightFull: heightFull,
148
150
  inactive: singleOption || inactive,
149
151
  icon: () => null,
150
152
  accessibilityRole: accessibilityRole,