@telus-uds/components-base 3.22.0 → 3.24.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 +29 -1
- package/lib/cjs/Button/Button.js +2 -0
- package/lib/cjs/Button/ButtonBase.js +10 -5
- package/lib/cjs/Button/ButtonDropdown.js +2 -0
- package/lib/cjs/Button/ButtonGroup.js +45 -38
- package/lib/cjs/Button/propTypes.js +6 -0
- package/lib/cjs/Card/CardBase.js +97 -17
- package/lib/cjs/Card/PressableCardBase.js +12 -8
- package/lib/cjs/Carousel/Carousel.js +52 -19
- package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +23 -3
- package/lib/cjs/HorizontalScroll/HorizontalScroll.js +5 -2
- package/lib/cjs/Icon/Icon.js +11 -11
- package/lib/cjs/Icon/IconText.js +0 -1
- package/lib/cjs/Listbox/GroupControl.js +44 -44
- package/lib/cjs/Listbox/Listbox.js +63 -20
- package/lib/cjs/Listbox/ListboxGroup.js +141 -9
- package/lib/cjs/Listbox/ListboxOverlay.js +13 -5
- package/lib/cjs/Listbox/PressableItem.js +8 -4
- package/lib/cjs/Listbox/SecondLevelHeader.js +201 -0
- package/lib/cjs/Listbox/dictionary.js +14 -0
- package/lib/cjs/Shortcuts/Shortcuts.js +169 -0
- package/lib/cjs/Shortcuts/ShortcutsItem.js +280 -0
- package/lib/cjs/Shortcuts/index.js +16 -0
- package/lib/cjs/TextInput/TextInputBase.js +5 -1
- package/lib/cjs/Tooltip/Tooltip.native.js +2 -0
- package/lib/cjs/Validator/Validator.js +171 -135
- package/lib/cjs/index.js +15 -0
- package/lib/esm/Button/Button.js +2 -0
- package/lib/esm/Button/ButtonBase.js +10 -5
- package/lib/esm/Button/ButtonDropdown.js +2 -0
- package/lib/esm/Button/ButtonGroup.js +44 -39
- package/lib/esm/Button/propTypes.js +6 -0
- package/lib/esm/Card/CardBase.js +97 -17
- package/lib/esm/Card/PressableCardBase.js +10 -8
- package/lib/esm/Carousel/Carousel.js +52 -19
- package/lib/esm/Carousel/CarouselItem/CarouselItem.js +23 -3
- package/lib/esm/HorizontalScroll/HorizontalScroll.js +6 -3
- package/lib/esm/Icon/Icon.js +11 -11
- package/lib/esm/Icon/IconText.js +0 -1
- package/lib/esm/Listbox/GroupControl.js +44 -44
- package/lib/esm/Listbox/Listbox.js +64 -21
- package/lib/esm/Listbox/ListboxGroup.js +143 -11
- package/lib/esm/Listbox/ListboxOverlay.js +13 -5
- package/lib/esm/Listbox/PressableItem.js +8 -4
- package/lib/esm/Listbox/SecondLevelHeader.js +194 -0
- package/lib/esm/Listbox/dictionary.js +8 -0
- package/lib/esm/Shortcuts/Shortcuts.js +160 -0
- package/lib/esm/Shortcuts/ShortcutsItem.js +273 -0
- package/lib/esm/Shortcuts/index.js +3 -0
- package/lib/esm/TextInput/TextInputBase.js +5 -1
- package/lib/esm/Tooltip/Tooltip.native.js +2 -0
- package/lib/esm/Validator/Validator.js +171 -135
- package/lib/esm/index.js +1 -0
- package/lib/package.json +2 -2
- package/package.json +2 -2
- package/src/Button/Button.jsx +2 -1
- package/src/Button/ButtonBase.jsx +18 -12
- package/src/Button/ButtonDropdown.jsx +2 -0
- package/src/Button/ButtonGroup.jsx +62 -45
- package/src/Button/propTypes.js +6 -0
- package/src/Card/CardBase.jsx +113 -14
- package/src/Card/PressableCardBase.jsx +17 -5
- package/src/Carousel/Carousel.jsx +58 -5
- package/src/Carousel/CarouselItem/CarouselItem.jsx +31 -3
- package/src/HorizontalScroll/HorizontalScroll.jsx +6 -3
- package/src/Icon/Icon.jsx +14 -14
- package/src/Icon/IconText.jsx +0 -1
- package/src/Listbox/GroupControl.jsx +72 -70
- package/src/Listbox/Listbox.jsx +67 -11
- package/src/Listbox/ListboxGroup.jsx +160 -27
- package/src/Listbox/ListboxOverlay.jsx +23 -5
- package/src/Listbox/PressableItem.jsx +8 -4
- package/src/Listbox/SecondLevelHeader.jsx +182 -0
- package/src/Listbox/dictionary.js +8 -0
- package/src/Shortcuts/Shortcuts.jsx +174 -0
- package/src/Shortcuts/ShortcutsItem.jsx +297 -0
- package/src/Shortcuts/index.js +4 -0
- package/src/TextInput/TextInputBase.jsx +5 -1
- package/src/Tooltip/Tooltip.native.jsx +2 -1
- package/src/Validator/Validator.jsx +180 -159
- package/src/index.js +1 -0
- package/types/Listbox.d.ts +24 -0
- package/types/Shortcuts.d.ts +136 -0
- package/types/index.d.ts +12 -0
package/src/Button/propTypes.js
CHANGED
|
@@ -10,6 +10,12 @@ export const textAndA11yText = ABBPropTypes.childrenOf(
|
|
|
10
10
|
|
|
11
11
|
const buttonPropTypes = {
|
|
12
12
|
tokens: getTokensPropType('Button'),
|
|
13
|
+
/**
|
|
14
|
+
* If true, the button will honor the align-items value from its parent flex container.
|
|
15
|
+
* If false, the button will always align to 'flex-start' in a flex container.
|
|
16
|
+
* Currently, `heightFull`'s default behaviour will expand to the full height of its parent's flex container. In an upcoming major release, the default behaviour will be changed to expand based on the content. To maintain the expected behaviour, you must explicitly set `heightFull: true`
|
|
17
|
+
*/
|
|
18
|
+
heightFull: PropTypes.bool,
|
|
13
19
|
/**
|
|
14
20
|
* If true, prevents the button from being pressed, changes the cursor (on web) and accessibility
|
|
15
21
|
* attributes to communicate this to the user, and applies `inactive: true` appearances from the theme
|
package/src/Card/CardBase.jsx
CHANGED
|
@@ -6,9 +6,18 @@ import { applyShadowToken } from '../ThemeProvider'
|
|
|
6
6
|
import { getTokensPropType, responsiveProps, useResponsiveProp, formatImageSource } from '../utils'
|
|
7
7
|
import { a11yProps, viewProps, selectSystemProps } from '../utils/props'
|
|
8
8
|
import backgroundImageStylesMap from './backgroundImageStylesMap'
|
|
9
|
+
import FlexGrid from '../FlexGrid/FlexGrid'
|
|
10
|
+
import FlexGridRow from '../FlexGrid/Row'
|
|
11
|
+
import FlexGridCol from '../FlexGrid/Col'
|
|
9
12
|
|
|
10
13
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
11
14
|
|
|
15
|
+
const GRID_COLUMNS = 12
|
|
16
|
+
|
|
17
|
+
const isOverlayColor = (color) => {
|
|
18
|
+
return color && typeof color === 'string' && color.startsWith('rgba(')
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
const setBackgroundImage = ({
|
|
13
22
|
src,
|
|
14
23
|
alt,
|
|
@@ -22,12 +31,10 @@ const setBackgroundImage = ({
|
|
|
22
31
|
const borderWidth = cardStyle?.borderWidth || 0
|
|
23
32
|
const adjustedBorderRadius = Math.max(0, borderRadius - borderWidth)
|
|
24
33
|
|
|
25
|
-
// For contain mode with position and align, use CSS background properties for web
|
|
26
34
|
if (backgroundImageResizeMode === 'contain' && backgroundImagePosition && backgroundImageAlign) {
|
|
27
35
|
const positionKey = `${backgroundImagePosition}-${backgroundImageAlign}`
|
|
28
36
|
|
|
29
37
|
if (Platform.OS === 'web') {
|
|
30
|
-
// Create background position based on position and align
|
|
31
38
|
let backgroundPosition
|
|
32
39
|
|
|
33
40
|
switch (positionKey) {
|
|
@@ -77,7 +84,7 @@ const setBackgroundImage = ({
|
|
|
77
84
|
</View>
|
|
78
85
|
)
|
|
79
86
|
}
|
|
80
|
-
|
|
87
|
+
|
|
81
88
|
const positionStyles = backgroundImageStylesMap[positionKey] || {}
|
|
82
89
|
|
|
83
90
|
return (
|
|
@@ -95,7 +102,6 @@ const setBackgroundImage = ({
|
|
|
95
102
|
)
|
|
96
103
|
}
|
|
97
104
|
|
|
98
|
-
// Use ImageBackground for all other resize modes and React Native
|
|
99
105
|
return (
|
|
100
106
|
<ImageBackground
|
|
101
107
|
source={src}
|
|
@@ -121,6 +127,10 @@ export const selectStyles = ({
|
|
|
121
127
|
paddingLeft,
|
|
122
128
|
paddingRight,
|
|
123
129
|
paddingTop,
|
|
130
|
+
marginTop,
|
|
131
|
+
marginBottom,
|
|
132
|
+
marginLeft,
|
|
133
|
+
marginRight,
|
|
124
134
|
minWidth,
|
|
125
135
|
shadow,
|
|
126
136
|
backgroundGradient,
|
|
@@ -128,9 +138,27 @@ export const selectStyles = ({
|
|
|
128
138
|
maxHeight,
|
|
129
139
|
overflowY
|
|
130
140
|
}) => {
|
|
141
|
+
const hasGradient = (gradient || backgroundGradient) && Platform.OS === 'web'
|
|
142
|
+
|
|
143
|
+
let backgroundImageValue = null
|
|
144
|
+
|
|
145
|
+
if (hasGradient) {
|
|
146
|
+
const gradientObj = gradient || backgroundGradient
|
|
147
|
+
const gradientString = `linear-gradient(${gradientObj.angle}deg, ${gradientObj.stops[0].color}, ${gradientObj.stops[1].color})`
|
|
148
|
+
const shouldApplyOverlay =
|
|
149
|
+
(gradient || (backgroundGradient && backgroundColor && backgroundColor !== 'transparent')) &&
|
|
150
|
+
isOverlayColor(backgroundColor)
|
|
151
|
+
|
|
152
|
+
backgroundImageValue = shouldApplyOverlay
|
|
153
|
+
? `linear-gradient(${backgroundColor}, ${backgroundColor}), ${gradientString}`
|
|
154
|
+
: gradientString
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const boxShadowColor = isOverlayColor(backgroundColor) ? backgroundColor : 'white'
|
|
158
|
+
|
|
131
159
|
return {
|
|
132
160
|
flex,
|
|
133
|
-
backgroundColor,
|
|
161
|
+
backgroundColor: hasGradient ? 'transparent' : backgroundColor,
|
|
134
162
|
borderColor,
|
|
135
163
|
borderRadius,
|
|
136
164
|
borderWidth,
|
|
@@ -138,19 +166,23 @@ export const selectStyles = ({
|
|
|
138
166
|
paddingLeft,
|
|
139
167
|
paddingRight,
|
|
140
168
|
paddingTop,
|
|
169
|
+
marginTop,
|
|
170
|
+
marginBottom,
|
|
171
|
+
marginLeft,
|
|
172
|
+
marginRight,
|
|
141
173
|
minWidth,
|
|
142
174
|
...applyShadowToken(shadow),
|
|
143
175
|
...(gradient && Platform.OS === 'web'
|
|
144
176
|
? {
|
|
145
|
-
backgroundImage:
|
|
177
|
+
backgroundImage: backgroundImageValue,
|
|
146
178
|
backgroundOrigin: `border-box`,
|
|
147
|
-
boxShadow: `inset 0 1000px
|
|
179
|
+
boxShadow: `inset 0 1000px ${boxShadowColor}`,
|
|
148
180
|
border: `${borderWidth}px solid transparent`
|
|
149
181
|
}
|
|
150
182
|
: {}),
|
|
151
183
|
...(backgroundGradient && Platform.OS === 'web'
|
|
152
184
|
? {
|
|
153
|
-
backgroundImage:
|
|
185
|
+
backgroundImage: backgroundImageValue
|
|
154
186
|
}
|
|
155
187
|
: {}),
|
|
156
188
|
...(Platform.OS === 'web' ? { maxHeight, overflowY } : {})
|
|
@@ -162,8 +194,8 @@ export const selectStyles = ({
|
|
|
162
194
|
* intended to be used in apps or sites directly: build themed components on top of this.
|
|
163
195
|
*/
|
|
164
196
|
const CardBase = React.forwardRef(
|
|
165
|
-
({ children, tokens, dataSet, backgroundImage, ...rest }, ref) => {
|
|
166
|
-
const cardStyle = selectStyles(typeof tokens === 'function' ? tokens() : tokens)
|
|
197
|
+
({ children, tokens, dataSet, backgroundImage, fullBleedContent, cardState, ...rest }, ref) => {
|
|
198
|
+
const cardStyle = selectStyles(typeof tokens === 'function' ? tokens(cardState) : tokens)
|
|
167
199
|
const props = selectProps(rest)
|
|
168
200
|
|
|
169
201
|
let content = children
|
|
@@ -174,12 +206,16 @@ const CardBase = React.forwardRef(
|
|
|
174
206
|
const backgroundImageAlign = useResponsiveProp(align)
|
|
175
207
|
const imageSourceViewport = formatImageSource(useResponsiveProp(src))
|
|
176
208
|
|
|
209
|
+
const {
|
|
210
|
+
content: fullBleedImageContent,
|
|
211
|
+
position: fullBleedContentPosition,
|
|
212
|
+
imgCol
|
|
213
|
+
} = fullBleedContent || {}
|
|
214
|
+
const fullBleedPosition = useResponsiveProp(fullBleedContentPosition, 'bottom')
|
|
215
|
+
|
|
177
216
|
if (backgroundImage && src) {
|
|
178
|
-
// When there's a background image, separate the padding from the container style
|
|
179
|
-
// so the image can fill the entire container without padding interference
|
|
180
217
|
const { paddingTop, paddingBottom, paddingLeft, paddingRight, ...containerStyle } = cardStyle
|
|
181
218
|
|
|
182
|
-
// Only create padding wrapper if there's actually padding defined
|
|
183
219
|
const hasPadding = paddingTop || paddingBottom || paddingLeft || paddingRight
|
|
184
220
|
const paddedContent = hasPadding ? (
|
|
185
221
|
<View style={{ paddingTop, paddingBottom, paddingLeft, paddingRight }}>{children}</View>
|
|
@@ -204,6 +240,53 @@ const CardBase = React.forwardRef(
|
|
|
204
240
|
)
|
|
205
241
|
}
|
|
206
242
|
|
|
243
|
+
if (fullBleedContent && fullBleedImageContent) {
|
|
244
|
+
const { paddingTop, paddingBottom, paddingLeft, paddingRight, ...containerStyle } = cardStyle
|
|
245
|
+
const imageColumns = imgCol || { xs: GRID_COLUMNS }
|
|
246
|
+
|
|
247
|
+
const textColumns = {}
|
|
248
|
+
Object.keys(imageColumns).forEach((breakpoint) => {
|
|
249
|
+
textColumns[breakpoint] = GRID_COLUMNS - (imageColumns[breakpoint] || GRID_COLUMNS)
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
const imageFirst = fullBleedPosition === 'top' || fullBleedPosition === 'left'
|
|
253
|
+
|
|
254
|
+
const imageColContent = (
|
|
255
|
+
<FlexGridCol {...imageColumns} style={staticStyles.fullBleedImageCol}>
|
|
256
|
+
{fullBleedImageContent}
|
|
257
|
+
</FlexGridCol>
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
const textColContent = (
|
|
261
|
+
<FlexGridCol
|
|
262
|
+
{...textColumns}
|
|
263
|
+
style={{ paddingTop, paddingBottom, paddingLeft, paddingRight }}
|
|
264
|
+
>
|
|
265
|
+
{children}
|
|
266
|
+
</FlexGridCol>
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
return (
|
|
270
|
+
<View style={containerStyle} dataSet={dataSet} ref={ref} {...props}>
|
|
271
|
+
<FlexGrid>
|
|
272
|
+
<FlexGridRow>
|
|
273
|
+
{imageFirst ? (
|
|
274
|
+
<>
|
|
275
|
+
{imageColContent}
|
|
276
|
+
{textColContent}
|
|
277
|
+
</>
|
|
278
|
+
) : (
|
|
279
|
+
<>
|
|
280
|
+
{textColContent}
|
|
281
|
+
{imageColContent}
|
|
282
|
+
</>
|
|
283
|
+
)}
|
|
284
|
+
</FlexGridRow>
|
|
285
|
+
</FlexGrid>
|
|
286
|
+
</View>
|
|
287
|
+
)
|
|
288
|
+
}
|
|
289
|
+
|
|
207
290
|
return (
|
|
208
291
|
<View style={cardStyle} dataSet={dataSet} ref={ref} {...props}>
|
|
209
292
|
{content}
|
|
@@ -213,6 +296,18 @@ const CardBase = React.forwardRef(
|
|
|
213
296
|
)
|
|
214
297
|
CardBase.displayName = 'CardBase'
|
|
215
298
|
|
|
299
|
+
export const fullBleedContentPropTypes = PropTypes.shape({
|
|
300
|
+
content: PropTypes.node.isRequired,
|
|
301
|
+
alt: PropTypes.string,
|
|
302
|
+
position: responsiveProps.getTypeOptionallyByViewport(
|
|
303
|
+
PropTypes.oneOf(['bottom', 'left', 'right', 'top'])
|
|
304
|
+
),
|
|
305
|
+
align: responsiveProps.getTypeOptionallyByViewport(
|
|
306
|
+
PropTypes.oneOf(['start', 'end', 'center', 'stretch'])
|
|
307
|
+
),
|
|
308
|
+
imgCol: PropTypes.object
|
|
309
|
+
})
|
|
310
|
+
|
|
216
311
|
const staticStyles = StyleSheet.create({
|
|
217
312
|
imageBackground: { width: '100%', height: '100%' },
|
|
218
313
|
contentOverlay: {
|
|
@@ -231,6 +326,9 @@ const staticStyles = StyleSheet.create({
|
|
|
231
326
|
position: 'absolute',
|
|
232
327
|
width: '100%',
|
|
233
328
|
height: '100%'
|
|
329
|
+
},
|
|
330
|
+
fullBleedImageCol: {
|
|
331
|
+
padding: 0
|
|
234
332
|
}
|
|
235
333
|
})
|
|
236
334
|
|
|
@@ -255,7 +353,8 @@ CardBase.propTypes = {
|
|
|
255
353
|
align: responsiveProps.getTypeOptionallyByViewport(
|
|
256
354
|
PropTypes.oneOf(['start', 'end', 'center', 'stretch'])
|
|
257
355
|
)
|
|
258
|
-
})
|
|
356
|
+
}),
|
|
357
|
+
fullBleedContent: fullBleedContentPropTypes
|
|
259
358
|
}
|
|
260
359
|
|
|
261
360
|
export default CardBase
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
viewProps,
|
|
19
19
|
withLinkRouter
|
|
20
20
|
} from '../utils'
|
|
21
|
-
import CardBase from './CardBase'
|
|
21
|
+
import CardBase, { fullBleedContentPropTypes } from './CardBase'
|
|
22
22
|
|
|
23
23
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
24
24
|
a11yProps,
|
|
@@ -37,6 +37,10 @@ const tokenKeys = [
|
|
|
37
37
|
'paddingLeft',
|
|
38
38
|
'paddingRight',
|
|
39
39
|
'paddingTop',
|
|
40
|
+
'marginTop',
|
|
41
|
+
'marginBottom',
|
|
42
|
+
'marginLeft',
|
|
43
|
+
'marginRight',
|
|
40
44
|
'minWidth',
|
|
41
45
|
'shadow',
|
|
42
46
|
'contentAlignItems',
|
|
@@ -81,6 +85,7 @@ const PressableCardBase = React.forwardRef(
|
|
|
81
85
|
hrefAttrs,
|
|
82
86
|
dataSet,
|
|
83
87
|
backgroundImage,
|
|
88
|
+
fullBleedContent,
|
|
84
89
|
accessibilityRole = href ? 'link' : undefined,
|
|
85
90
|
...rawRest
|
|
86
91
|
},
|
|
@@ -159,13 +164,14 @@ const PressableCardBase = React.forwardRef(
|
|
|
159
164
|
setFocused(false)
|
|
160
165
|
setPressed(false)
|
|
161
166
|
}}
|
|
162
|
-
style={
|
|
167
|
+
style={staticStyles.linkContainer}
|
|
163
168
|
{...(hrefAttrs || {})}
|
|
164
169
|
role={accessibilityRole}
|
|
165
170
|
>
|
|
166
171
|
<CardBase
|
|
167
172
|
tokens={getCardTokens({ pressed, focused, hovered })}
|
|
168
173
|
backgroundImage={backgroundImage}
|
|
174
|
+
fullBleedContent={fullBleedContent}
|
|
169
175
|
>
|
|
170
176
|
{typeof children === 'function'
|
|
171
177
|
? children(getCardState({ pressed, focused, hovered }))
|
|
@@ -188,7 +194,11 @@ const PressableCardBase = React.forwardRef(
|
|
|
188
194
|
{...selectProps({ ...rest, accessibilityRole })}
|
|
189
195
|
>
|
|
190
196
|
{(pressableState) => (
|
|
191
|
-
<CardBase
|
|
197
|
+
<CardBase
|
|
198
|
+
tokens={getCardTokens(pressableState)}
|
|
199
|
+
backgroundImage={backgroundImage}
|
|
200
|
+
fullBleedContent={fullBleedContent}
|
|
201
|
+
>
|
|
192
202
|
{typeof children === 'function' ? children(getCardState(pressableState)) : children}
|
|
193
203
|
</CardBase>
|
|
194
204
|
)}
|
|
@@ -206,7 +216,8 @@ const staticStyles = StyleSheet.create({
|
|
|
206
216
|
flex: 1,
|
|
207
217
|
display: 'flex',
|
|
208
218
|
alignItems: 'stretch',
|
|
209
|
-
justifyContent: 'flex-start'
|
|
219
|
+
justifyContent: 'flex-start',
|
|
220
|
+
textDecorationLine: 'none'
|
|
210
221
|
}
|
|
211
222
|
})
|
|
212
223
|
|
|
@@ -234,7 +245,8 @@ PressableCardBase.propTypes = {
|
|
|
234
245
|
PropTypes.oneOf(['start', 'end', 'center', 'stretch']),
|
|
235
246
|
PropTypes.object
|
|
236
247
|
])
|
|
237
|
-
})
|
|
248
|
+
}),
|
|
249
|
+
fullBleedContent: fullBleedContentPropTypes
|
|
238
250
|
}
|
|
239
251
|
|
|
240
252
|
export default withLinkRouter(PressableCardBase)
|
|
@@ -407,6 +407,7 @@ const Carousel = React.forwardRef(
|
|
|
407
407
|
ref
|
|
408
408
|
) => {
|
|
409
409
|
let childrenArray = unpackFragment(children)
|
|
410
|
+
const isTransitioningRef = React.useRef(false)
|
|
410
411
|
const viewport = useViewport()
|
|
411
412
|
const totalItems = getTotalItems(enableDisplayMultipleItemsPerSlide, childrenArray, viewport)
|
|
412
413
|
const autoPlayFeatureEnabled =
|
|
@@ -482,10 +483,14 @@ const Carousel = React.forwardRef(
|
|
|
482
483
|
)
|
|
483
484
|
const handleAnimationEnd = React.useCallback(
|
|
484
485
|
(...args) => {
|
|
485
|
-
|
|
486
|
-
|
|
486
|
+
const result = args[args.length - 1]
|
|
487
|
+
if (result?.finished) {
|
|
488
|
+
if (typeof onAnimationEnd === 'function') onAnimationEnd(...args)
|
|
489
|
+
setIsAnimating(false)
|
|
490
|
+
isTransitioningRef.current = false
|
|
491
|
+
}
|
|
487
492
|
},
|
|
488
|
-
[onAnimationEnd]
|
|
493
|
+
[onAnimationEnd, isTransitioningRef]
|
|
489
494
|
)
|
|
490
495
|
|
|
491
496
|
const updateOffset = React.useCallback(() => {
|
|
@@ -602,7 +607,9 @@ const Carousel = React.forwardRef(
|
|
|
602
607
|
|
|
603
608
|
const index = activeIndexRef.current + calcDelta
|
|
604
609
|
if (skipChanges) {
|
|
610
|
+
isTransitioningRef.current = true
|
|
605
611
|
animate(pan, toValue, index)
|
|
612
|
+
|
|
606
613
|
if (enableHero) {
|
|
607
614
|
animate(heroPan, toValue, index)
|
|
608
615
|
}
|
|
@@ -622,6 +629,7 @@ const Carousel = React.forwardRef(
|
|
|
622
629
|
toValue.x = finalWidth * -1 * calcDelta
|
|
623
630
|
const heroToValue = { x: 0, y: 0 }
|
|
624
631
|
heroToValue.x = heroContainerLayoutRef.current.width * -1 * calcDelta
|
|
632
|
+
isTransitioningRef.current = true
|
|
625
633
|
animate(pan, toValue, index)
|
|
626
634
|
if (enableHero) {
|
|
627
635
|
animate(heroPan, heroToValue, index)
|
|
@@ -1012,6 +1020,15 @@ const Carousel = React.forwardRef(
|
|
|
1012
1020
|
setisCarouselPlaying((prevState) => !prevState)
|
|
1013
1021
|
}, [isCarouselPlaying, stopAutoplay, startAutoplay])
|
|
1014
1022
|
|
|
1023
|
+
const handleKeyDown = React.useCallback(
|
|
1024
|
+
(event) => {
|
|
1025
|
+
if (isTransitioningRef.current && event.key === 'Tab') {
|
|
1026
|
+
event.preventDefault()
|
|
1027
|
+
}
|
|
1028
|
+
},
|
|
1029
|
+
[isTransitioningRef]
|
|
1030
|
+
)
|
|
1031
|
+
|
|
1015
1032
|
return (
|
|
1016
1033
|
<View style={selectRootContainerStyles(enableHero, viewport)}>
|
|
1017
1034
|
<View style={selectMainContainerStyles(enableHero, viewport)}>
|
|
@@ -1041,6 +1058,7 @@ const Carousel = React.forwardRef(
|
|
|
1041
1058
|
ref={ref}
|
|
1042
1059
|
{...systemProps}
|
|
1043
1060
|
{...containerProps}
|
|
1061
|
+
{...(Platform.OS === 'web' ? { onKeyDown: handleKeyDown } : {})}
|
|
1044
1062
|
>
|
|
1045
1063
|
{isAutoPlayEnabled ? (
|
|
1046
1064
|
<View
|
|
@@ -1130,10 +1148,45 @@ const Carousel = React.forwardRef(
|
|
|
1130
1148
|
accessibilityLiveRegion={accessibilityLiveRegion}
|
|
1131
1149
|
>
|
|
1132
1150
|
{childrenArray.map((element, index) => {
|
|
1133
|
-
|
|
1151
|
+
let hidden = !isAnimating && index !== activeIndex
|
|
1152
|
+
|
|
1153
|
+
if (enablePeeking && !isAnimating) {
|
|
1154
|
+
if (enableDisplayMultipleItemsPerSlide) {
|
|
1155
|
+
const maxItemsForSlide = getMaximumItemsForSlide(
|
|
1156
|
+
enableDisplayMultipleItemsPerSlide,
|
|
1157
|
+
viewport
|
|
1158
|
+
)
|
|
1159
|
+
if (
|
|
1160
|
+
index >= activeIndex * maxItemsForSlide - 1 &&
|
|
1161
|
+
index < activeIndex * maxItemsForSlide + maxItemsForSlide + 1
|
|
1162
|
+
) {
|
|
1163
|
+
hidden = false
|
|
1164
|
+
} else {
|
|
1165
|
+
hidden = true
|
|
1166
|
+
}
|
|
1167
|
+
} else if (index >= activeIndex - 1 && index <= activeIndex + 1) {
|
|
1168
|
+
hidden = false
|
|
1169
|
+
}
|
|
1170
|
+
} else if (
|
|
1171
|
+
!enablePeeking &&
|
|
1172
|
+
enableDisplayMultipleItemsPerSlide &&
|
|
1173
|
+
!isAnimating
|
|
1174
|
+
) {
|
|
1175
|
+
const maxItemsForSlide = getMaximumItemsForSlide(
|
|
1176
|
+
enableDisplayMultipleItemsPerSlide,
|
|
1177
|
+
viewport
|
|
1178
|
+
)
|
|
1179
|
+
if (
|
|
1180
|
+
index >= activeIndex * maxItemsForSlide &&
|
|
1181
|
+
index < activeIndex * maxItemsForSlide + maxItemsForSlide
|
|
1182
|
+
) {
|
|
1183
|
+
hidden = false
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1134
1187
|
const clonedElement = React.cloneElement(element, {
|
|
1135
1188
|
elementIndex: index,
|
|
1136
|
-
hidden
|
|
1189
|
+
hidden,
|
|
1137
1190
|
enablePeeking,
|
|
1138
1191
|
peekingProps: getPeekingProps(viewport),
|
|
1139
1192
|
enableDisplayMultipleItemsPerSlide,
|
|
@@ -114,15 +114,43 @@ const CarouselItem = React.forwardRef(
|
|
|
114
114
|
|
|
115
115
|
const handleFocus = React.useCallback(
|
|
116
116
|
(event) => {
|
|
117
|
-
if (Platform.OS === 'web'
|
|
118
|
-
|
|
117
|
+
if (Platform.OS === 'web') {
|
|
118
|
+
if (enablePeeking) {
|
|
119
|
+
if (enableDisplayMultipleItemsPerSlide) {
|
|
120
|
+
const startIndex = maximumItemsForSlide * activeIndex
|
|
121
|
+
const endIndex = startIndex + maximumItemsForSlide - 1
|
|
122
|
+
if (elementIndex < startIndex) {
|
|
123
|
+
if (activeIndex - 1 < 0) {
|
|
124
|
+
goTo(0)
|
|
125
|
+
} else {
|
|
126
|
+
goTo(activeIndex - 1)
|
|
127
|
+
}
|
|
128
|
+
} else if (elementIndex > endIndex) {
|
|
129
|
+
goTo(activeIndex + 1)
|
|
130
|
+
}
|
|
131
|
+
} else if (elementIndex !== activeIndex) {
|
|
132
|
+
if (elementIndex > activeIndex) {
|
|
133
|
+
goTo(activeIndex + 1)
|
|
134
|
+
} else if (elementIndex < activeIndex) {
|
|
135
|
+
goTo(activeIndex - 1)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
119
139
|
}
|
|
120
140
|
|
|
121
141
|
if (rest.onFocus) {
|
|
122
142
|
rest.onFocus(event)
|
|
123
143
|
}
|
|
124
144
|
},
|
|
125
|
-
[
|
|
145
|
+
[
|
|
146
|
+
elementIndex,
|
|
147
|
+
activeIndex,
|
|
148
|
+
goTo,
|
|
149
|
+
maximumItemsForSlide,
|
|
150
|
+
rest,
|
|
151
|
+
enablePeeking,
|
|
152
|
+
enableDisplayMultipleItemsPerSlide
|
|
153
|
+
]
|
|
126
154
|
)
|
|
127
155
|
|
|
128
156
|
return (
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
getTokensSetPropType,
|
|
9
9
|
selectSystemProps,
|
|
10
10
|
selectTokens,
|
|
11
|
+
variantProp,
|
|
11
12
|
viewProps
|
|
12
13
|
} from '../utils'
|
|
13
14
|
import ScrollViewEnd from './ScrollViewEnd'
|
|
@@ -38,7 +39,7 @@ const selectBorderStyles = (borderBottomWidth, borderBottomColor) => ({
|
|
|
38
39
|
* @TODO revisit `ScrollButton` after IconButton is stable.
|
|
39
40
|
*/
|
|
40
41
|
const HorizontalScroll = React.forwardRef(
|
|
41
|
-
({ ScrollButton, tokens, itemPositions, children, ...rest }, ref) => {
|
|
42
|
+
({ ScrollButton, tokens, itemPositions, variant, children, ...rest }, ref) => {
|
|
42
43
|
const {
|
|
43
44
|
nextIcon,
|
|
44
45
|
previousIcon,
|
|
@@ -74,8 +75,9 @@ const HorizontalScroll = React.forwardRef(
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
const scrollMax = Math.max(0, contentWidth - containerWidth)
|
|
77
|
-
const
|
|
78
|
-
const
|
|
78
|
+
const hideNavigationButtons = variant?.hideNavigationButtons || false
|
|
79
|
+
const showNextButton = scrollOffset < scrollMax && !hideNavigationButtons
|
|
80
|
+
const showPreviousButton = scrollOffset > 0 && !hideNavigationButtons
|
|
79
81
|
|
|
80
82
|
const scrollRef = React.useRef(null)
|
|
81
83
|
const scrollTo = (targetX) => {
|
|
@@ -173,6 +175,7 @@ HorizontalScroll.propTypes = {
|
|
|
173
175
|
itemPositions: itemPositionsPropType,
|
|
174
176
|
ScrollButton: PropTypes.elementType,
|
|
175
177
|
tokens: getTokensSetPropType(tokenKeys, { allowFunction: true }),
|
|
178
|
+
variant: variantProp.propType,
|
|
176
179
|
children: PropTypes.node
|
|
177
180
|
}
|
|
178
181
|
|
package/src/Icon/Icon.jsx
CHANGED
|
@@ -14,6 +14,7 @@ const Icon = React.forwardRef(
|
|
|
14
14
|
tokens,
|
|
15
15
|
scalesWithText = false,
|
|
16
16
|
style = {},
|
|
17
|
+
testID,
|
|
17
18
|
dataSet
|
|
18
19
|
},
|
|
19
20
|
ref
|
|
@@ -43,24 +44,23 @@ const Icon = React.forwardRef(
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
const getIconContentForMobile = () => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
return iconContent
|
|
47
|
+
return (
|
|
48
|
+
<View
|
|
49
|
+
testID={testID}
|
|
50
|
+
style={{
|
|
51
|
+
backgroundColor: themeTokens.backgroundColor,
|
|
52
|
+
borderRadius: themeTokens.borderRadius,
|
|
53
|
+
...paddingStyles
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
{iconContent}
|
|
57
|
+
</View>
|
|
58
|
+
)
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
return Platform.OS === 'web' ? (
|
|
63
62
|
<View
|
|
63
|
+
testID={testID}
|
|
64
64
|
ref={ref}
|
|
65
65
|
// eslint-disable-next-line react-native/no-inline-styles
|
|
66
66
|
style={{
|
package/src/Icon/IconText.jsx
CHANGED