@telus-uds/components-base 1.10.0 → 1.12.1
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 +37 -3
- package/component-docs.json +413 -62
- package/lib/BaseProvider/index.js +7 -2
- package/lib/Button/ButtonBase.js +18 -14
- package/lib/Carousel/Carousel.js +92 -71
- package/lib/Carousel/CarouselContext.js +12 -4
- package/lib/Carousel/CarouselItem/CarouselItem.js +24 -9
- package/lib/Carousel/CarouselStepTracker/CarouselStepTracker.js +56 -0
- package/lib/Carousel/CarouselStepTracker/index.js +13 -0
- package/lib/Carousel/dictionary.js +23 -0
- package/lib/Checkbox/Checkbox.js +7 -3
- package/lib/Checkbox/CheckboxGroup.js +1 -1
- package/lib/Feedback/Feedback.js +18 -10
- package/lib/Icon/IconText.js +5 -0
- package/lib/InputLabel/InputLabel.js +11 -5
- package/lib/InputSupports/InputSupports.js +10 -3
- package/lib/InputSupports/useInputSupports.js +3 -2
- package/lib/Link/LinkBase.js +7 -3
- package/lib/List/ListItem.js +7 -3
- package/lib/Modal/Modal.js +4 -0
- package/lib/Notification/Notification.js +7 -2
- package/lib/Pagination/Pagination.js +7 -3
- package/lib/RadioCard/RadioCard.js +6 -1
- package/lib/Select/Select.js +7 -3
- package/lib/Skeleton/Skeleton.js +1 -0
- package/lib/StepTracker/Step.js +8 -4
- package/lib/StepTracker/StepTracker.js +17 -13
- package/lib/Tabs/TabsItem.js +4 -0
- package/lib/TextInput/TextInput.js +3 -1
- package/lib/TextInput/TextInputBase.js +7 -3
- package/lib/ThemeProvider/ThemeProvider.js +20 -3
- package/lib/ThemeProvider/utils/styles.js +8 -1
- package/lib/ThemeProvider/utils/theme-tokens.js +1 -1
- package/lib/Typography/Typography.js +6 -2
- package/lib/index.js +9 -0
- package/lib/utils/index.js +9 -0
- package/lib/utils/props/clickProps.js +2 -2
- package/lib/utils/props/handlerProps.js +77 -31
- package/lib/utils/useScrollBlocking.js +66 -0
- package/lib/utils/useScrollBlocking.native.js +11 -0
- package/lib-module/BaseProvider/index.js +7 -2
- package/lib-module/Button/ButtonBase.js +7 -3
- package/lib-module/Carousel/Carousel.js +85 -70
- package/lib-module/Carousel/CarouselContext.js +11 -4
- package/lib-module/Carousel/CarouselItem/CarouselItem.js +25 -10
- package/lib-module/Carousel/CarouselStepTracker/CarouselStepTracker.js +42 -0
- package/lib-module/Carousel/CarouselStepTracker/index.js +2 -0
- package/lib-module/Carousel/dictionary.js +16 -0
- package/lib-module/Checkbox/Checkbox.js +8 -4
- package/lib-module/Checkbox/CheckboxGroup.js +1 -1
- package/lib-module/Feedback/Feedback.js +19 -11
- package/lib-module/Icon/IconText.js +5 -0
- package/lib-module/InputLabel/InputLabel.js +12 -6
- package/lib-module/InputSupports/InputSupports.js +10 -3
- package/lib-module/InputSupports/useInputSupports.js +3 -2
- package/lib-module/Link/LinkBase.js +8 -4
- package/lib-module/List/ListItem.js +8 -4
- package/lib-module/Modal/Modal.js +3 -0
- package/lib-module/Notification/Notification.js +8 -3
- package/lib-module/Pagination/Pagination.js +8 -4
- package/lib-module/RadioCard/RadioCard.js +7 -2
- package/lib-module/Select/Select.js +8 -4
- package/lib-module/Skeleton/Skeleton.js +1 -0
- package/lib-module/StepTracker/Step.js +9 -5
- package/lib-module/StepTracker/StepTracker.js +17 -14
- package/lib-module/Tabs/TabsItem.js +5 -1
- package/lib-module/TextInput/TextInput.js +3 -1
- package/lib-module/TextInput/TextInputBase.js +8 -4
- package/lib-module/ThemeProvider/ThemeProvider.js +20 -3
- package/lib-module/ThemeProvider/utils/styles.js +8 -1
- package/lib-module/ThemeProvider/utils/theme-tokens.js +1 -1
- package/lib-module/Typography/Typography.js +7 -3
- package/lib-module/index.js +1 -0
- package/lib-module/utils/index.js +1 -0
- package/lib-module/utils/props/clickProps.js +2 -2
- package/lib-module/utils/props/handlerProps.js +78 -31
- package/lib-module/utils/useScrollBlocking.js +58 -0
- package/lib-module/utils/useScrollBlocking.native.js +2 -0
- package/package.json +3 -3
- package/src/BaseProvider/index.jsx +6 -3
- package/src/Button/ButtonBase.jsx +8 -3
- package/src/Carousel/Carousel.jsx +106 -74
- package/src/Carousel/CarouselContext.jsx +15 -4
- package/src/Carousel/CarouselItem/CarouselItem.jsx +26 -8
- package/src/Carousel/CarouselStepTracker/CarouselStepTracker.jsx +36 -0
- package/src/Carousel/CarouselStepTracker/index.js +3 -0
- package/src/Carousel/dictionary.js +16 -0
- package/src/Checkbox/Checkbox.jsx +14 -11
- package/src/Checkbox/CheckboxGroup.jsx +1 -1
- package/src/Feedback/Feedback.jsx +14 -7
- package/src/Icon/IconText.jsx +2 -0
- package/src/InputLabel/InputLabel.jsx +13 -12
- package/src/InputSupports/InputSupports.jsx +18 -3
- package/src/InputSupports/useInputSupports.js +2 -2
- package/src/Link/LinkBase.jsx +10 -4
- package/src/List/ListItem.jsx +9 -4
- package/src/Modal/Modal.jsx +3 -1
- package/src/Notification/Notification.jsx +5 -3
- package/src/Pagination/Pagination.jsx +6 -4
- package/src/RadioCard/RadioCard.jsx +3 -2
- package/src/Select/Select.jsx +12 -3
- package/src/Skeleton/Skeleton.jsx +1 -0
- package/src/StepTracker/Step.jsx +12 -4
- package/src/StepTracker/StepTracker.jsx +20 -13
- package/src/Tabs/TabsItem.jsx +3 -2
- package/src/TextInput/TextInput.jsx +1 -1
- package/src/TextInput/TextInputBase.jsx +11 -3
- package/src/ThemeProvider/ThemeProvider.jsx +16 -3
- package/src/ThemeProvider/utils/styles.js +9 -1
- package/src/ThemeProvider/utils/theme-tokens.js +1 -1
- package/src/Typography/Typography.jsx +11 -12
- package/src/index.js +1 -0
- package/src/utils/index.js +1 -0
- package/src/utils/props/clickProps.js +2 -2
- package/src/utils/props/handlerProps.js +64 -16
- package/src/utils/useScrollBlocking.js +57 -0
- package/src/utils/useScrollBlocking.native.js +2 -0
|
@@ -3,13 +3,22 @@ import { View, Animated, PanResponder, StyleSheet, Platform } from 'react-native
|
|
|
3
3
|
import PropTypes from 'prop-types'
|
|
4
4
|
import { useThemeTokens } from '../ThemeProvider'
|
|
5
5
|
import { useViewport } from '../ViewportProvider'
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
getTokensPropType,
|
|
8
|
+
getA11yPropsFromHtmlTag,
|
|
9
|
+
layoutTags,
|
|
10
|
+
variantProp,
|
|
11
|
+
selectSystemProps,
|
|
12
|
+
a11yProps,
|
|
13
|
+
viewProps,
|
|
14
|
+
useCopy
|
|
15
|
+
} from '../utils'
|
|
7
16
|
import { useA11yInfo } from '../A11yInfoProvider'
|
|
8
17
|
import { CarouselProvider } from './CarouselContext'
|
|
9
18
|
import CarouselItem from './CarouselItem'
|
|
10
|
-
import StepTracker from '../StepTracker'
|
|
11
|
-
import StackView from '../StackView'
|
|
12
19
|
import IconButton from '../IconButton'
|
|
20
|
+
import CarouselStepTracker from './CarouselStepTracker/CarouselStepTracker'
|
|
21
|
+
import dictionary from './dictionary'
|
|
13
22
|
|
|
14
23
|
const staticStyles = StyleSheet.create({
|
|
15
24
|
root: {
|
|
@@ -22,19 +31,6 @@ const staticStyles = StyleSheet.create({
|
|
|
22
31
|
}
|
|
23
32
|
})
|
|
24
33
|
|
|
25
|
-
const staticTokens = {
|
|
26
|
-
stackView: {
|
|
27
|
-
justifyContent: 'center'
|
|
28
|
-
},
|
|
29
|
-
stepTracker: {
|
|
30
|
-
showStepLabel: false,
|
|
31
|
-
showStepTrackerLabel: true,
|
|
32
|
-
knobCompletedBackgroundColor: 'none',
|
|
33
|
-
connectorCompletedColor: 'none',
|
|
34
|
-
connectorColor: 'none'
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
34
|
const selectContainerStyles = (width) => ({
|
|
39
35
|
backgroundColor: 'transparent',
|
|
40
36
|
overflow: 'hidden',
|
|
@@ -79,15 +75,6 @@ const selectPreviousNextNavigationButtonStyles = (
|
|
|
79
75
|
return styles
|
|
80
76
|
}
|
|
81
77
|
|
|
82
|
-
const defaultPanelNavigationDictionary = {
|
|
83
|
-
en: {
|
|
84
|
-
stepTrackerLabel: 'Showing %{stepNumber} of %{stepCount}'
|
|
85
|
-
},
|
|
86
|
-
fr: {
|
|
87
|
-
stepTrackerLabel: 'Étape %{stepNumber} sur %{stepCount}: %{stepLabel}'
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
78
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
92
79
|
|
|
93
80
|
/**
|
|
@@ -150,6 +137,7 @@ const Carousel = React.forwardRef(
|
|
|
150
137
|
tokens,
|
|
151
138
|
variant,
|
|
152
139
|
children,
|
|
140
|
+
itemLabel = 'item',
|
|
153
141
|
previousNextNavigationPosition = 'inside',
|
|
154
142
|
previousNextIconSize = 'default',
|
|
155
143
|
minDistanceToCapture = 5,
|
|
@@ -158,26 +146,46 @@ const Carousel = React.forwardRef(
|
|
|
158
146
|
onAnimationEnd,
|
|
159
147
|
onIndexChanged,
|
|
160
148
|
springConfig = undefined,
|
|
161
|
-
|
|
162
|
-
|
|
149
|
+
panelNavigation = <CarouselStepTracker />,
|
|
150
|
+
tag = 'ul',
|
|
163
151
|
accessibilityRole = 'adjustable',
|
|
164
152
|
accessibilityLabel = 'carousel',
|
|
153
|
+
copy,
|
|
165
154
|
...rest
|
|
166
155
|
},
|
|
167
156
|
ref
|
|
168
157
|
) => {
|
|
169
158
|
const viewport = useViewport()
|
|
159
|
+
const themeTokens = useThemeTokens('Carousel', tokens, variant, {
|
|
160
|
+
viewport
|
|
161
|
+
})
|
|
170
162
|
const {
|
|
171
163
|
previousIcon,
|
|
172
164
|
nextIcon,
|
|
173
165
|
showPreviousNextNavigation,
|
|
174
166
|
showPanelNavigation,
|
|
175
|
-
spaceBetweenSlideAndPreviousNextNavigation
|
|
176
|
-
|
|
177
|
-
} = useThemeTokens('Carousel', tokens, variant, {
|
|
178
|
-
viewport
|
|
179
|
-
})
|
|
167
|
+
spaceBetweenSlideAndPreviousNextNavigation
|
|
168
|
+
} = themeTokens
|
|
180
169
|
const [activeIndex, setActiveIndex] = React.useState(0)
|
|
170
|
+
|
|
171
|
+
const [isAnimating, setIsAnimating] = React.useState(false)
|
|
172
|
+
const handleAnimationStart = React.useCallback(
|
|
173
|
+
(...args) => {
|
|
174
|
+
if (typeof onAnimationStart === 'function') onAnimationStart(...args)
|
|
175
|
+
setIsAnimating(true)
|
|
176
|
+
},
|
|
177
|
+
[onAnimationStart]
|
|
178
|
+
)
|
|
179
|
+
const handleAnimationEnd = React.useCallback(
|
|
180
|
+
(...args) => {
|
|
181
|
+
if (typeof onAnimationEnd === 'function') onAnimationEnd(...args)
|
|
182
|
+
setIsAnimating(false)
|
|
183
|
+
},
|
|
184
|
+
[onAnimationEnd]
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
const getCopy = useCopy({ dictionary, copy })
|
|
188
|
+
|
|
181
189
|
const childrenArray = React.Children.toArray(children)
|
|
182
190
|
const systemProps = selectProps({
|
|
183
191
|
...rest,
|
|
@@ -202,10 +210,6 @@ const Carousel = React.forwardRef(
|
|
|
202
210
|
const animatedY = React.useRef(0)
|
|
203
211
|
const isFirstSlide = !activeIndex
|
|
204
212
|
const isLastSlide = activeIndex + 1 >= children.length
|
|
205
|
-
const panelNavigationTokens = {
|
|
206
|
-
...staticTokens.stepTracker,
|
|
207
|
-
containerPaddingTop: spaceBetweenSlideAndPanelNavigation
|
|
208
|
-
}
|
|
209
213
|
|
|
210
214
|
const onContainerLayout = ({
|
|
211
215
|
nativeEvent: {
|
|
@@ -230,18 +234,21 @@ const Carousel = React.forwardRef(
|
|
|
230
234
|
}, [activeIndex, containerLayout.width, pan, animatedX])
|
|
231
235
|
|
|
232
236
|
const animate = React.useCallback(
|
|
233
|
-
(toValue) => {
|
|
237
|
+
(toValue, toIndex) => {
|
|
238
|
+
const handleAnimationEndToIndex = (...args) => handleAnimationEnd(toIndex, ...args)
|
|
234
239
|
if (reduceMotionEnabled) {
|
|
235
|
-
Animated.timing(pan, { toValue, duration: 1, useNativeDriver: false }).start(
|
|
240
|
+
Animated.timing(pan, { toValue, duration: 1, useNativeDriver: false }).start(
|
|
241
|
+
handleAnimationEndToIndex
|
|
242
|
+
)
|
|
236
243
|
} else {
|
|
237
244
|
Animated.spring(pan, {
|
|
238
245
|
...springConfig,
|
|
239
246
|
toValue,
|
|
240
247
|
useNativeDriver: false
|
|
241
|
-
}).start()
|
|
248
|
+
}).start(handleAnimationEndToIndex)
|
|
242
249
|
}
|
|
243
250
|
},
|
|
244
|
-
[pan, springConfig, reduceMotionEnabled]
|
|
251
|
+
[pan, springConfig, reduceMotionEnabled, handleAnimationEnd]
|
|
245
252
|
)
|
|
246
253
|
|
|
247
254
|
const updateIndex = React.useCallback(
|
|
@@ -258,32 +265,32 @@ const Carousel = React.forwardRef(
|
|
|
258
265
|
calcDelta = -1 * activeIndex + delta - 1
|
|
259
266
|
}
|
|
260
267
|
|
|
268
|
+
const index = activeIndex + calcDelta
|
|
269
|
+
|
|
261
270
|
if (skipChanges) {
|
|
262
|
-
animate(toValue)
|
|
271
|
+
animate(toValue, index)
|
|
263
272
|
return calcDelta
|
|
264
273
|
}
|
|
265
274
|
|
|
266
|
-
const index = activeIndex + calcDelta
|
|
267
275
|
setActiveIndex(index)
|
|
268
276
|
|
|
269
277
|
toValue.x = containerLayout.width * -1 * calcDelta
|
|
270
278
|
|
|
271
|
-
animate(toValue)
|
|
279
|
+
animate(toValue, index)
|
|
272
280
|
|
|
273
281
|
if (onIndexChanged) onIndexChanged(calcDelta)
|
|
274
|
-
if (onAnimationEnd) onAnimationEnd(index)
|
|
275
282
|
return calcDelta
|
|
276
283
|
},
|
|
277
|
-
[containerLayout.width, activeIndex, animate, children.length, onIndexChanged
|
|
284
|
+
[containerLayout.width, activeIndex, animate, children.length, onIndexChanged]
|
|
278
285
|
)
|
|
279
286
|
|
|
280
287
|
const fixOffsetAndGo = React.useCallback(
|
|
281
288
|
(delta) => {
|
|
282
289
|
updateOffset()
|
|
283
|
-
|
|
290
|
+
handleAnimationStart(activeIndex)
|
|
284
291
|
updateIndex(delta)
|
|
285
292
|
},
|
|
286
|
-
[updateIndex, updateOffset, activeIndex,
|
|
293
|
+
[updateIndex, updateOffset, activeIndex, handleAnimationStart]
|
|
287
294
|
)
|
|
288
295
|
|
|
289
296
|
const goToNeighboring = React.useCallback(
|
|
@@ -310,7 +317,7 @@ const Carousel = React.forwardRef(
|
|
|
310
317
|
return false
|
|
311
318
|
}
|
|
312
319
|
|
|
313
|
-
|
|
320
|
+
handleAnimationStart(activeIndex)
|
|
314
321
|
|
|
315
322
|
return Math.abs(gestureState.dx) > minDistanceToCapture
|
|
316
323
|
},
|
|
@@ -322,7 +329,7 @@ const Carousel = React.forwardRef(
|
|
|
322
329
|
const correction = gesture.moveX - gesture.x0
|
|
323
330
|
|
|
324
331
|
if (Math.abs(correction) < containerLayout.width * minDistanceForAction) {
|
|
325
|
-
animate({ x: 0, y: 0 })
|
|
332
|
+
animate({ x: 0, y: 0 }, 0)
|
|
326
333
|
} else {
|
|
327
334
|
const delta = correction > 0 ? -1 : 1
|
|
328
335
|
updateIndex(delta)
|
|
@@ -337,7 +344,7 @@ const Carousel = React.forwardRef(
|
|
|
337
344
|
isSwipeAllowed,
|
|
338
345
|
activeIndex,
|
|
339
346
|
minDistanceForAction,
|
|
340
|
-
|
|
347
|
+
handleAnimationStart,
|
|
341
348
|
minDistanceToCapture,
|
|
342
349
|
pan.x
|
|
343
350
|
]
|
|
@@ -379,12 +386,27 @@ const Carousel = React.forwardRef(
|
|
|
379
386
|
// Related discussion - https://github.com/telus/universal-design-system/issues/1549
|
|
380
387
|
const previousNextIconButtonVariants = { size: previousNextIconSize, raised: true }
|
|
381
388
|
|
|
389
|
+
const getCopyWithPlaceholders = React.useCallback(
|
|
390
|
+
(copyKey) => {
|
|
391
|
+
const copyText = getCopy(copyKey)
|
|
392
|
+
.replace(/%\{itemLabel\}/g, itemLabel)
|
|
393
|
+
.replace(/%\{stepNumber\}/g, activeIndex + 1)
|
|
394
|
+
.replace(/%\{stepCount\}/g, childrenArray.length)
|
|
395
|
+
|
|
396
|
+
// First word might be a lowercase placeholder: capitalize the first letter
|
|
397
|
+
return `${copyText[0].toUpperCase()}${copyText.slice(1)}`
|
|
398
|
+
},
|
|
399
|
+
[activeIndex, childrenArray.length, itemLabel, getCopy]
|
|
400
|
+
)
|
|
401
|
+
|
|
382
402
|
return (
|
|
383
403
|
<CarouselProvider
|
|
384
404
|
activeIndex={activeIndex}
|
|
385
405
|
totalItems={childrenArray.length}
|
|
386
406
|
width={containerLayout.width}
|
|
387
407
|
goTo={goTo}
|
|
408
|
+
getCopyWithPlaceholders={getCopyWithPlaceholders}
|
|
409
|
+
themeTokens={themeTokens}
|
|
388
410
|
>
|
|
389
411
|
<View style={staticStyles.root} onLayout={onContainerLayout} ref={ref} {...systemProps}>
|
|
390
412
|
{showPreviousNextNavigation && (
|
|
@@ -404,7 +426,10 @@ const Carousel = React.forwardRef(
|
|
|
404
426
|
icon={previousIcon}
|
|
405
427
|
onPress={goToPrev}
|
|
406
428
|
variant={previousNextIconButtonVariants}
|
|
407
|
-
accessibilityLabel=
|
|
429
|
+
accessibilityLabel={getCopyWithPlaceholders('iconButtonLabel').replace(
|
|
430
|
+
'%{targetStep}',
|
|
431
|
+
activeIndex
|
|
432
|
+
)}
|
|
408
433
|
/>
|
|
409
434
|
</View>
|
|
410
435
|
)}
|
|
@@ -417,9 +442,11 @@ const Carousel = React.forwardRef(
|
|
|
417
442
|
}
|
|
418
443
|
])}
|
|
419
444
|
{...panResponder.panHandlers}
|
|
445
|
+
{...getA11yPropsFromHtmlTag(tag)}
|
|
420
446
|
>
|
|
421
447
|
{childrenArray.map((element, index) => {
|
|
422
|
-
const
|
|
448
|
+
const hidden = !isAnimating && index !== activeIndex
|
|
449
|
+
const clonedElement = React.cloneElement(element, { elementIndex: index, hidden })
|
|
423
450
|
return <React.Fragment key={index.toFixed(2)}>{clonedElement}</React.Fragment>
|
|
424
451
|
})}
|
|
425
452
|
</Animated.View>
|
|
@@ -441,25 +468,15 @@ const Carousel = React.forwardRef(
|
|
|
441
468
|
icon={nextIcon}
|
|
442
469
|
onPress={goToNext}
|
|
443
470
|
variant={previousNextIconButtonVariants}
|
|
444
|
-
accessibilityLabel=
|
|
471
|
+
accessibilityLabel={getCopyWithPlaceholders('iconButtonLabel').replace(
|
|
472
|
+
'%{targetStep}',
|
|
473
|
+
activeIndex + 2
|
|
474
|
+
)}
|
|
445
475
|
/>
|
|
446
476
|
</View>
|
|
447
477
|
)}
|
|
448
478
|
</View>
|
|
449
|
-
{showPanelNavigation ?
|
|
450
|
-
<StackView direction="row" tokens={staticTokens.stackView}>
|
|
451
|
-
{onRenderPanelNavigation ? (
|
|
452
|
-
onRenderPanelNavigation({ activeIndex, totalItems: childrenArray.length })
|
|
453
|
-
) : (
|
|
454
|
-
<StepTracker
|
|
455
|
-
current={activeIndex}
|
|
456
|
-
steps={childrenArray.map((_, index) => String(index))}
|
|
457
|
-
dictionary={panelNavigationTextDictionary}
|
|
458
|
-
tokens={panelNavigationTokens}
|
|
459
|
-
/>
|
|
460
|
-
)}
|
|
461
|
-
</StackView>
|
|
462
|
-
) : null}
|
|
479
|
+
{showPanelNavigation ? panelNavigation : null}
|
|
463
480
|
</CarouselProvider>
|
|
464
481
|
)
|
|
465
482
|
}
|
|
@@ -473,6 +490,13 @@ Carousel.propTypes = {
|
|
|
473
490
|
* Slides to render in Carousel. Wrap individual slides in `Carousel.Item`
|
|
474
491
|
*/
|
|
475
492
|
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
|
|
493
|
+
/**
|
|
494
|
+
* Lowercase language-appropriate user-facing description of what each Carousel slide represents.
|
|
495
|
+
* This is used when generating item labels. For example, if a carousel contains offers,
|
|
496
|
+
* pass itemLabel="summer offer" (or copy="fr" and an appropriate French translation) to genereate
|
|
497
|
+
* accessible labels such as "Summer offer 1 of 3" and "Show summer offer 2 of 3".
|
|
498
|
+
*/
|
|
499
|
+
itemLabel: PropTypes.string,
|
|
476
500
|
/**
|
|
477
501
|
* `inside` renders the previous and next buttons inside the slide
|
|
478
502
|
* `outside` renders the previous and next buttons outside the slide
|
|
@@ -514,20 +538,20 @@ Carousel.propTypes = {
|
|
|
514
538
|
*/
|
|
515
539
|
onIndexChanged: PropTypes.func,
|
|
516
540
|
/**
|
|
517
|
-
* Use this to render a custom panel navigation element instead of
|
|
518
|
-
*
|
|
519
|
-
* activeIndex: index of current slide
|
|
520
|
-
* totalItems: total number of slides
|
|
541
|
+
* Use this to render a custom panel navigation element instead of the default StepTracker's based navigation
|
|
542
|
+
* You can make use of `useCarousel` within your custom panel navigation component to hook into various Carousel states such as:
|
|
543
|
+
* - activeIndex: index of current slide
|
|
544
|
+
* - totalItems: total number of slides
|
|
521
545
|
* Use it as follows:
|
|
522
546
|
* ```js
|
|
523
547
|
* <Carousel
|
|
524
|
-
*
|
|
548
|
+
* panelNavigation={<CustomPanelNavigation />}
|
|
525
549
|
* >
|
|
526
550
|
* <Carousel.Item>First Slide</Carousel.Item>
|
|
527
551
|
* </Carousel>
|
|
528
552
|
* ```
|
|
529
553
|
*/
|
|
530
|
-
|
|
554
|
+
panelNavigation: PropTypes.element,
|
|
531
555
|
/**
|
|
532
556
|
* When slide animation start
|
|
533
557
|
* This function is also provided with a parameter indicating the current slide index before animation starts
|
|
@@ -576,7 +600,15 @@ Carousel.propTypes = {
|
|
|
576
600
|
/**
|
|
577
601
|
* Provide custom accessibilityLabel for Carousel container
|
|
578
602
|
*/
|
|
579
|
-
accessibilityLabel: PropTypes.string
|
|
603
|
+
accessibilityLabel: PropTypes.string,
|
|
604
|
+
/**
|
|
605
|
+
* HTML tag to use for the Carousel item's immediate parent. Defaults to `'ul'` so that
|
|
606
|
+
* assistive technology tools know to intepret the carousel as a list.
|
|
607
|
+
*
|
|
608
|
+
* Note that if the immediate Carousel children do not all render as `'li'` elements,
|
|
609
|
+
* this should be changed (e.g. pass tag="div") because only 'li' is a valid child of 'ul'.
|
|
610
|
+
*/
|
|
611
|
+
tag: PropTypes.oneOf(layoutTags)
|
|
580
612
|
}
|
|
581
613
|
|
|
582
614
|
Carousel.Item = CarouselItem
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
+
import { getTokensPropType } from '../utils'
|
|
3
4
|
|
|
4
5
|
const CarouselContext = React.createContext()
|
|
5
6
|
|
|
6
|
-
const CarouselProvider = ({
|
|
7
|
+
const CarouselProvider = ({
|
|
8
|
+
children,
|
|
9
|
+
activeIndex,
|
|
10
|
+
totalItems,
|
|
11
|
+
width,
|
|
12
|
+
goTo,
|
|
13
|
+
getCopyWithPlaceholders,
|
|
14
|
+
themeTokens
|
|
15
|
+
}) => {
|
|
7
16
|
const value = React.useMemo(
|
|
8
|
-
() => ({ activeIndex, totalItems, width, goTo }),
|
|
9
|
-
[activeIndex, totalItems, width, goTo]
|
|
17
|
+
() => ({ activeIndex, totalItems, width, goTo, getCopyWithPlaceholders, themeTokens }),
|
|
18
|
+
[activeIndex, totalItems, width, goTo, getCopyWithPlaceholders, themeTokens]
|
|
10
19
|
)
|
|
11
20
|
return <CarouselContext.Provider value={value}>{children}</CarouselContext.Provider>
|
|
12
21
|
}
|
|
@@ -24,7 +33,9 @@ CarouselProvider.propTypes = {
|
|
|
24
33
|
activeIndex: PropTypes.number.isRequired,
|
|
25
34
|
totalItems: PropTypes.number.isRequired,
|
|
26
35
|
width: PropTypes.number.isRequired,
|
|
27
|
-
goTo: PropTypes.func.isRequired
|
|
36
|
+
goTo: PropTypes.func.isRequired,
|
|
37
|
+
getCopyWithPlaceholders: PropTypes.func.isRequired,
|
|
38
|
+
themeTokens: getTokensPropType('Carousel')
|
|
28
39
|
}
|
|
29
40
|
|
|
30
41
|
export { CarouselProvider, useCarousel }
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
import { View, Platform } from 'react-native'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
layoutTags,
|
|
6
|
+
getA11yPropsFromHtmlTag,
|
|
7
|
+
selectSystemProps,
|
|
8
|
+
a11yProps,
|
|
9
|
+
viewProps
|
|
10
|
+
} from '../../utils'
|
|
5
11
|
import { useCarousel } from '../CarouselContext'
|
|
6
12
|
|
|
7
13
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
@@ -10,17 +16,21 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, vie
|
|
|
10
16
|
* `Carousel.Item` is used to wrap the content of an individual slide and is suppsoed to be the
|
|
11
17
|
* only top-level component passed to the `Carousel`
|
|
12
18
|
*/
|
|
13
|
-
const CarouselItem = ({ children, elementIndex, ...rest }) => {
|
|
14
|
-
const { width, activeIndex
|
|
19
|
+
const CarouselItem = ({ children, elementIndex, tag = 'li', hidden, ...rest }) => {
|
|
20
|
+
const { width, activeIndex } = useCarousel()
|
|
15
21
|
const selectedProps = selectProps({
|
|
16
22
|
...rest,
|
|
17
|
-
|
|
18
|
-
accessibilityRole: Platform.OS === 'android' ? 'none' : 'group',
|
|
19
|
-
accessibilityLabel: `Showing ${elementIndex + 1} of ${totalItems}`
|
|
23
|
+
...getA11yPropsFromHtmlTag(tag, rest.accessibilityRole)
|
|
20
24
|
})
|
|
25
|
+
|
|
21
26
|
const focusabilityProps = activeIndex === elementIndex ? {} : a11yProps.nonFocusableProps
|
|
27
|
+
const style = { width }
|
|
28
|
+
if (hidden && Platform.OS === 'web') {
|
|
29
|
+
// On web, visibility: hidden makes all children non-focusable. It doesn't exist on native.
|
|
30
|
+
style.visibility = 'hidden'
|
|
31
|
+
}
|
|
22
32
|
return (
|
|
23
|
-
<View style={
|
|
33
|
+
<View style={style} {...selectedProps} {...focusabilityProps}>
|
|
24
34
|
{children}
|
|
25
35
|
</View>
|
|
26
36
|
)
|
|
@@ -40,7 +50,15 @@ CarouselItem.propTypes = {
|
|
|
40
50
|
/**
|
|
41
51
|
* Content of the slide
|
|
42
52
|
*/
|
|
43
|
-
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired
|
|
53
|
+
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
|
|
54
|
+
/**
|
|
55
|
+
* Sets the HTML tag of the outer container. By default `'li'` so that assistive technology sees
|
|
56
|
+
* the Carousel as a list of items.
|
|
57
|
+
*
|
|
58
|
+
* Carousel's innermost container defaults to `'ul'` which can be overridden. If the tag of either
|
|
59
|
+
* `Carousel` or `Carousel.Item` is overriden, the other should be too, to avoid producing invalid HTML.
|
|
60
|
+
*/
|
|
61
|
+
tag: PropTypes.oneOf(layoutTags)
|
|
44
62
|
}
|
|
45
63
|
|
|
46
64
|
CarouselItem.displayName = 'Carousel.Item'
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { useCarousel } from '../CarouselContext'
|
|
3
|
+
import StepTracker from '../../StepTracker'
|
|
4
|
+
import StackView from '../../StackView'
|
|
5
|
+
|
|
6
|
+
const CarouselStepTracker = () => {
|
|
7
|
+
const { activeIndex, totalItems, getCopyWithPlaceholders, themeTokens } = useCarousel()
|
|
8
|
+
const stackViewTokens = {
|
|
9
|
+
justifyContent: 'center'
|
|
10
|
+
}
|
|
11
|
+
const stepTrackerTokens = {
|
|
12
|
+
showStepLabel: false,
|
|
13
|
+
showStepTrackerLabel: true,
|
|
14
|
+
knobCompletedBackgroundColor: 'none',
|
|
15
|
+
connectorCompletedColor: 'none',
|
|
16
|
+
connectorColor: 'none',
|
|
17
|
+
containerPaddingTop: themeTokens.spaceBetweenSlideAndPanelNavigation
|
|
18
|
+
}
|
|
19
|
+
const steps = Array.from(Array(totalItems)).map((_, index) => String(index))
|
|
20
|
+
return (
|
|
21
|
+
<StackView direction="row" tokens={stackViewTokens}>
|
|
22
|
+
<StepTracker
|
|
23
|
+
current={activeIndex}
|
|
24
|
+
steps={steps}
|
|
25
|
+
copy={{
|
|
26
|
+
// Give StepTracker copy from Carousel's language and dictionary
|
|
27
|
+
stepLabel: getCopyWithPlaceholders('stepLabel'),
|
|
28
|
+
stepTrackerLabel: getCopyWithPlaceholders('stepTrackerLabel')
|
|
29
|
+
}}
|
|
30
|
+
tokens={stepTrackerTokens}
|
|
31
|
+
/>
|
|
32
|
+
</StackView>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default CarouselStepTracker
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// 'stepLabel' and 'stepTrackerLabel' are passed down to StepTracker
|
|
2
|
+
export default {
|
|
3
|
+
en: {
|
|
4
|
+
carouselLabel: '%{stepCount} items',
|
|
5
|
+
iconButtonLabel: 'Show %{itemLabel} %{targetStep} of %{stepCount}',
|
|
6
|
+
stepLabel: '%{itemLabel} %{stepNumber}',
|
|
7
|
+
stepTrackerLabel: '%{itemLabel} %{stepNumber} of %{stepCount}'
|
|
8
|
+
},
|
|
9
|
+
fr: {
|
|
10
|
+
// TODO: French translations here
|
|
11
|
+
carouselLabel: '(fr) %{stepCount} items',
|
|
12
|
+
iconButtonLabel: '(fr) Show %{itemLabel} %{targetStep} of %{stepCount}',
|
|
13
|
+
stepLabel: '(fr) %{itemLabel} %{stepNumber}',
|
|
14
|
+
stepTrackerLabel: '(fr) %{itemLabel} %{stepNumber} of %{stepCount}'
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -6,7 +6,12 @@ import CheckboxInput from './CheckboxInput'
|
|
|
6
6
|
import CheckboxLabel from '../InputLabel/LabelContent'
|
|
7
7
|
import Feedback from '../Feedback'
|
|
8
8
|
import StackView from '../StackView'
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
applyShadowToken,
|
|
11
|
+
applyTextStyles,
|
|
12
|
+
useTheme,
|
|
13
|
+
useThemeTokensCallback
|
|
14
|
+
} from '../ThemeProvider'
|
|
10
15
|
import {
|
|
11
16
|
a11yProps,
|
|
12
17
|
focusHandlerProps,
|
|
@@ -54,21 +59,18 @@ const selectInputStyles = (
|
|
|
54
59
|
}
|
|
55
60
|
})
|
|
56
61
|
})
|
|
57
|
-
const selectLabelStyles = (
|
|
58
|
-
labelColor,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
labelFontWeight,
|
|
62
|
-
labelMarginLeft,
|
|
63
|
-
labelLineHeight
|
|
64
|
-
}) => ({
|
|
62
|
+
const selectLabelStyles = (
|
|
63
|
+
{ labelColor, labelFontName, labelFontSize, labelFontWeight, labelMarginLeft, labelLineHeight },
|
|
64
|
+
themeOptions
|
|
65
|
+
) => ({
|
|
65
66
|
marginLeft: labelMarginLeft,
|
|
66
67
|
...applyTextStyles({
|
|
67
68
|
color: labelColor,
|
|
68
69
|
fontName: labelFontName,
|
|
69
70
|
fontWeight: labelFontWeight,
|
|
70
71
|
fontSize: labelFontSize,
|
|
71
|
-
lineHeight: labelLineHeight
|
|
72
|
+
lineHeight: labelLineHeight,
|
|
73
|
+
themeOptions
|
|
72
74
|
})
|
|
73
75
|
})
|
|
74
76
|
const selectIconTokens = ({ icon, iconColor, iconSize }) => ({
|
|
@@ -172,6 +174,7 @@ const Checkbox = forwardRef(
|
|
|
172
174
|
}
|
|
173
175
|
const uniqueId = useUniqueId('checkbox')
|
|
174
176
|
const inputId = id ?? uniqueId
|
|
177
|
+
const { themeOptions } = useTheme()
|
|
175
178
|
|
|
176
179
|
return (
|
|
177
180
|
<View style={staticStyles.wrapper} ref={ref}>
|
|
@@ -187,7 +190,7 @@ const Checkbox = forwardRef(
|
|
|
187
190
|
{({ focused: focus, hovered: hover, pressed }) => {
|
|
188
191
|
const { icon: IconComponent, ...stateTokens } = getTokens({ focus, hover, pressed })
|
|
189
192
|
const iconTokens = selectIconTokens(stateTokens)
|
|
190
|
-
const labelStyles = selectLabelStyles(stateTokens)
|
|
193
|
+
const labelStyles = selectLabelStyles(stateTokens, themeOptions)
|
|
191
194
|
const alignWithLabel = label
|
|
192
195
|
? [staticStyles.alignWithLabel, { height: labelStyles.lineHeight }]
|
|
193
196
|
: null
|
|
@@ -61,7 +61,7 @@ const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
|
|
|
61
61
|
* @example
|
|
62
62
|
* ```jsx
|
|
63
63
|
* <CheckboxGroup
|
|
64
|
-
*
|
|
64
|
+
* initialCheckedIds="check1"
|
|
65
65
|
* items={[
|
|
66
66
|
* { label: 'Checkbox 1', id: 'check1' },
|
|
67
67
|
* { label: 'Checkbox 2', id: 'check2' },
|
|
@@ -2,7 +2,7 @@ import React, { forwardRef } from 'react'
|
|
|
2
2
|
import { StyleSheet, Text, View } from 'react-native'
|
|
3
3
|
import PropTypes from 'prop-types'
|
|
4
4
|
|
|
5
|
-
import { applyTextStyles, useThemeTokens } from '../ThemeProvider'
|
|
5
|
+
import { applyTextStyles, useTheme, useThemeTokens } from '../ThemeProvider'
|
|
6
6
|
import {
|
|
7
7
|
a11yProps,
|
|
8
8
|
getTokensPropType,
|
|
@@ -17,10 +17,16 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, vie
|
|
|
17
17
|
|
|
18
18
|
const selectStyles = (tokens) => selectTokens('Feedback', tokens)
|
|
19
19
|
|
|
20
|
-
const selectTitleTextStyles = ({ titleFontSize, ...tokens }) =>
|
|
21
|
-
applyTextStyles(
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
const selectTitleTextStyles = ({ titleFontSize, ...tokens }, themeOptions) =>
|
|
21
|
+
applyTextStyles({
|
|
22
|
+
...selectTokens('Typography', { ...tokens, fontSize: titleFontSize, themeOptions }),
|
|
23
|
+
themeOptions
|
|
24
|
+
})
|
|
25
|
+
const selectContentTextStyles = ({ contentFontSize, ...tokens }, themeOptions) =>
|
|
26
|
+
applyTextStyles({
|
|
27
|
+
...selectTokens('Typography', { ...tokens, fontSize: contentFontSize }),
|
|
28
|
+
themeOptions
|
|
29
|
+
})
|
|
24
30
|
|
|
25
31
|
const selectIconTokens = ({ iconSize, iconColor }) => ({
|
|
26
32
|
size: iconSize,
|
|
@@ -56,8 +62,9 @@ const Feedback = forwardRef(
|
|
|
56
62
|
|
|
57
63
|
const { icon: IconComponent } = themeTokens
|
|
58
64
|
|
|
59
|
-
const
|
|
60
|
-
const
|
|
65
|
+
const { themeOptions } = useTheme()
|
|
66
|
+
const titleTextStyles = selectTitleTextStyles(themeTokens, themeOptions)
|
|
67
|
+
const contentTextStyles = selectContentTextStyles(themeTokens, themeOptions)
|
|
61
68
|
|
|
62
69
|
const content =
|
|
63
70
|
typeof children === 'string' ? <Text style={contentTextStyles}>{children}</Text> : children
|
package/src/Icon/IconText.jsx
CHANGED
|
@@ -39,6 +39,7 @@ const IconText = forwardRef(
|
|
|
39
39
|
IconText.displayName = 'IconText'
|
|
40
40
|
|
|
41
41
|
IconText.propTypes = {
|
|
42
|
+
/* eslint-disable react/no-unused-prop-types */ // eslint is having hard time seeing these props through forwardRef
|
|
42
43
|
/**
|
|
43
44
|
* Amount of space to separate the text content and icon. Uses the themes's spacing scale
|
|
44
45
|
* (see useSpacingScale for more info).
|
|
@@ -63,6 +64,7 @@ IconText.propTypes = {
|
|
|
63
64
|
* `<Typography>` component, or a component that renders `<Text>`.
|
|
64
65
|
*/
|
|
65
66
|
children: PropTypes.node
|
|
67
|
+
/* eslint-enable react/no-unused-prop-types */
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
export default IconText
|