@telus-uds/components-base 3.24.0 → 3.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -1
- package/lib/cjs/Button/ButtonGroup.js +9 -2
- package/lib/cjs/Carousel/Carousel.js +90 -30
- package/lib/cjs/Carousel/Constants.js +13 -2
- package/lib/cjs/FlexGrid/FlexGrid.js +31 -35
- package/lib/cjs/IconButton/IconButton.js +15 -5
- package/lib/cjs/InputSupports/InputSupports.js +2 -1
- package/lib/cjs/PriceLockup/PriceLockup.js +1 -1
- package/lib/cjs/TextInput/TextInputBase.js +2 -3
- package/lib/cjs/utils/index.js +9 -1
- package/lib/cjs/utils/resolveContentMaxWidth.js +30 -0
- package/lib/esm/Button/ButtonGroup.js +9 -2
- package/lib/esm/Carousel/Carousel.js +84 -24
- package/lib/esm/Carousel/Constants.js +12 -1
- package/lib/esm/FlexGrid/FlexGrid.js +31 -35
- package/lib/esm/IconButton/IconButton.js +15 -5
- package/lib/esm/InputSupports/InputSupports.js +2 -1
- package/lib/esm/PriceLockup/PriceLockup.js +1 -1
- package/lib/esm/TextInput/TextInputBase.js +2 -3
- package/lib/esm/utils/index.js +2 -1
- package/lib/esm/utils/resolveContentMaxWidth.js +24 -0
- package/lib/package.json +2 -2
- package/package.json +2 -2
- package/src/Button/ButtonGroup.jsx +20 -3
- package/src/Carousel/Carousel.jsx +104 -30
- package/src/Carousel/Constants.js +14 -0
- package/src/FlexGrid/FlexGrid.jsx +30 -39
- package/src/IconButton/IconButton.jsx +12 -5
- package/src/InputSupports/InputSupports.jsx +6 -1
- package/src/PriceLockup/PriceLockup.jsx +1 -1
- package/src/TextInput/TextInputBase.jsx +2 -2
- package/src/utils/index.js +1 -0
- package/src/utils/resolveContentMaxWidth.js +28 -0
- package/types/Status.d.ts +42 -0
- package/types/index.d.ts +3 -0
|
@@ -151,7 +151,17 @@ const ButtonGroup = React.forwardRef(
|
|
|
151
151
|
>
|
|
152
152
|
<View accessibilityRole={innerRole} style={viewStyles}>
|
|
153
153
|
{items.map(
|
|
154
|
-
(
|
|
154
|
+
(
|
|
155
|
+
{
|
|
156
|
+
label,
|
|
157
|
+
id = label,
|
|
158
|
+
accessibilityLabel,
|
|
159
|
+
ref: itemRef,
|
|
160
|
+
inactive: itemInactive,
|
|
161
|
+
...itemRest
|
|
162
|
+
},
|
|
163
|
+
index
|
|
164
|
+
) => {
|
|
155
165
|
const isSelected = currentValues.includes(id)
|
|
156
166
|
|
|
157
167
|
// Pass an object of relevant component state as first argument for any passed-in press handlers
|
|
@@ -171,6 +181,8 @@ const ButtonGroup = React.forwardRef(
|
|
|
171
181
|
...a11yProps.getPositionInSet(items.length, index)
|
|
172
182
|
}
|
|
173
183
|
|
|
184
|
+
const isInactive = itemInactive !== undefined ? itemInactive : inactive
|
|
185
|
+
|
|
174
186
|
// Ensure button is direct child of group as MacOS voiceover only applies "X of Y" to
|
|
175
187
|
// "radio" if it's a direct child of "radiogroup", even if aria-posinset etc exists
|
|
176
188
|
return (
|
|
@@ -182,7 +194,7 @@ const ButtonGroup = React.forwardRef(
|
|
|
182
194
|
onPress={handlePress}
|
|
183
195
|
tokens={getButtonTokens}
|
|
184
196
|
selected={isSelected}
|
|
185
|
-
inactive={
|
|
197
|
+
inactive={isInactive}
|
|
186
198
|
icon={iconProp}
|
|
187
199
|
{...selectItemProps({
|
|
188
200
|
...itemRest,
|
|
@@ -243,7 +255,12 @@ ButtonGroup.propTypes = {
|
|
|
243
255
|
/**
|
|
244
256
|
* An optional ref for one individual button in the ButtonGroup
|
|
245
257
|
*/
|
|
246
|
-
ref: ABBPropTypes.ref()
|
|
258
|
+
ref: ABBPropTypes.ref(),
|
|
259
|
+
/**
|
|
260
|
+
* If true, this individual button cannot be interacted with. Takes precedence
|
|
261
|
+
* over the group-level `inactive` prop. Useful for disabling specific options.
|
|
262
|
+
*/
|
|
263
|
+
inactive: PropTypes.bool
|
|
247
264
|
})
|
|
248
265
|
),
|
|
249
266
|
/**
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Animated,
|
|
5
|
+
PanResponder,
|
|
6
|
+
StyleSheet,
|
|
7
|
+
Platform,
|
|
8
|
+
Dimensions,
|
|
9
|
+
Easing
|
|
10
|
+
} from 'react-native'
|
|
3
11
|
import PropTypes from 'prop-types'
|
|
4
|
-
import { useThemeTokens } from '../ThemeProvider'
|
|
12
|
+
import { useThemeTokens, useTheme } from '../ThemeProvider'
|
|
5
13
|
import { useViewport } from '../ViewportProvider'
|
|
6
14
|
import {
|
|
7
15
|
getTokensPropType,
|
|
@@ -13,7 +21,9 @@ import {
|
|
|
13
21
|
viewProps,
|
|
14
22
|
useCopy,
|
|
15
23
|
unpackFragment,
|
|
16
|
-
isTouchDevice
|
|
24
|
+
isTouchDevice,
|
|
25
|
+
useResponsiveProp,
|
|
26
|
+
resolveContentMaxWidth
|
|
17
27
|
} from '../utils'
|
|
18
28
|
import { useA11yInfo } from '../A11yInfoProvider'
|
|
19
29
|
import { CarouselProvider } from './CarouselContext'
|
|
@@ -36,15 +46,13 @@ import {
|
|
|
36
46
|
DEFAULT_VIEWPORT_MARGIN,
|
|
37
47
|
PEEKING_MULTIPLIER,
|
|
38
48
|
ACTIVE_INDEX_OFFSET_MULTIPLIER,
|
|
39
|
-
NEGATIVE_MULTIPLIER
|
|
49
|
+
NEGATIVE_MULTIPLIER,
|
|
50
|
+
TRANSITION_MODES,
|
|
51
|
+
SWIPE_RELEASE_STYLES,
|
|
52
|
+
INSTANT_ANIMATION_DURATION,
|
|
53
|
+
DEFAULT_SWIPE_RELEASE_DURATION
|
|
40
54
|
} from './Constants'
|
|
41
55
|
|
|
42
|
-
const TRANSITION_MODES = {
|
|
43
|
-
MANUAL: 'manual',
|
|
44
|
-
AUTOMATIC: 'automatic',
|
|
45
|
-
SWIPE: 'swipe'
|
|
46
|
-
}
|
|
47
|
-
|
|
48
56
|
const staticStyles = StyleSheet.create({
|
|
49
57
|
root: {
|
|
50
58
|
backgroundColor: 'transparent',
|
|
@@ -252,11 +260,18 @@ const selectRootContainerStyles = (enableHero, viewport) => {
|
|
|
252
260
|
return {}
|
|
253
261
|
}
|
|
254
262
|
|
|
255
|
-
const selectMainContainerStyles = (enableHero, viewport) => {
|
|
263
|
+
const selectMainContainerStyles = (enableHero, viewport, maxWidth) => {
|
|
256
264
|
if (enableHero && viewport === 'xl' && Platform.OS === 'web') {
|
|
257
265
|
return {
|
|
258
266
|
width: '100%',
|
|
259
|
-
maxWidth: 1200
|
|
267
|
+
maxWidth: maxWidth || 1200
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (maxWidth !== null && maxWidth !== undefined) {
|
|
271
|
+
return {
|
|
272
|
+
maxWidth,
|
|
273
|
+
alignSelf: 'center',
|
|
274
|
+
width: '100%'
|
|
260
275
|
}
|
|
261
276
|
}
|
|
262
277
|
return {}
|
|
@@ -402,6 +417,9 @@ const Carousel = React.forwardRef(
|
|
|
402
417
|
loopDuration = transitionDuration,
|
|
403
418
|
autoPlay = false,
|
|
404
419
|
enablePeeking = false,
|
|
420
|
+
contentMaxWidth,
|
|
421
|
+
swipeReleaseStyle = SWIPE_RELEASE_STYLES.INSTANT,
|
|
422
|
+
swipeReleaseDuration = DEFAULT_SWIPE_RELEASE_DURATION,
|
|
405
423
|
...rest
|
|
406
424
|
},
|
|
407
425
|
ref
|
|
@@ -409,6 +427,11 @@ const Carousel = React.forwardRef(
|
|
|
409
427
|
let childrenArray = unpackFragment(children)
|
|
410
428
|
const isTransitioningRef = React.useRef(false)
|
|
411
429
|
const viewport = useViewport()
|
|
430
|
+
const { themeOptions } = useTheme()
|
|
431
|
+
|
|
432
|
+
const contentMaxWidthValue = useResponsiveProp(contentMaxWidth)
|
|
433
|
+
const responsiveWidth = useResponsiveProp(themeOptions?.contentMaxWidth)
|
|
434
|
+
const maxWidth = resolveContentMaxWidth(contentMaxWidthValue, responsiveWidth)
|
|
412
435
|
const totalItems = getTotalItems(enableDisplayMultipleItemsPerSlide, childrenArray, viewport)
|
|
413
436
|
const autoPlayFeatureEnabled =
|
|
414
437
|
autoPlay && slideDuration > 0 && transitionDuration > 0 && totalItems > 1
|
|
@@ -538,14 +561,29 @@ const Carousel = React.forwardRef(
|
|
|
538
561
|
}, [pan, animatedX, heroPan, heroAnimatedX, enableHero, viewport, enablePeeking])
|
|
539
562
|
|
|
540
563
|
const animate = React.useCallback(
|
|
541
|
-
(panToAnimate, toValue, toIndex) => {
|
|
564
|
+
(panToAnimate, toValue, toIndex, isSwipeRelease = false) => {
|
|
542
565
|
const applicableTransitionDuration =
|
|
543
566
|
isLastSlide && toIndex === 0 ? loopDuration : transitionDuration
|
|
544
567
|
const handleAnimationEndToIndex = (...args) => handleAnimationEnd(toIndex, ...args)
|
|
545
|
-
if (reduceMotionRef.current
|
|
546
|
-
Animated.timing(panToAnimate, {
|
|
547
|
-
|
|
548
|
-
|
|
568
|
+
if (reduceMotionRef.current) {
|
|
569
|
+
Animated.timing(panToAnimate, {
|
|
570
|
+
toValue,
|
|
571
|
+
duration: INSTANT_ANIMATION_DURATION,
|
|
572
|
+
useNativeDriver: false
|
|
573
|
+
}).start(handleAnimationEndToIndex)
|
|
574
|
+
} else if (isSwipeRelease && swipeReleaseStyle === SWIPE_RELEASE_STYLES.EASE_OUT) {
|
|
575
|
+
Animated.timing(panToAnimate, {
|
|
576
|
+
toValue,
|
|
577
|
+
duration: swipeReleaseDuration,
|
|
578
|
+
easing: Easing.out(Easing.cubic),
|
|
579
|
+
useNativeDriver: false
|
|
580
|
+
}).start(handleAnimationEndToIndex)
|
|
581
|
+
} else if (isSwiping.current || isSwipeRelease) {
|
|
582
|
+
Animated.timing(panToAnimate, {
|
|
583
|
+
toValue,
|
|
584
|
+
duration: INSTANT_ANIMATION_DURATION,
|
|
585
|
+
useNativeDriver: false
|
|
586
|
+
}).start(handleAnimationEndToIndex)
|
|
549
587
|
} else if (isAutoPlayEnabled) {
|
|
550
588
|
Animated.timing(panToAnimate, {
|
|
551
589
|
...springConfig,
|
|
@@ -576,7 +614,9 @@ const Carousel = React.forwardRef(
|
|
|
576
614
|
isLastSlide,
|
|
577
615
|
isAutoPlayEnabled,
|
|
578
616
|
enablePeeking,
|
|
579
|
-
enableDisplayMultipleItemsPerSlide
|
|
617
|
+
enableDisplayMultipleItemsPerSlide,
|
|
618
|
+
swipeReleaseStyle,
|
|
619
|
+
swipeReleaseDuration
|
|
580
620
|
]
|
|
581
621
|
)
|
|
582
622
|
|
|
@@ -597,6 +637,7 @@ const Carousel = React.forwardRef(
|
|
|
597
637
|
const toValue = { x: 0, y: 0 }
|
|
598
638
|
let skipChanges = !delta
|
|
599
639
|
let calcDelta = delta
|
|
640
|
+
const isSwipeRelease = transitionMode === TRANSITION_MODES.SWIPE
|
|
600
641
|
if (activeIndexRef.current <= 0 && delta < 0) {
|
|
601
642
|
skipChanges = transitionMode !== TRANSITION_MODES.AUTOMATIC
|
|
602
643
|
calcDelta = totalItems + delta
|
|
@@ -608,10 +649,10 @@ const Carousel = React.forwardRef(
|
|
|
608
649
|
const index = activeIndexRef.current + calcDelta
|
|
609
650
|
if (skipChanges) {
|
|
610
651
|
isTransitioningRef.current = true
|
|
611
|
-
animate(pan, toValue, index)
|
|
652
|
+
animate(pan, toValue, index, isSwipeRelease)
|
|
612
653
|
|
|
613
654
|
if (enableHero) {
|
|
614
|
-
animate(heroPan, toValue, index)
|
|
655
|
+
animate(heroPan, toValue, index, isSwipeRelease)
|
|
615
656
|
}
|
|
616
657
|
return calcDelta
|
|
617
658
|
}
|
|
@@ -630,9 +671,9 @@ const Carousel = React.forwardRef(
|
|
|
630
671
|
const heroToValue = { x: 0, y: 0 }
|
|
631
672
|
heroToValue.x = heroContainerLayoutRef.current.width * -1 * calcDelta
|
|
632
673
|
isTransitioningRef.current = true
|
|
633
|
-
animate(pan, toValue, index)
|
|
674
|
+
animate(pan, toValue, index, isSwipeRelease)
|
|
634
675
|
if (enableHero) {
|
|
635
|
-
animate(heroPan, heroToValue, index)
|
|
676
|
+
animate(heroPan, heroToValue, index, isSwipeRelease)
|
|
636
677
|
}
|
|
637
678
|
if (isCarouselPlaying) {
|
|
638
679
|
stopAutoplay()
|
|
@@ -851,14 +892,14 @@ const Carousel = React.forwardRef(
|
|
|
851
892
|
}
|
|
852
893
|
const correction = gesture.moveX - gesture.x0
|
|
853
894
|
|
|
895
|
+
isSwiping.current = false
|
|
896
|
+
|
|
854
897
|
if (Math.abs(correction) < containerLayoutRef.current.width * minDistanceForAction) {
|
|
855
|
-
animate(pan, { x: 0, y: 0 },
|
|
898
|
+
animate(pan, { x: 0, y: 0 }, activeIndexRef.current, true)
|
|
856
899
|
} else {
|
|
857
900
|
const delta = correction > 0 ? -1 : 1
|
|
858
901
|
updateIndex(delta, TRANSITION_MODES.SWIPE)
|
|
859
902
|
}
|
|
860
|
-
|
|
861
|
-
isSwiping.current = false
|
|
862
903
|
}
|
|
863
904
|
}),
|
|
864
905
|
[
|
|
@@ -909,14 +950,14 @@ const Carousel = React.forwardRef(
|
|
|
909
950
|
}
|
|
910
951
|
const correction = gesture.moveX - gesture.x0
|
|
911
952
|
|
|
953
|
+
isSwiping.current = false
|
|
954
|
+
|
|
912
955
|
if (Math.abs(correction) < containerLayoutRef.current.width * minDistanceForAction) {
|
|
913
|
-
animate(heroPan, { x: 0, y: 0 },
|
|
956
|
+
animate(heroPan, { x: 0, y: 0 }, activeIndexRef.current, true)
|
|
914
957
|
} else {
|
|
915
958
|
const delta = correction > 0 ? -1 : 1
|
|
916
959
|
updateIndex(delta, TRANSITION_MODES.SWIPE)
|
|
917
960
|
}
|
|
918
|
-
|
|
919
|
-
isSwiping.current = false
|
|
920
961
|
}
|
|
921
962
|
}),
|
|
922
963
|
[
|
|
@@ -1031,7 +1072,7 @@ const Carousel = React.forwardRef(
|
|
|
1031
1072
|
|
|
1032
1073
|
return (
|
|
1033
1074
|
<View style={selectRootContainerStyles(enableHero, viewport)}>
|
|
1034
|
-
<View style={selectMainContainerStyles(enableHero, viewport)}>
|
|
1075
|
+
<View style={selectMainContainerStyles(enableHero, viewport, maxWidth)}>
|
|
1035
1076
|
<CarouselProvider
|
|
1036
1077
|
activeIndex={activeIndex}
|
|
1037
1078
|
goTo={goTo}
|
|
@@ -1463,7 +1504,40 @@ Carousel.propTypes = {
|
|
|
1463
1504
|
* If set to `true`, the Carousel will show multiple slides at once
|
|
1464
1505
|
* - Default value is `false`
|
|
1465
1506
|
*/
|
|
1466
|
-
enableDisplayMultipleItemsPerSlide: PropTypes.bool
|
|
1507
|
+
enableDisplayMultipleItemsPerSlide: PropTypes.bool,
|
|
1508
|
+
/**
|
|
1509
|
+
* The maximum width of the content in the Carousel.
|
|
1510
|
+
* This prop accepts responsive values for different viewports. If a number is provided,
|
|
1511
|
+
* it will be the max content width for the desired viewport.
|
|
1512
|
+
* - `xs`: 'max' | 'full' | <number>
|
|
1513
|
+
* - `sm`: 'max' | 'full' | <number>
|
|
1514
|
+
* - `md`: 'max' | 'full' | <number>
|
|
1515
|
+
* - `lg`: 'max' | 'full' | <number>
|
|
1516
|
+
* - `xl`: 'max' | 'full' | <number>
|
|
1517
|
+
*/
|
|
1518
|
+
contentMaxWidth: PropTypes.shape({
|
|
1519
|
+
xl: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
1520
|
+
lg: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
1521
|
+
md: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
1522
|
+
sm: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
1523
|
+
xs: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number])
|
|
1524
|
+
}),
|
|
1525
|
+
/**
|
|
1526
|
+
* Animation style for swipe release transitions.
|
|
1527
|
+
* - `'instant'`: Immediate snap to position (current default)
|
|
1528
|
+
* - `'ease-out'`: Smooth deceleration animation
|
|
1529
|
+
* - Default value is `'instant'`
|
|
1530
|
+
* - Use `swipeReleaseDuration` to customize the animation duration when using `'ease-out'`
|
|
1531
|
+
*
|
|
1532
|
+
* @deprecated The default will change to `'ease-out'` in Q2 2026 (introduced Jan 2026).
|
|
1533
|
+
*/
|
|
1534
|
+
swipeReleaseStyle: PropTypes.oneOf(['instant', 'ease-out']),
|
|
1535
|
+
/**
|
|
1536
|
+
* Duration in milliseconds of the ease-out animation when releasing a swipe gesture.
|
|
1537
|
+
* Only applies when `swipeReleaseStyle` is set to `'ease-out'`.
|
|
1538
|
+
* - Default value is `500` (500ms)
|
|
1539
|
+
*/
|
|
1540
|
+
swipeReleaseDuration: PropTypes.number
|
|
1467
1541
|
}
|
|
1468
1542
|
|
|
1469
1543
|
Carousel.Item = CarouselItem
|
|
@@ -9,3 +9,17 @@ export const DEFAULT_VIEWPORT_MARGIN = 10
|
|
|
9
9
|
export const PEEKING_MULTIPLIER = 2
|
|
10
10
|
export const ACTIVE_INDEX_OFFSET_MULTIPLIER = 1
|
|
11
11
|
export const NEGATIVE_MULTIPLIER = -1
|
|
12
|
+
|
|
13
|
+
export const TRANSITION_MODES = {
|
|
14
|
+
MANUAL: 'manual',
|
|
15
|
+
AUTOMATIC: 'automatic',
|
|
16
|
+
SWIPE: 'swipe'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const SWIPE_RELEASE_STYLES = {
|
|
20
|
+
INSTANT: 'instant',
|
|
21
|
+
EASE_OUT: 'ease-out'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const INSTANT_ANIMATION_DURATION = 1
|
|
25
|
+
export const DEFAULT_SWIPE_RELEASE_DURATION = 500
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
createMediaQueryStyles,
|
|
14
14
|
useResponsiveProp
|
|
15
15
|
} from '../utils'
|
|
16
|
+
import resolveContentMaxWidth from '../utils/resolveContentMaxWidth'
|
|
16
17
|
import Row from './Row'
|
|
17
18
|
import Col from './Col'
|
|
18
19
|
import GutterContext from './providers/GutterContext'
|
|
@@ -22,43 +23,16 @@ import { useViewport } from '../ViewportProvider'
|
|
|
22
23
|
|
|
23
24
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
24
25
|
|
|
25
|
-
const CONTENT_MAX_WIDTH = 'max'
|
|
26
|
-
const CONTENT_FULL_WIDTH = 'full'
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Resolves the maximum width for content based on the provided value and responsive width.
|
|
30
|
-
*
|
|
31
|
-
* @param {number|string|null|undefined} contentMinWidthValue - The minimum width value for the content.
|
|
32
|
-
* Can be a number, a special string constant (e.g., CONTENT_FULL_WIDTH, CONTENT_MAX_WIDTH), or null/undefined.
|
|
33
|
-
* @param {number} responsiveWidth - The responsive width to use when contentMinWidthValue is CONTENT_MAX_WIDTH.
|
|
34
|
-
* @returns {number|string|null} The resolved maximum width value, or null if full width is desired.
|
|
35
|
-
*/
|
|
36
|
-
const resolveContentMaxWidth = (contentMinWidthValue, responsiveWidth) => {
|
|
37
|
-
if (!contentMinWidthValue || contentMinWidthValue === CONTENT_FULL_WIDTH) {
|
|
38
|
-
return null
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (Number.isFinite(contentMinWidthValue)) {
|
|
42
|
-
return contentMinWidthValue
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (contentMinWidthValue === CONTENT_MAX_WIDTH) {
|
|
46
|
-
return responsiveWidth
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return contentMinWidthValue
|
|
50
|
-
}
|
|
51
|
-
|
|
52
26
|
/**
|
|
53
|
-
* Calculates the maximum width for a given viewport based on limitWidth and
|
|
27
|
+
* Calculates the maximum width for a given viewport based on limitWidth and contentMaxWidth settings.
|
|
54
28
|
*
|
|
55
29
|
* @param {string} viewportKey - The viewport key ('xs', 'sm', 'md', 'lg', 'xl')
|
|
56
30
|
* @param {boolean} limitWidth - Whether to limit the width to viewport breakpoints
|
|
57
|
-
* @param {any}
|
|
31
|
+
* @param {any} contentWidthProp - The contentMaxWidth (or contentMinWidth) prop value
|
|
58
32
|
* @param {number|string|null} maxWidth - The resolved max width value
|
|
59
33
|
* @returns {number|string|null} The calculated maximum width for the viewport
|
|
60
34
|
*/
|
|
61
|
-
const getMaxWidthForViewport = (viewportKey, limitWidth,
|
|
35
|
+
const getMaxWidthForViewport = (viewportKey, limitWidth, contentWidthProp, maxWidth) => {
|
|
62
36
|
if (limitWidth) {
|
|
63
37
|
if (viewportKey === 'xs') {
|
|
64
38
|
return null
|
|
@@ -66,7 +40,7 @@ const getMaxWidthForViewport = (viewportKey, limitWidth, contentMinWidth, maxWid
|
|
|
66
40
|
return viewports.map.get(viewportKey)
|
|
67
41
|
}
|
|
68
42
|
|
|
69
|
-
if (
|
|
43
|
+
if (contentWidthProp) {
|
|
70
44
|
return maxWidth
|
|
71
45
|
}
|
|
72
46
|
|
|
@@ -92,6 +66,7 @@ const FlexGrid = React.forwardRef(
|
|
|
92
66
|
accessibilityRole,
|
|
93
67
|
children,
|
|
94
68
|
dataSet,
|
|
69
|
+
contentMaxWidth,
|
|
95
70
|
contentMinWidth,
|
|
96
71
|
...rest
|
|
97
72
|
},
|
|
@@ -107,29 +82,31 @@ const FlexGrid = React.forwardRef(
|
|
|
107
82
|
let flexgridStyles
|
|
108
83
|
let mediaIds
|
|
109
84
|
|
|
110
|
-
|
|
85
|
+
// Support both contentMaxWidth and deprecated contentMinWidth for backwards compatibility
|
|
86
|
+
const contentWidthProp = contentMaxWidth || contentMinWidth
|
|
87
|
+
const contentWidthValue = useResponsiveProp(contentWidthProp)
|
|
111
88
|
const responsiveWidth = useResponsiveProp(themeOptions?.contentMaxWidth)
|
|
112
|
-
const maxWidth = resolveContentMaxWidth(
|
|
89
|
+
const maxWidth = resolveContentMaxWidth(contentWidthValue, responsiveWidth)
|
|
113
90
|
|
|
114
91
|
const stylesByViewport = {
|
|
115
92
|
xs: {
|
|
116
|
-
maxWidth: getMaxWidthForViewport('xs', limitWidth,
|
|
93
|
+
maxWidth: getMaxWidthForViewport('xs', limitWidth, contentWidthProp, maxWidth),
|
|
117
94
|
flexDirection: reverseLevel[0] ? 'column-reverse' : 'column'
|
|
118
95
|
},
|
|
119
96
|
sm: {
|
|
120
|
-
maxWidth: getMaxWidthForViewport('sm', limitWidth,
|
|
97
|
+
maxWidth: getMaxWidthForViewport('sm', limitWidth, contentWidthProp, maxWidth),
|
|
121
98
|
flexDirection: reverseLevel[1] ? 'column-reverse' : 'column'
|
|
122
99
|
},
|
|
123
100
|
md: {
|
|
124
|
-
maxWidth: getMaxWidthForViewport('md', limitWidth,
|
|
101
|
+
maxWidth: getMaxWidthForViewport('md', limitWidth, contentWidthProp, maxWidth),
|
|
125
102
|
flexDirection: reverseLevel[2] ? 'column-reverse' : 'column'
|
|
126
103
|
},
|
|
127
104
|
lg: {
|
|
128
|
-
maxWidth: getMaxWidthForViewport('lg', limitWidth,
|
|
105
|
+
maxWidth: getMaxWidthForViewport('lg', limitWidth, contentWidthProp, maxWidth),
|
|
129
106
|
flexDirection: reverseLevel[3] ? 'column-reverse' : 'column'
|
|
130
107
|
},
|
|
131
108
|
xl: {
|
|
132
|
-
maxWidth: getMaxWidthForViewport('xl', limitWidth,
|
|
109
|
+
maxWidth: getMaxWidthForViewport('xl', limitWidth, contentWidthProp, maxWidth),
|
|
133
110
|
flexDirection: reverseLevel[4] ? 'column-reverse' : 'column'
|
|
134
111
|
}
|
|
135
112
|
}
|
|
@@ -223,7 +200,7 @@ FlexGrid.propTypes = {
|
|
|
223
200
|
*/
|
|
224
201
|
children: PropTypes.node.isRequired,
|
|
225
202
|
/**
|
|
226
|
-
* The
|
|
203
|
+
* The maximum width of the content in the FlexGrid.
|
|
227
204
|
* This prop accepts responsive values for different viewports. If a number is provided,
|
|
228
205
|
* it will be the max content width for the desired viewport.
|
|
229
206
|
* - `xs`: 'max' | 'full' | <number>
|
|
@@ -232,6 +209,20 @@ FlexGrid.propTypes = {
|
|
|
232
209
|
* - `lg`: 'max' | 'full' | <number>
|
|
233
210
|
* - `xl`: 'max' | 'full' | <number>
|
|
234
211
|
*/
|
|
212
|
+
contentMaxWidth: PropTypes.shape({
|
|
213
|
+
xl: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
214
|
+
lg: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
215
|
+
md: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
216
|
+
sm: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
217
|
+
xs: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number])
|
|
218
|
+
}),
|
|
219
|
+
/**
|
|
220
|
+
* @deprecated Use `contentMaxWidth` instead. This prop will be removed in a future version.
|
|
221
|
+
*
|
|
222
|
+
* The minimum width of the content in the FlexGrid.
|
|
223
|
+
* This prop accepts responsive values for different viewports. If a number is provided,
|
|
224
|
+
* it will be the max content width for the desired viewport.
|
|
225
|
+
*/
|
|
235
226
|
contentMinWidth: PropTypes.shape({
|
|
236
227
|
xl: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
237
228
|
lg: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
@@ -136,6 +136,7 @@ const IconButton = React.forwardRef(
|
|
|
136
136
|
hrefAttrs,
|
|
137
137
|
testID,
|
|
138
138
|
accessibilityRole = href ? 'link' : 'button',
|
|
139
|
+
inactive = false,
|
|
139
140
|
...rawRest
|
|
140
141
|
},
|
|
141
142
|
ref
|
|
@@ -149,9 +150,10 @@ const IconButton = React.forwardRef(
|
|
|
149
150
|
linkProps.handleHref({ href, onPress })({ nativeEvent: { target: ref?.current?.id } })
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
const
|
|
153
|
+
const mergedVariant = inactive ? { ...variant, inactive: true } : variant
|
|
154
|
+
const getTokens = useThemeTokensCallback('IconButton', tokens, mergedVariant)
|
|
153
155
|
const getOuterStyle = (pressableState) =>
|
|
154
|
-
selectOuterStyle(getTokens(resolvePressableState(pressableState),
|
|
156
|
+
selectOuterStyle(getTokens(resolvePressableState(pressableState), mergedVariant.password))
|
|
155
157
|
|
|
156
158
|
return (
|
|
157
159
|
<Pressable
|
|
@@ -162,16 +164,17 @@ const IconButton = React.forwardRef(
|
|
|
162
164
|
style={getOuterStyle}
|
|
163
165
|
{...selectedProps}
|
|
164
166
|
testID={testID}
|
|
167
|
+
disabled={inactive}
|
|
165
168
|
>
|
|
166
169
|
{(pressableState) => {
|
|
167
170
|
const themeTokens = getTokens(resolvePressableState(pressableState))
|
|
168
171
|
return (
|
|
169
|
-
<View style={selectInnerStyle(themeTokens,
|
|
172
|
+
<View style={selectInnerStyle(themeTokens, mergedVariant.password)}>
|
|
170
173
|
<Icon
|
|
171
174
|
icon={IconComponent || themeTokens.icon}
|
|
172
175
|
title={selectedProps.accessibilityLabel}
|
|
173
176
|
tokens={selectTokens('Icon', themeTokens, 'icon')}
|
|
174
|
-
variant={
|
|
177
|
+
variant={mergedVariant}
|
|
175
178
|
/>
|
|
176
179
|
</View>
|
|
177
180
|
)
|
|
@@ -206,7 +209,11 @@ IconButton.propTypes = {
|
|
|
206
209
|
/**
|
|
207
210
|
* Function to execute when the `Iconbutton` is pressed
|
|
208
211
|
*/
|
|
209
|
-
onPress: PropTypes.func
|
|
212
|
+
onPress: PropTypes.func,
|
|
213
|
+
/**
|
|
214
|
+
* When true, applies the inactive variant styling
|
|
215
|
+
*/
|
|
216
|
+
inactive: PropTypes.bool
|
|
210
217
|
}
|
|
211
218
|
|
|
212
219
|
const staticStyles = StyleSheet.create({
|
|
@@ -66,7 +66,12 @@ const InputSupports = React.forwardRef(
|
|
|
66
66
|
/>
|
|
67
67
|
)}
|
|
68
68
|
{typeof children === 'function'
|
|
69
|
-
? children({
|
|
69
|
+
? children({
|
|
70
|
+
inputId,
|
|
71
|
+
...a11yProps,
|
|
72
|
+
validation: feedbackValidation,
|
|
73
|
+
accessibilityDescribedBy: feedbackId
|
|
74
|
+
})
|
|
70
75
|
: children}
|
|
71
76
|
{feedback || maxCharsReachedErrorMessage ? (
|
|
72
77
|
<Feedback
|
|
@@ -121,7 +121,7 @@ const PriceLockup = React.forwardRef(
|
|
|
121
121
|
>
|
|
122
122
|
{topText ? (
|
|
123
123
|
<View style={staticStyles.topText}>
|
|
124
|
-
{renderTypography(topText, topTextTypographyTokens)}
|
|
124
|
+
{renderTypography(topText, topTextTypographyTokens, undefined, fontColor)}
|
|
125
125
|
</View>
|
|
126
126
|
) : null}
|
|
127
127
|
{renderPrice(
|
|
@@ -313,7 +313,7 @@ const TextInputBase = React.forwardRef(
|
|
|
313
313
|
icon={ClearButtonIcon}
|
|
314
314
|
key="clear"
|
|
315
315
|
onPress={handleClear}
|
|
316
|
-
variant={{
|
|
316
|
+
variant={{ subtle: true }}
|
|
317
317
|
/>
|
|
318
318
|
)
|
|
319
319
|
}
|
|
@@ -329,7 +329,7 @@ const TextInputBase = React.forwardRef(
|
|
|
329
329
|
icon={!showPassword ? passwordShowButtonIcon : passwordHideButtonIcon}
|
|
330
330
|
key={!showPassword ? 'hide' : 'show'}
|
|
331
331
|
onPress={handleShowOrHide}
|
|
332
|
-
variant={{
|
|
332
|
+
variant={{ subtle: true, inactive: variant.inactive, size: 'large' }}
|
|
333
333
|
tokens={{ width: 40, height: 40 }}
|
|
334
334
|
/>
|
|
335
335
|
)
|
package/src/utils/index.js
CHANGED
|
@@ -27,3 +27,4 @@ export { default as formatImageSource } from './formatImageSource'
|
|
|
27
27
|
export { default as getSpacingScale } from './getSpacingScale'
|
|
28
28
|
export { default as useVariants } from './useVariants'
|
|
29
29
|
export { default as isTouchDevice } from './isTouchDevice'
|
|
30
|
+
export { default as resolveContentMaxWidth } from './resolveContentMaxWidth'
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const CONTENT_MAX_WIDTH = 'max'
|
|
2
|
+
const CONTENT_FULL_WIDTH = 'full'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the maximum width for content based on the provided value and responsive width.
|
|
6
|
+
*
|
|
7
|
+
* @param {number|string|null|undefined} contentMaxWidthValue - The maximum width value for the content.
|
|
8
|
+
* Can be a number, a special string constant (e.g., CONTENT_FULL_WIDTH, CONTENT_MAX_WIDTH), or null/undefined.
|
|
9
|
+
* @param {number} responsiveWidth - The responsive width to use when contentMaxWidthValue is CONTENT_MAX_WIDTH.
|
|
10
|
+
* @returns {number|string|null} The resolved maximum width value, or null if full width is desired.
|
|
11
|
+
*/
|
|
12
|
+
const resolveContentMaxWidth = (contentMaxWidthValue, responsiveWidth) => {
|
|
13
|
+
if (!contentMaxWidthValue || contentMaxWidthValue === CONTENT_FULL_WIDTH) {
|
|
14
|
+
return null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (Number.isFinite(contentMaxWidthValue)) {
|
|
18
|
+
return contentMaxWidthValue
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (contentMaxWidthValue === CONTENT_MAX_WIDTH) {
|
|
22
|
+
return responsiveWidth
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return contentMaxWidthValue
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default resolveContentMaxWidth
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Variant } from '../../components-web/types/common'
|
|
2
|
+
import { ComponentType, ReactNode } from 'react'
|
|
3
|
+
|
|
4
|
+
import { IconProps } from './Icon'
|
|
5
|
+
|
|
6
|
+
export type StatusTokensProps = {
|
|
7
|
+
backgroundColor: string
|
|
8
|
+
backgroundGradient: string
|
|
9
|
+
borderColor: string
|
|
10
|
+
borderRadius: number | string
|
|
11
|
+
borderWidth: number | string
|
|
12
|
+
fontName: string
|
|
13
|
+
fontSize: number | string
|
|
14
|
+
fontWeight: number | string
|
|
15
|
+
icon: IconProps
|
|
16
|
+
iconColor: string
|
|
17
|
+
iconGradient: string
|
|
18
|
+
marginLeft: string | number
|
|
19
|
+
paddingBottom: string | number
|
|
20
|
+
paddingLeft: string | number
|
|
21
|
+
paddingRight: string | number
|
|
22
|
+
paddingTop: string | number
|
|
23
|
+
textColor: string
|
|
24
|
+
textLineHeight: string | number
|
|
25
|
+
iconSize: number | string
|
|
26
|
+
iconPaddingTop: number | string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface StatusProps {
|
|
30
|
+
children: ReactNode
|
|
31
|
+
variant?: Variant
|
|
32
|
+
tokens?: Partial<StatusTokensProps>
|
|
33
|
+
customGradient?: (
|
|
34
|
+
gradient: { colors: string[]; start: { x: number; y: number }; end: { x: number; y: number } },
|
|
35
|
+
styles: Record<string, unknown>,
|
|
36
|
+
content: ReactNode
|
|
37
|
+
) => ReactNode
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
declare const Status: ComponentType<StatusProps>
|
|
41
|
+
|
|
42
|
+
export default Status
|
package/types/index.d.ts
CHANGED
|
@@ -90,4 +90,7 @@ export { PortalProps } from './Portal'
|
|
|
90
90
|
export { default as Listbox } from './Listbox'
|
|
91
91
|
export { ListboxProps, ListboxTokens } from './Listbox'
|
|
92
92
|
|
|
93
|
+
export { default as Status } from './Status'
|
|
94
|
+
export { StatusProps, StatusTokensProps } from './Status'
|
|
95
|
+
|
|
93
96
|
export * from './Common'
|