@telus-uds/components-base 3.25.0 → 3.27.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 (48) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/lib/cjs/Card/Card.js +34 -13
  3. package/lib/cjs/Card/CardBase.js +78 -11
  4. package/lib/cjs/Card/PressableCardBase.js +147 -8
  5. package/lib/cjs/Carousel/Carousel.js +161 -77
  6. package/lib/cjs/Carousel/CarouselContext.js +10 -4
  7. package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +11 -7
  8. package/lib/cjs/Carousel/Constants.js +22 -2
  9. package/lib/cjs/Checkbox/Checkbox.js +43 -13
  10. package/lib/cjs/InputSupports/InputSupports.js +2 -1
  11. package/lib/cjs/List/List.js +24 -9
  12. package/lib/cjs/List/ListItem.js +18 -1
  13. package/lib/cjs/List/ListItemBase.js +27 -8
  14. package/lib/cjs/List/ListItemMark.js +33 -62
  15. package/lib/cjs/List/PressableListItemBase.js +1 -0
  16. package/lib/cjs/PriceLockup/PriceLockup.js +1 -1
  17. package/lib/esm/Card/Card.js +34 -13
  18. package/lib/esm/Card/CardBase.js +78 -11
  19. package/lib/esm/Card/PressableCardBase.js +148 -9
  20. package/lib/esm/Carousel/Carousel.js +153 -69
  21. package/lib/esm/Carousel/CarouselContext.js +10 -4
  22. package/lib/esm/Carousel/CarouselItem/CarouselItem.js +11 -7
  23. package/lib/esm/Carousel/Constants.js +21 -1
  24. package/lib/esm/Checkbox/Checkbox.js +43 -13
  25. package/lib/esm/InputSupports/InputSupports.js +2 -1
  26. package/lib/esm/List/List.js +24 -9
  27. package/lib/esm/List/ListItem.js +19 -2
  28. package/lib/esm/List/ListItemBase.js +27 -8
  29. package/lib/esm/List/ListItemMark.js +33 -62
  30. package/lib/esm/List/PressableListItemBase.js +1 -0
  31. package/lib/esm/PriceLockup/PriceLockup.js +1 -1
  32. package/lib/package.json +2 -2
  33. package/package.json +2 -2
  34. package/src/Card/Card.jsx +29 -7
  35. package/src/Card/CardBase.jsx +88 -8
  36. package/src/Card/PressableCardBase.jsx +135 -9
  37. package/src/Carousel/Carousel.jsx +185 -88
  38. package/src/Carousel/CarouselContext.jsx +12 -4
  39. package/src/Carousel/CarouselItem/CarouselItem.jsx +10 -6
  40. package/src/Carousel/Constants.js +24 -0
  41. package/src/Checkbox/Checkbox.jsx +29 -7
  42. package/src/InputSupports/InputSupports.jsx +6 -1
  43. package/src/List/List.jsx +33 -9
  44. package/src/List/ListItem.jsx +33 -11
  45. package/src/List/ListItemBase.jsx +33 -9
  46. package/src/List/ListItemMark.jsx +32 -53
  47. package/src/List/PressableListItemBase.jsx +1 -0
  48. package/src/PriceLockup/PriceLockup.jsx +1 -1
@@ -116,6 +116,44 @@ const setBackgroundImage = ({
116
116
  )
117
117
  }
118
118
 
119
+ const selectPaddedContentStyles = ({
120
+ paddingTop,
121
+ paddingBottom,
122
+ paddingLeft,
123
+ paddingRight,
124
+ borderWidth,
125
+ borderColor,
126
+ borderRadius,
127
+ hasInteractiveBorder
128
+ }) => ({
129
+ paddingTop,
130
+ paddingBottom,
131
+ paddingLeft,
132
+ paddingRight,
133
+ ...(hasInteractiveBorder
134
+ ? {
135
+ borderWidth,
136
+ borderColor,
137
+ borderRadius
138
+ }
139
+ : {})
140
+ })
141
+
142
+ const selectInteractiveOverlayStyles = ({ backgroundColor, borderRadius, borderWidth }) => {
143
+ const adjustedBorderRadius = Math.max(0, borderRadius - borderWidth)
144
+ return {
145
+ position: 'absolute',
146
+ top: 0,
147
+ left: 0,
148
+ right: 0,
149
+ bottom: 0,
150
+ backgroundColor,
151
+ borderRadius: adjustedBorderRadius,
152
+ pointerEvents: 'none',
153
+ zIndex: 1
154
+ }
155
+ }
156
+
119
157
  // Ensure explicit selection of tokens
120
158
  export const selectStyles = ({
121
159
  flex,
@@ -214,13 +252,55 @@ const CardBase = React.forwardRef(
214
252
  const fullBleedPosition = useResponsiveProp(fullBleedContentPosition, 'bottom')
215
253
 
216
254
  if (backgroundImage && src) {
217
- const { paddingTop, paddingBottom, paddingLeft, paddingRight, ...containerStyle } = cardStyle
255
+ const {
256
+ paddingTop,
257
+ paddingBottom,
258
+ paddingLeft,
259
+ paddingRight,
260
+ borderWidth,
261
+ borderColor,
262
+ borderRadius,
263
+ backgroundColor,
264
+ ...containerStyle
265
+ } = cardStyle
218
266
 
219
267
  const hasPadding = paddingTop || paddingBottom || paddingLeft || paddingRight
220
- const paddedContent = hasPadding ? (
221
- <View style={{ paddingTop, paddingBottom, paddingLeft, paddingRight }}>{children}</View>
222
- ) : (
223
- children
268
+ const hasInteractiveBorder = borderWidth && borderWidth > 0
269
+ const hasInteractiveOverlay = isOverlayColor(backgroundColor)
270
+
271
+ const paddedContent =
272
+ hasPadding || hasInteractiveBorder ? (
273
+ <View
274
+ style={selectPaddedContentStyles({
275
+ paddingTop,
276
+ paddingBottom,
277
+ paddingLeft,
278
+ paddingRight,
279
+ borderWidth,
280
+ borderColor,
281
+ borderRadius,
282
+ hasInteractiveBorder
283
+ })}
284
+ >
285
+ {children}
286
+ </View>
287
+ ) : (
288
+ children
289
+ )
290
+
291
+ const contentWithOverlay = (
292
+ <>
293
+ {hasInteractiveOverlay && Platform.OS === 'web' && (
294
+ <View
295
+ style={selectInteractiveOverlayStyles({
296
+ backgroundColor,
297
+ borderRadius,
298
+ borderWidth
299
+ })}
300
+ />
301
+ )}
302
+ <View style={staticStyles.contentOverlay}>{paddedContent}</View>
303
+ </>
224
304
  )
225
305
 
226
306
  content = setBackgroundImage({
@@ -229,8 +309,8 @@ const CardBase = React.forwardRef(
229
309
  backgroundImageResizeMode,
230
310
  backgroundImagePosition,
231
311
  backgroundImageAlign,
232
- content: paddedContent,
233
- cardStyle: containerStyle
312
+ content: contentWithOverlay,
313
+ cardStyle: { ...containerStyle, borderRadius }
234
314
  })
235
315
 
236
316
  return (
@@ -314,7 +394,7 @@ const staticStyles = StyleSheet.create({
314
394
  position: 'relative',
315
395
  width: '100%',
316
396
  height: '100%',
317
- zIndex: 1
397
+ zIndex: 2
318
398
  },
319
399
  containContainer: {
320
400
  width: '100%',
@@ -1,7 +1,6 @@
1
1
  import React from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import { Pressable, Platform, View, StyleSheet } from 'react-native'
4
-
5
4
  import { useViewport } from '../ViewportProvider'
6
5
  import { applyOuterBorder, validateThemeTokens } from '../ThemeProvider'
7
6
  import {
@@ -26,6 +25,71 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([
26
25
  viewProps
27
26
  ])
28
27
 
28
+ const selectFocusOverlayContainerStyles = (tokens) => {
29
+ const { flex, minWidth, marginTop, marginBottom, marginLeft, marginRight } = tokens
30
+
31
+ return {
32
+ flex: flex || 1,
33
+ minWidth: minWidth || 0,
34
+ marginTop,
35
+ marginBottom,
36
+ marginLeft,
37
+ marginRight
38
+ }
39
+ }
40
+
41
+ const selectFocusBorderStyles = (tokens) => {
42
+ const { borderWidth, borderColor, borderRadius } = tokens
43
+
44
+ return {
45
+ borderWidth,
46
+ borderColor,
47
+ borderRadius: borderRadius || 0
48
+ }
49
+ }
50
+
51
+ const FocusBorderOverlay = ({ tokens, pressableState, children }) => {
52
+ const { borderWidth = 0 } = tokens
53
+ const showFocusBorder = pressableState.focused && borderWidth > 0
54
+
55
+ return (
56
+ <View
57
+ style={[
58
+ staticStyles.focusOverlayContainer,
59
+ selectFocusOverlayContainerStyles(tokens),
60
+ Platform.OS === 'web' && staticStyles.webOutlineNone
61
+ ]}
62
+ >
63
+ {children}
64
+ {showFocusBorder && (
65
+ <View
66
+ style={[staticStyles.focusBorder, selectFocusBorderStyles(tokens)]}
67
+ accessible={false}
68
+ importantForAccessibility="no"
69
+ />
70
+ )}
71
+ </View>
72
+ )
73
+ }
74
+
75
+ FocusBorderOverlay.propTypes = {
76
+ tokens: PropTypes.shape({
77
+ flex: PropTypes.number,
78
+ minWidth: PropTypes.number,
79
+ marginTop: PropTypes.number,
80
+ marginBottom: PropTypes.number,
81
+ marginLeft: PropTypes.number,
82
+ marginRight: PropTypes.number,
83
+ borderColor: PropTypes.string,
84
+ borderWidth: PropTypes.number,
85
+ borderRadius: PropTypes.number
86
+ }).isRequired,
87
+ pressableState: PropTypes.shape({
88
+ focused: PropTypes.bool
89
+ }).isRequired,
90
+ children: PropTypes.node
91
+ }
92
+
29
93
  const tokenKeys = [
30
94
  'flex',
31
95
  'backgroundColor',
@@ -103,11 +167,53 @@ const PressableCardBase = React.forwardRef(
103
167
  'PressableCard'
104
168
  )
105
169
 
106
- const getCardTokens = (pressableState) => selectTokens('Card', getTokens(pressableState))
170
+ const getCardTokens = (pressableState) => {
171
+ const allTokens = getTokens(pressableState)
172
+ const cardTokens = selectTokens('Card', allTokens)
173
+
174
+ // Handle focus border transparency to avoid double borders
175
+ if (pressableState.focused && allTokens.borderWidth > 0) {
176
+ const result = {
177
+ ...cardTokens,
178
+ borderColor: 'transparent'
179
+ }
180
+
181
+ // Also handle backgroundImage for interactive states
182
+ if (backgroundImage) {
183
+ const { hovered, pressed, focused } = pressableState || {}
184
+ const isInteractiveState = hovered || pressed || focused
185
+
186
+ if (!isInteractiveState) {
187
+ const { backgroundColor, ...restTokens } = result
188
+ return restTokens
189
+ }
190
+ }
191
+
192
+ return result
193
+ }
194
+
195
+ // Handle backgroundImage when not in focus state
196
+ if (backgroundImage) {
197
+ const { hovered, pressed, focused } = pressableState || {}
198
+ const isInteractiveState = hovered || pressed || focused
199
+
200
+ if (!isInteractiveState) {
201
+ const { backgroundColor, ...restTokens } = cardTokens
202
+ return restTokens
203
+ }
204
+ }
205
+
206
+ return cardTokens
207
+ }
208
+
107
209
  const getOuterBorderStyle = (pressableState) => {
108
210
  const {
109
211
  flex,
110
212
  minWidth,
213
+ marginTop,
214
+ marginBottom,
215
+ marginLeft,
216
+ marginRight,
111
217
  outerBorderColor,
112
218
  outerBorderGap = 0,
113
219
  outerBorderWidth = 0,
@@ -116,6 +222,10 @@ const PressableCardBase = React.forwardRef(
116
222
  return {
117
223
  flex,
118
224
  minWidth: minWidth + outerBorderGap + outerBorderWidth,
225
+ marginTop,
226
+ marginBottom,
227
+ marginLeft,
228
+ marginRight,
119
229
  ...applyOuterBorder({ outerBorderColor, outerBorderGap, outerBorderWidth, borderRadius }),
120
230
  ...Platform.select({ web: { outline: 'none' } })
121
231
  }
@@ -194,13 +304,15 @@ const PressableCardBase = React.forwardRef(
194
304
  {...selectProps({ ...rest, accessibilityRole })}
195
305
  >
196
306
  {(pressableState) => (
197
- <CardBase
198
- tokens={getCardTokens(pressableState)}
199
- backgroundImage={backgroundImage}
200
- fullBleedContent={fullBleedContent}
201
- >
202
- {typeof children === 'function' ? children(getCardState(pressableState)) : children}
203
- </CardBase>
307
+ <FocusBorderOverlay tokens={getTokens(pressableState)} pressableState={pressableState}>
308
+ <CardBase
309
+ tokens={getCardTokens(pressableState)}
310
+ backgroundImage={backgroundImage}
311
+ fullBleedContent={fullBleedContent}
312
+ >
313
+ {typeof children === 'function' ? children(getCardState(pressableState)) : children}
314
+ </CardBase>
315
+ </FocusBorderOverlay>
204
316
  )}
205
317
  </Pressable>
206
318
  )
@@ -218,6 +330,20 @@ const staticStyles = StyleSheet.create({
218
330
  alignItems: 'stretch',
219
331
  justifyContent: 'flex-start',
220
332
  textDecorationLine: 'none'
333
+ },
334
+ focusOverlayContainer: {
335
+ position: 'relative'
336
+ },
337
+ webOutlineNone: {
338
+ outline: 'none'
339
+ },
340
+ focusBorder: {
341
+ position: 'absolute',
342
+ top: 0,
343
+ left: 0,
344
+ right: 0,
345
+ bottom: 0,
346
+ pointerEvents: 'none'
221
347
  }
222
348
  })
223
349