@telus-uds/components-base 3.20.0 → 3.22.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 (40) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/lib/cjs/Button/Button.js +10 -3
  3. package/lib/cjs/Button/ButtonBase.js +53 -5
  4. package/lib/cjs/Card/PressableCardBase.js +3 -1
  5. package/lib/cjs/Carousel/Carousel.js +11 -3
  6. package/lib/cjs/Responsive/ResponsiveWithMediaQueryStyleSheet.js +1 -1
  7. package/lib/cjs/StackView/StackView.js +62 -12
  8. package/lib/cjs/Tabs/TabsDropdown.js +4 -5
  9. package/lib/cjs/ThemeProvider/index.js +9 -1
  10. package/lib/cjs/ThemeProvider/useResponsiveThemeTokensCallback.js +124 -0
  11. package/lib/cjs/index.js +7 -0
  12. package/lib/cjs/utils/ssr-media-query/index.js +7 -0
  13. package/lib/cjs/utils/ssr-media-query/utils/use-all-viewport-tokens.js +53 -0
  14. package/lib/esm/Button/Button.js +11 -4
  15. package/lib/esm/Button/ButtonBase.js +54 -6
  16. package/lib/esm/Card/PressableCardBase.js +3 -1
  17. package/lib/esm/Carousel/Carousel.js +11 -3
  18. package/lib/esm/Responsive/ResponsiveWithMediaQueryStyleSheet.js +1 -1
  19. package/lib/esm/StackView/StackView.js +63 -13
  20. package/lib/esm/Tabs/TabsDropdown.js +4 -5
  21. package/lib/esm/ThemeProvider/index.js +1 -0
  22. package/lib/esm/ThemeProvider/useResponsiveThemeTokensCallback.js +117 -0
  23. package/lib/esm/index.js +1 -1
  24. package/lib/esm/utils/ssr-media-query/index.js +2 -1
  25. package/lib/esm/utils/ssr-media-query/utils/use-all-viewport-tokens.js +48 -0
  26. package/lib/package.json +4 -4
  27. package/package.json +4 -4
  28. package/src/Button/Button.jsx +24 -4
  29. package/src/Button/ButtonBase.jsx +61 -4
  30. package/src/Card/PressableCardBase.jsx +3 -1
  31. package/src/Carousel/Carousel.jsx +13 -2
  32. package/src/PriceLockup/utils/renderPrice.jsx +15 -17
  33. package/src/Responsive/ResponsiveWithMediaQueryStyleSheet.jsx +1 -1
  34. package/src/StackView/StackView.jsx +62 -9
  35. package/src/Tabs/TabsDropdown.jsx +10 -9
  36. package/src/ThemeProvider/index.js +1 -0
  37. package/src/ThemeProvider/useResponsiveThemeTokensCallback.js +129 -0
  38. package/src/index.js +2 -1
  39. package/src/utils/ssr-media-query/index.js +2 -1
  40. package/src/utils/ssr-media-query/utils/use-all-viewport-tokens.js +32 -0
package/CHANGELOG.md CHANGED
@@ -1,9 +1,35 @@
1
1
  # Change Log - @telus-uds/components-base
2
2
 
3
- This log was last generated on Fri, 10 Oct 2025 15:11:05 GMT and should not be manually modified.
3
+ This log was last generated on Wed, 29 Oct 2025 07:40:46 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 3.22.0
8
+
9
+ Wed, 29 Oct 2025 07:40:46 GMT
10
+
11
+ ### Minor changes
12
+
13
+ - `Carousel`: Add `loopDuration` prop (oscar.palencia@telus.com)
14
+ - `Button`: add RNMQ support (guillermo.peitzner@telus.com)
15
+
16
+ ### Patches
17
+
18
+ - `Interactive Card`: extra spacing fixed in interactive Cards when padding tokens are set to none (35577399+JoshHC@users.noreply.github.com)
19
+
20
+ ## 3.21.0
21
+
22
+ Tue, 21 Oct 2025 14:46:26 GMT
23
+
24
+ ### Minor changes
25
+
26
+ - `StackView`: add RNMQ support (guillermo.peitzner@telus.com)
27
+
28
+ ### Patches
29
+
30
+ - `Responsive`: fix console warning (guillermo.peitzner@telus.com)
31
+ - `TabsDropdown`: outline unnecessary property removed (35577399+JoshHC@users.noreply.github.com)
32
+
7
33
  ## 3.20.0
8
34
 
9
35
  Fri, 10 Oct 2025 15:11:05 GMT
@@ -22,16 +22,23 @@ const Button = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
22
22
  ...props
23
23
  } = _ref;
24
24
  const viewport = (0, _ViewportProvider.useViewport)();
25
- const buttonVariant = {
25
+ const {
26
+ themeOptions: {
27
+ enableMediaQueryStyleSheet
28
+ }
29
+ } = (0, _ThemeProvider.useTheme)();
30
+ const buttonVariant = enableMediaQueryStyleSheet ? variant : {
26
31
  viewport,
27
32
  ...variant
28
33
  };
29
- const getTokens = (0, _ThemeProvider.useThemeTokensCallback)('Button', tokens, buttonVariant);
34
+ const useTokens = enableMediaQueryStyleSheet ? _ThemeProvider.useResponsiveThemeTokensCallback : _ThemeProvider.useThemeTokensCallback;
35
+ const getTokens = useTokens('Button', tokens, buttonVariant);
30
36
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ButtonBase.default, {
31
37
  ...props,
32
38
  tokens: getTokens,
33
39
  accessibilityRole: accessibilityRole,
34
- ref: ref
40
+ ref: ref,
41
+ viewport: viewport
35
42
  });
36
43
  });
37
44
  Button.displayName = 'Button';
@@ -257,6 +257,13 @@ const ButtonBase = /*#__PURE__*/_react.default.forwardRef((_ref12, ref) => {
257
257
  iconProps,
258
258
  ...rawRest
259
259
  } = _ref12;
260
+ const {
261
+ themeOptions
262
+ } = (0, _ThemeProvider.useTheme)();
263
+ const {
264
+ viewport
265
+ } = rawRest;
266
+ const enableMediaQueryStyleSheet = themeOptions.enableMediaQueryStyleSheet && viewport;
260
267
  const {
261
268
  onPress,
262
269
  ...rest
@@ -268,15 +275,55 @@ const ButtonBase = /*#__PURE__*/_react.default.forwardRef((_ref12, ref) => {
268
275
  };
269
276
  const resolveButtonTokens = pressableState => (0, _utils.resolvePressableTokens)(tokens, pressableState, extraButtonState);
270
277
  const systemProps = selectProps(rest);
278
+ let layoutMediaQueryStyles;
279
+ let flexAndWidthStylesIds;
280
+ if (enableMediaQueryStyleSheet) {
281
+ const defaultPressableState = {
282
+ pressed: false,
283
+ hovered: false,
284
+ focused: false
285
+ };
286
+ const defaultTokensByViewport = resolveButtonTokens(defaultPressableState);
287
+ const layoutTokensByViewport = Object.entries(defaultTokensByViewport).reduce((acc, _ref13) => {
288
+ let [vp, viewportTokens] = _ref13;
289
+ const flexAndWidthStyles = viewportTokens.width === '100%' && viewportTokens.flex === 1 ? selectFlexAndWidthStyles(viewportTokens) : {};
290
+ acc[vp] = {
291
+ ...staticStyles.row,
292
+ ...selectWebOnlyStyles(inactive, viewportTokens, systemProps),
293
+ ...(Object.keys(flexAndWidthStyles).length > 0 ? flexAndWidthStyles : {}),
294
+ ...selectOuterSizeStyles(viewportTokens)
295
+ };
296
+ return acc;
297
+ }, {});
298
+ const mediaQueryStyles = (0, _utils.createMediaQueryStyles)(layoutTokensByViewport);
299
+ const {
300
+ ids,
301
+ styles
302
+ } = _utils.StyleSheet.create({
303
+ layout: {
304
+ ...mediaQueryStyles
305
+ }
306
+ });
307
+ layoutMediaQueryStyles = styles.layout;
308
+ flexAndWidthStylesIds = ids.layout;
309
+ }
271
310
  const getPressableStyle = pressableState => {
311
+ if (enableMediaQueryStyleSheet) {
312
+ const themeTokens = resolveButtonTokens(pressableState)[viewport];
313
+ return [layoutMediaQueryStyles, selectOuterContainerStyles(themeTokens)];
314
+ }
272
315
  const themeTokens = resolveButtonTokens(pressableState);
273
- // 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
274
316
  const flexAndWidthStyles = themeTokens.width === '100%' && themeTokens.flex === 1 ? selectFlexAndWidthStyles(themeTokens) : {};
275
317
  return [staticStyles.row, selectWebOnlyStyles(inactive, themeTokens, systemProps), selectOuterContainerStyles(themeTokens), ...(Object.keys(flexAndWidthStyles).length > 0 ? [flexAndWidthStyles] : []), selectOuterSizeStyles(themeTokens)];
276
318
  };
277
- const {
278
- themeOptions
279
- } = (0, _ThemeProvider.useTheme)();
319
+ const dataSetProp = flexAndWidthStylesIds || rawRest.dataSet ? {
320
+ dataSet: {
321
+ ...(flexAndWidthStylesIds ? {
322
+ media: flexAndWidthStylesIds
323
+ } : {}),
324
+ ...rawRest.dataSet
325
+ }
326
+ } : {};
280
327
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Pressable.default, {
281
328
  ref: ref,
282
329
  href: href,
@@ -288,8 +335,9 @@ const ButtonBase = /*#__PURE__*/_react.default.forwardRef((_ref12, ref) => {
288
335
  disabled: inactive,
289
336
  hrefAttrs: hrefAttrs,
290
337
  ...systemProps,
338
+ ...dataSetProp,
291
339
  children: pressableState => {
292
- const themeTokens = resolveButtonTokens(pressableState);
340
+ const themeTokens = enableMediaQueryStyleSheet ? resolveButtonTokens(pressableState)[viewport] : resolveButtonTokens(pressableState);
293
341
  const containerStyles = selectInnerContainerStyles(themeTokens);
294
342
  const borderStyles = selectBorderStyles(themeTokens);
295
343
  const textStyles = [selectTextStyles(themeTokens, themeOptions), staticStyles.text, _Platform.default.select({
@@ -181,7 +181,9 @@ const staticStyles = _StyleSheet.default.create({
181
181
  },
182
182
  linkContainer: {
183
183
  flex: 1,
184
- display: 'flex'
184
+ display: 'flex',
185
+ alignItems: 'stretch',
186
+ justifyContent: 'flex-start'
185
187
  }
186
188
  });
187
189
  PressableCardBase.displayName = 'PressableCardBase';
@@ -358,6 +358,7 @@ const Carousel = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
358
358
  copy,
359
359
  slideDuration = 0,
360
360
  transitionDuration = 0,
361
+ loopDuration = transitionDuration,
361
362
  autoPlay = false,
362
363
  enablePeeking = false,
363
364
  ...rest
@@ -477,6 +478,7 @@ const Carousel = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
477
478
  }
478
479
  }, [pan, animatedX, heroPan, heroAnimatedX, enableHero, viewport, enablePeeking]);
479
480
  const animate = _react.default.useCallback((panToAnimate, toValue, toIndex) => {
481
+ const applicableTransitionDuration = isLastSlide && toIndex === 0 ? loopDuration : transitionDuration;
480
482
  const handleAnimationEndToIndex = function () {
481
483
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
482
484
  args[_key] = arguments[_key];
@@ -494,14 +496,14 @@ const Carousel = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
494
496
  ...springConfig,
495
497
  toValue,
496
498
  useNativeDriver: false,
497
- duration: transitionDuration * 1000
499
+ duration: applicableTransitionDuration * 1000
498
500
  }).start(handleAnimationEndToIndex);
499
501
  } else if (enablePeeking || enableDisplayMultipleItemsPerSlide) {
500
502
  _Animated.default.timing(panToAnimate, {
501
503
  ...springConfig,
502
504
  toValue,
503
505
  useNativeDriver: false,
504
- duration: transitionDuration ? transitionDuration * 1000 : 1000
506
+ duration: applicableTransitionDuration ? applicableTransitionDuration * 1000 : 1000
505
507
  }).start(handleAnimationEndToIndex);
506
508
  } else {
507
509
  _Animated.default.spring(panToAnimate, {
@@ -510,7 +512,7 @@ const Carousel = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
510
512
  useNativeDriver: false
511
513
  }).start(handleAnimationEndToIndex);
512
514
  }
513
- }, [springConfig, handleAnimationEnd, transitionDuration, isAutoPlayEnabled, enablePeeking, enableDisplayMultipleItemsPerSlide]);
515
+ }, [springConfig, handleAnimationEnd, transitionDuration, loopDuration, isLastSlide, isAutoPlayEnabled, enablePeeking, enableDisplayMultipleItemsPerSlide]);
514
516
  const stopAutoplay = _react.default.useCallback(() => {
515
517
  if (autoPlayRef?.current) {
516
518
  clearTimeout(autoPlayRef?.current);
@@ -1204,6 +1206,12 @@ Carousel.propTypes = {
1204
1206
  * - `autoPlay` and `slideDuration` are required to be set for this to work
1205
1207
  */
1206
1208
  transitionDuration: _propTypes.default.number,
1209
+ /**
1210
+ * Time it takes in seconds to transition from last slide to first slide
1211
+ * - Default value equals `transitionDuration`'s value
1212
+ * - `autoPlay` and `transitionDuration` are required to be set for this to work
1213
+ */
1214
+ loopDuration: _propTypes.default.number,
1207
1215
  /**
1208
1216
  * If set to `true`, the Carousel will show the previous and next slides
1209
1217
  * - Default value is `false`
@@ -68,7 +68,7 @@ ResponsiveWithMediaQueryStyleSheet.propTypes = {
68
68
  /**
69
69
  * To hide children of `Responsive` if the current viewport is larger than `max`
70
70
  */
71
- max: _propTypes.default.oneOf(['sm', 'md', 'lg', 'xl']),
71
+ max: _propTypes.default.oneOf(['xs', 'sm', 'md', 'lg', 'xl']),
72
72
  inheritedStyles: _propTypes.default.arrayOf(_propTypes.default.string),
73
73
  children: _propTypes.default.node.isRequired
74
74
  };
@@ -9,8 +9,7 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
9
9
  var _View = _interopRequireDefault(require("react-native-web/dist/cjs/exports/View"));
10
10
  var _Divider = _interopRequireDefault(require("../Divider"));
11
11
  var _utils = require("../utils");
12
- var _ThemeProvider = require("../ThemeProvider");
13
- var _ViewportProvider = require("../ViewportProvider");
12
+ var _useTheme = _interopRequireDefault(require("../ThemeProvider/useTheme"));
14
13
  var _getStackedContent = _interopRequireDefault(require("./getStackedContent"));
15
14
  var _common = require("./common");
16
15
  var _jsxRuntime = require("react/jsx-runtime");
@@ -69,10 +68,15 @@ const StackView = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
69
68
  tokens,
70
69
  tag,
71
70
  accessibilityRole,
71
+ dataSet,
72
72
  ...rest
73
73
  } = _ref;
74
- const viewport = (0, _ViewportProvider.useViewport)();
75
74
  const direction = (0, _utils.useResponsiveProp)(directionProp, 'column');
75
+ const {
76
+ themeOptions: {
77
+ enableMediaQueryStyleSheet
78
+ }
79
+ } = (0, _useTheme.default)();
76
80
  const selectedProps = selectProps({
77
81
  accessibilityRole,
78
82
  ...(0, _utils.getA11yPropsFromHtmlTag)(tag, accessibilityRole),
@@ -83,17 +87,58 @@ const StackView = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
83
87
  divider,
84
88
  space
85
89
  });
86
- const themeTokens = (0, _ThemeProvider.useThemeTokens)('StackView', tokens, variant, {
87
- viewport
88
- });
89
- const flexStyles = (0, _common.selectFlexStyles)(themeTokens);
90
- const size = {
91
- width: themeTokens.width
92
- };
90
+ const allTokens = (0, _utils.useAllViewportTokens)('StackView', tokens, variant);
91
+ let stackViewStyles;
92
+ let dataSetValue = dataSet;
93
+ if (enableMediaQueryStyleSheet) {
94
+ const stylesByViewport = {
95
+ xs: {
96
+ ...(0, _common.selectFlexStyles)(allTokens.xs),
97
+ width: allTokens.xs.width
98
+ },
99
+ sm: {
100
+ ...(0, _common.selectFlexStyles)(allTokens.sm),
101
+ width: allTokens.sm.width
102
+ },
103
+ md: {
104
+ ...(0, _common.selectFlexStyles)(allTokens.md),
105
+ width: allTokens.md.width
106
+ },
107
+ lg: {
108
+ ...(0, _common.selectFlexStyles)(allTokens.lg),
109
+ width: allTokens.lg.width
110
+ },
111
+ xl: {
112
+ ...(0, _common.selectFlexStyles)(allTokens.xl),
113
+ width: allTokens.xl.width
114
+ }
115
+ };
116
+ const mediaQueryStyles = (0, _utils.createMediaQueryStyles)(stylesByViewport);
117
+ const {
118
+ ids,
119
+ styles
120
+ } = _utils.StyleSheet.create({
121
+ stackView: {
122
+ ...mediaQueryStyles
123
+ }
124
+ });
125
+ stackViewStyles = [_common.staticStyles[direction], styles.stackView];
126
+ dataSetValue = {
127
+ media: ids.stackView,
128
+ ...dataSet
129
+ };
130
+ } else {
131
+ const flexStyles = (0, _common.selectFlexStyles)(allTokens.current);
132
+ const size = {
133
+ width: allTokens.current.width
134
+ };
135
+ stackViewStyles = [flexStyles, _common.staticStyles[direction], size];
136
+ }
93
137
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
94
138
  ref: ref,
95
139
  ...selectedProps,
96
- style: [flexStyles, _common.staticStyles[direction], size],
140
+ style: stackViewStyles,
141
+ dataSet: dataSetValue,
97
142
  children: content
98
143
  });
99
144
  });
@@ -128,6 +173,11 @@ StackView.propTypes = {
128
173
  * A StackView may take any children, but will have no effect if it is only passed one child or is passed children
129
174
  * wrapped in a component. If necessary, children may be wrapped in one React Fragment.
130
175
  */
131
- children: _propTypes.default.node
176
+ children: _propTypes.default.node,
177
+ /**
178
+ * Data attributes to be applied to the element. When media query stylesheet is enabled,
179
+ * this will include media query IDs for responsive styling.
180
+ */
181
+ dataSet: _propTypes.default.object
132
182
  };
133
183
  var _default = exports.default = StackView;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  var _react = _interopRequireDefault(require("react"));
8
8
  var _propTypes = _interopRequireDefault(require("prop-types"));
9
+ var _Platform = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Platform"));
9
10
  var _Pressable = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Pressable"));
10
11
  var _StyleSheet = _interopRequireDefault(require("react-native-web/dist/cjs/exports/StyleSheet"));
11
12
  var _Text = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Text"));
@@ -233,20 +234,18 @@ const styles = _StyleSheet.default.create({
233
234
  position: 'relative',
234
235
  width: '100%'
235
236
  },
236
- pressable: {
237
+ pressable: _Platform.default.OS === 'web' ? {
237
238
  outlineWidth: 0,
238
239
  outlineStyle: 'none',
239
240
  outlineColor: 'transparent'
240
- },
241
+ } : {},
241
242
  buttonContent: {
242
243
  display: 'flex',
243
244
  flexDirection: 'row',
244
245
  alignItems: 'center',
245
246
  justifyContent: 'space-between',
246
247
  width: '100%',
247
- minHeight: 44,
248
- outline: 'none',
249
- boxSizing: 'border-box'
248
+ minHeight: 44
250
249
  }
251
250
  });
252
251
  var _default = exports.default = TabsDropdown;
@@ -6,7 +6,8 @@ Object.defineProperty(exports, "__esModule", {
6
6
  var _exportNames = {
7
7
  useTheme: true,
8
8
  useSetTheme: true,
9
- useResponsiveThemeTokens: true
9
+ useResponsiveThemeTokens: true,
10
+ useResponsiveThemeTokensCallback: true
10
11
  };
11
12
  exports.default = void 0;
12
13
  Object.defineProperty(exports, "useResponsiveThemeTokens", {
@@ -15,6 +16,12 @@ Object.defineProperty(exports, "useResponsiveThemeTokens", {
15
16
  return _useResponsiveThemeTokens.default;
16
17
  }
17
18
  });
19
+ Object.defineProperty(exports, "useResponsiveThemeTokensCallback", {
20
+ enumerable: true,
21
+ get: function () {
22
+ return _useResponsiveThemeTokensCallback.default;
23
+ }
24
+ });
18
25
  Object.defineProperty(exports, "useSetTheme", {
19
26
  enumerable: true,
20
27
  get: function () {
@@ -31,6 +38,7 @@ var _ThemeProvider = _interopRequireDefault(require("./ThemeProvider"));
31
38
  var _useTheme = _interopRequireDefault(require("./useTheme"));
32
39
  var _useSetTheme = _interopRequireDefault(require("./useSetTheme"));
33
40
  var _useResponsiveThemeTokens = _interopRequireDefault(require("./useResponsiveThemeTokens"));
41
+ var _useResponsiveThemeTokensCallback = _interopRequireDefault(require("./useResponsiveThemeTokensCallback"));
34
42
  var _useThemeTokens = require("./useThemeTokens");
35
43
  Object.keys(_useThemeTokens).forEach(function (key) {
36
44
  if (key === "default" || key === "__esModule") return;
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = require("react");
8
+ var _systemConstants = require("@telus-uds/system-constants");
9
+ var _useTheme = _interopRequireDefault(require("./useTheme"));
10
+ var _utils = require("./utils");
11
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
+ const getResponsiveThemeTokens = function (_ref, tokensProp) {
13
+ let {
14
+ rules = [],
15
+ tokens: defaultThemeTokens = {}
16
+ } = _ref;
17
+ let variants = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
18
+ let states = arguments.length > 3 ? arguments[3] : undefined;
19
+ const appearances = (0, _utils.mergeAppearances)(variants, states);
20
+ const tokensByViewport = Object.fromEntries(_systemConstants.viewports.keys.map(viewport => [viewport, {
21
+ ...defaultThemeTokens
22
+ }]));
23
+
24
+ // Go through each rule and collect them for the corresponding viewport if they apply
25
+ rules.forEach(rule => {
26
+ if (doesRuleApply(rule, appearances)) {
27
+ // If the rule does not have a viewport specified, we collect it in all viewports
28
+ let targetViewports = rule.if.viewport || _systemConstants.viewports.keys;
29
+ if (!Array.isArray(targetViewports)) {
30
+ targetViewports = [targetViewports];
31
+ }
32
+ targetViewports.forEach(viewport => {
33
+ tokensByViewport[viewport] = {
34
+ ...tokensByViewport[viewport],
35
+ ...rule.tokens
36
+ };
37
+ });
38
+ }
39
+ });
40
+ Object.keys(tokensByViewport).forEach(viewport => {
41
+ tokensByViewport[viewport] = (0, _utils.resolveThemeTokens)(tokensByViewport[viewport], appearances, tokensProp);
42
+ });
43
+ return tokensByViewport;
44
+ };
45
+ const doesRuleApply = (rule, appearances) => Object.entries(rule.if).every(condition => doesConditionApply(condition, appearances));
46
+ const doesConditionApply = (_ref2, appearances) => {
47
+ let [key, value] = _ref2;
48
+ if (key === 'viewport') {
49
+ return true;
50
+ }
51
+ // use null rather than undefined so we can serialise the value in themes
52
+ const appearanceValue = appearances[key] ?? null;
53
+ return Array.isArray(value) ? value.includes(appearanceValue) : value === appearanceValue;
54
+ };
55
+
56
+ /**
57
+ * @typedef {import('../utils/props/tokens.js').TokensSet} TokensSet
58
+ * @typedef {import('../utils/props/tokens.js').TokensProp} TokensProp
59
+ * @typedef {import('../utils/props/variantProp').AppearanceSet} AppearanceSet
60
+ */
61
+
62
+ /**
63
+ * Returns a memoised tokens getter function that gets responsive tokens for all viewports,
64
+ * similar to calling useResponsiveThemeTokens but with the callback pattern of useThemeTokensCallback.
65
+ *
66
+ * Scenarios where `useResponsiveThemeTokensCallback` should be used:
67
+ *
68
+ * - Where responsive tokens are to be obtained from state that is accessible only in scopes like callbacks
69
+ * and render functions, where calling useResponsiveThemeTokens directly would be disallowed by React's hook rules.
70
+ * - When using media query stylesheets and need to resolve tokens based on dynamic state (e.g., pressed, hovered)
71
+ * that changes at runtime.
72
+ * - Passing a responsive tokens getter down via a child component's `tokens` prop, applying rules using the
73
+ * child component's current state.
74
+ *
75
+ * The function returned may be called with an object of state appearances to get an object
76
+ * of tokens for each viewport, which can then be passed to createMediaQueryStyles.
77
+ *
78
+ * @example
79
+ * // Resolving responsive tokens inside Pressable's style function, based on Pressable state
80
+ * const PressMe = ({ tokens, variant, children }) => {
81
+ * const getResponsiveTokens = useResponsiveThemeTokensCallback('PressMe', tokens, variant)
82
+ * const getPressableStyle = ({ pressed }) => {
83
+ * const responsiveTokens = getResponsiveTokens({ pressed })
84
+ * const mediaQueryStyles = createMediaQueryStyles(responsiveTokens)
85
+ * return mediaQueryStyles
86
+ * }
87
+ * return <Pressable style={getPressableStyle}>{children}</Pressable>
88
+ * }
89
+ *
90
+ * @example
91
+ * // Setting the theme in a parent and resolving it in a child based on child's state
92
+ * const MenuButton = ({ tokens, variant, ...buttonProps }) => {
93
+ * // Define what theme, variant etc we want in this component...
94
+ * const getResponsiveTokens = useResponsiveThemeTokensCallback('Button', tokens, variant)
95
+ * // ...resolve them in another component based on its state (e.g. press, hover...)
96
+ * return <ButtonBase tokens={getResponsiveTokens} accessibilityRole="menuitem" {...buttonProps} />
97
+ * }
98
+ *
99
+ * @typedef {Object} ResponsiveObject
100
+ * @property {TokensSet} xs
101
+ * @property {TokensSet} sm
102
+ * @property {TokensSet} md
103
+ * @property {TokensSet} lg
104
+ * @property {TokensSet} xl
105
+ *
106
+ * @param {string} componentName - the name as defined in the theme schema of the component whose theme is to be used
107
+ * @param {TokensProp} [tokens] - every themed component should accept a `tokens` prop allowing theme tokens to be overridden
108
+ * @param {AppearanceSet} [variants] - variants passed in as props that don't change dynamically
109
+ * @returns {(states: AppearanceSet, tokenOverrides?: TokensProp) => ResponsiveObject}
110
+ * - callback function that returns an overridable responsive tokens object for current state. Only pass
111
+ * tokenOverrides in rare cases where tokens overrides are also generated outside hook scope.
112
+ */
113
+ const useResponsiveThemeTokensCallback = function (componentName) {
114
+ let tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
115
+ let variants = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
116
+ const theme = (0, _useTheme.default)();
117
+ const componentTheme = (0, _utils.getComponentTheme)(theme, componentName);
118
+ const getResponsiveThemeTokensCallback = (0, _react.useCallback)((states, tokenOverrides) => {
119
+ const resolvedTokens = (0, _utils.resolveThemeTokens)(tokens, (0, _utils.mergeAppearances)(variants, states), tokenOverrides);
120
+ return getResponsiveThemeTokens(componentTheme, resolvedTokens, variants, states);
121
+ }, [componentTheme, tokens, variants]);
122
+ return getResponsiveThemeTokensCallback;
123
+ };
124
+ var _default = exports.default = useResponsiveThemeTokensCallback;
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 () {
@@ -10,8 +10,15 @@ Object.defineProperty(exports, "createMediaQueryStyles", {
10
10
  return _createMediaQueryStyles.default;
11
11
  }
12
12
  });
13
+ Object.defineProperty(exports, "useAllViewportTokens", {
14
+ enumerable: true,
15
+ get: function () {
16
+ return _useAllViewportTokens.default;
17
+ }
18
+ });
13
19
  var _createStylesheet = _interopRequireDefault(require("./create-stylesheet"));
14
20
  var _createMediaQueryStyles = _interopRequireDefault(require("./utils/create-media-query-styles"));
21
+ var _useAllViewportTokens = _interopRequireDefault(require("./utils/use-all-viewport-tokens"));
15
22
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
23
  const StyleSheet = exports.StyleSheet = {
17
24
  create: _createStylesheet.default
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _ThemeProvider = require("../../../ThemeProvider");
8
+ var _ViewportProvider = require("../../../ViewportProvider");
9
+ /**
10
+ * Hook to get theme tokens for all viewports at once.
11
+ * This is useful for components that need to support React Native Media Queries (RNMQ).
12
+ *
13
+ * All hooks are called unconditionally to comply with React's Rules of Hooks.
14
+ *
15
+ * @param {string} componentName - The name of the component to get tokens for
16
+ * @param {object|function} tokens - Custom tokens or token function
17
+ * @param {object} variant - Variant configuration
18
+ * @returns {object} Object with tokens for each viewport (xs, sm, md, lg, xl, current)
19
+ *
20
+ * @example
21
+ * const allTokens = useAllViewportTokens('StackView', tokens, variant)
22
+ * // Returns: { xs: {...}, sm: {...}, md: {...}, lg: {...}, xl: {...}, current: {...} }
23
+ */
24
+ const useAllViewportTokens = (componentName, tokens, variant) => {
25
+ const viewport = (0, _ViewportProvider.useViewport)();
26
+ const xs = (0, _ThemeProvider.useThemeTokens)(componentName, tokens, variant, {
27
+ viewport: 'xs'
28
+ });
29
+ const sm = (0, _ThemeProvider.useThemeTokens)(componentName, tokens, variant, {
30
+ viewport: 'sm'
31
+ });
32
+ const md = (0, _ThemeProvider.useThemeTokens)(componentName, tokens, variant, {
33
+ viewport: 'md'
34
+ });
35
+ const lg = (0, _ThemeProvider.useThemeTokens)(componentName, tokens, variant, {
36
+ viewport: 'lg'
37
+ });
38
+ const xl = (0, _ThemeProvider.useThemeTokens)(componentName, tokens, variant, {
39
+ viewport: 'xl'
40
+ });
41
+ const current = (0, _ThemeProvider.useThemeTokens)(componentName, tokens, variant, {
42
+ viewport
43
+ });
44
+ return {
45
+ xs,
46
+ sm,
47
+ md,
48
+ lg,
49
+ xl,
50
+ current
51
+ };
52
+ };
53
+ var _default = exports.default = useAllViewportTokens;
@@ -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";
@@ -13,16 +13,23 @@ const Button = /*#__PURE__*/React.forwardRef((_ref, ref) => {
13
13
  ...props
14
14
  } = _ref;
15
15
  const viewport = useViewport();
16
- const buttonVariant = {
16
+ const {
17
+ themeOptions: {
18
+ enableMediaQueryStyleSheet
19
+ }
20
+ } = useTheme();
21
+ const buttonVariant = enableMediaQueryStyleSheet ? variant : {
17
22
  viewport,
18
23
  ...variant
19
24
  };
20
- const getTokens = useThemeTokensCallback('Button', tokens, buttonVariant);
25
+ const useTokens = enableMediaQueryStyleSheet ? useResponsiveThemeTokensCallback : useThemeTokensCallback;
26
+ const getTokens = useTokens('Button', tokens, buttonVariant);
21
27
  return /*#__PURE__*/_jsx(ButtonBase, {
22
28
  ...props,
23
29
  tokens: getTokens,
24
30
  accessibilityRole: accessibilityRole,
25
- ref: ref
31
+ ref: ref,
32
+ viewport: viewport
26
33
  });
27
34
  });
28
35
  Button.displayName = 'Button';