@telus-uds/components-base 1.8.5 → 1.11.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 (69) hide show
  1. package/CHANGELOG.md +47 -2
  2. package/component-docs.json +666 -27
  3. package/lib/Card/Card.js +9 -4
  4. package/lib/Carousel/Carousel.js +672 -0
  5. package/lib/Carousel/CarouselContext.js +59 -0
  6. package/lib/Carousel/CarouselItem/CarouselItem.js +92 -0
  7. package/lib/Carousel/CarouselItem/index.js +13 -0
  8. package/lib/Carousel/dictionary.js +23 -0
  9. package/lib/Carousel/index.js +32 -0
  10. package/lib/ExpandCollapse/Panel.js +10 -1
  11. package/lib/InputSupports/InputSupports.js +10 -3
  12. package/lib/InputSupports/useInputSupports.js +3 -2
  13. package/lib/Modal/Modal.js +4 -0
  14. package/lib/Skeleton/Skeleton.js +1 -0
  15. package/lib/StepTracker/StepTracker.js +15 -12
  16. package/lib/TextInput/TextInput.js +3 -12
  17. package/lib/TextInput/TextInputBase.js +9 -0
  18. package/lib/TextInput/propTypes.js +3 -8
  19. package/lib/index.js +23 -0
  20. package/lib/utils/props/clickProps.js +2 -2
  21. package/lib/utils/props/handlerProps.js +77 -31
  22. package/lib/utils/props/textInputProps.js +8 -1
  23. package/lib/utils/useScrollBlocking.js +66 -0
  24. package/lib/utils/useScrollBlocking.native.js +11 -0
  25. package/lib-module/Card/Card.js +5 -4
  26. package/lib-module/Carousel/Carousel.js +617 -0
  27. package/lib-module/Carousel/CarouselContext.js +43 -0
  28. package/lib-module/Carousel/CarouselItem/CarouselItem.js +75 -0
  29. package/lib-module/Carousel/CarouselItem/index.js +2 -0
  30. package/lib-module/Carousel/dictionary.js +16 -0
  31. package/lib-module/Carousel/index.js +2 -0
  32. package/lib-module/ExpandCollapse/Panel.js +9 -1
  33. package/lib-module/InputSupports/InputSupports.js +10 -3
  34. package/lib-module/InputSupports/useInputSupports.js +3 -2
  35. package/lib-module/Modal/Modal.js +3 -0
  36. package/lib-module/Skeleton/Skeleton.js +1 -0
  37. package/lib-module/StepTracker/StepTracker.js +14 -12
  38. package/lib-module/TextInput/TextInput.js +3 -9
  39. package/lib-module/TextInput/TextInputBase.js +10 -1
  40. package/lib-module/TextInput/propTypes.js +4 -8
  41. package/lib-module/index.js +2 -0
  42. package/lib-module/utils/props/clickProps.js +2 -2
  43. package/lib-module/utils/props/handlerProps.js +78 -31
  44. package/lib-module/utils/props/textInputProps.js +8 -1
  45. package/lib-module/utils/useScrollBlocking.js +58 -0
  46. package/lib-module/utils/useScrollBlocking.native.js +2 -0
  47. package/package.json +3 -3
  48. package/src/Card/Card.jsx +6 -4
  49. package/src/Carousel/Carousel.jsx +649 -0
  50. package/src/Carousel/CarouselContext.jsx +30 -0
  51. package/src/Carousel/CarouselItem/CarouselItem.jsx +66 -0
  52. package/src/Carousel/CarouselItem/index.js +3 -0
  53. package/src/Carousel/dictionary.js +16 -0
  54. package/src/Carousel/index.js +2 -0
  55. package/src/ExpandCollapse/Panel.jsx +8 -1
  56. package/src/InputSupports/InputSupports.jsx +18 -3
  57. package/src/InputSupports/useInputSupports.js +2 -2
  58. package/src/Modal/Modal.jsx +3 -1
  59. package/src/Skeleton/Skeleton.jsx +1 -0
  60. package/src/StepTracker/StepTracker.jsx +21 -8
  61. package/src/TextInput/TextInput.jsx +2 -9
  62. package/src/TextInput/TextInputBase.jsx +11 -1
  63. package/src/TextInput/propTypes.js +3 -7
  64. package/src/index.js +2 -0
  65. package/src/utils/props/clickProps.js +2 -2
  66. package/src/utils/props/handlerProps.js +64 -16
  67. package/src/utils/props/textInputProps.js +7 -1
  68. package/src/utils/useScrollBlocking.js +57 -0
  69. package/src/utils/useScrollBlocking.native.js +2 -0
@@ -0,0 +1,617 @@
1
+ import React from 'react';
2
+ import View from "react-native-web/dist/exports/View";
3
+ import Animated from "react-native-web/dist/exports/Animated";
4
+ import PanResponder from "react-native-web/dist/exports/PanResponder";
5
+ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
6
+ import Platform from "react-native-web/dist/exports/Platform";
7
+ import PropTypes from 'prop-types';
8
+ import { useThemeTokens } from '../ThemeProvider';
9
+ import { useViewport } from '../ViewportProvider';
10
+ import { getTokensPropType, getA11yPropsFromHtmlTag, layoutTags, variantProp, selectSystemProps, a11yProps, viewProps, useCopy } from '../utils';
11
+ import { useA11yInfo } from '../A11yInfoProvider';
12
+ import { CarouselProvider } from './CarouselContext';
13
+ import CarouselItem from './CarouselItem';
14
+ import StepTracker from '../StepTracker';
15
+ import StackView from '../StackView';
16
+ import IconButton from '../IconButton';
17
+ import dictionary from './dictionary';
18
+ import { jsx as _jsx } from "react/jsx-runtime";
19
+ import { jsxs as _jsxs } from "react/jsx-runtime";
20
+ const staticStyles = StyleSheet.create({
21
+ root: {
22
+ backgroundColor: 'transparent',
23
+ justifyContent: 'center',
24
+ alignItems: 'center',
25
+ position: 'relative',
26
+ top: 0,
27
+ left: 0
28
+ }
29
+ });
30
+ const staticTokens = {
31
+ stackView: {
32
+ justifyContent: 'center'
33
+ },
34
+ stepTracker: {
35
+ showStepLabel: false,
36
+ showStepTrackerLabel: true,
37
+ knobCompletedBackgroundColor: 'none',
38
+ connectorCompletedColor: 'none',
39
+ connectorColor: 'none'
40
+ }
41
+ };
42
+
43
+ const selectContainerStyles = width => ({
44
+ backgroundColor: 'transparent',
45
+ overflow: 'hidden',
46
+ width
47
+ });
48
+
49
+ const selectSwipeAreaStyles = (count, width) => ({
50
+ width: width * count,
51
+ justifyContent: 'space-between',
52
+ flexDirection: 'row'
53
+ });
54
+
55
+ const selectPreviousNextNavigationButtonStyles = (previousNextNavigationButtonWidth, previousNextNavigationPosition, spaceBetweenSlideAndPreviousNextNavigation, isFirstSlide, isLastSlide, areStylesAppliedOnPreviousButton) => {
56
+ const styles = {
57
+ zIndex: 1,
58
+ position: 'absolute'
59
+ };
60
+ const dynamicPositionProperty = areStylesAppliedOnPreviousButton ? 'left' : 'right';
61
+
62
+ if (isFirstSlide) {
63
+ styles.visibility = areStylesAppliedOnPreviousButton ? 'hidden' : 'visible';
64
+ } else if (isLastSlide) {
65
+ styles.visibility = areStylesAppliedOnPreviousButton ? 'visible' : 'hidden';
66
+ } else {
67
+ styles.visibility = 'visible';
68
+ }
69
+
70
+ if (previousNextNavigationPosition === 'edge') {
71
+ styles[dynamicPositionProperty] = -1 * (previousNextNavigationButtonWidth / 2);
72
+ } else if (previousNextNavigationPosition === 'inside') {
73
+ styles[dynamicPositionProperty] = 0;
74
+ } else if (previousNextNavigationPosition === 'outside') {
75
+ styles[dynamicPositionProperty] = -1 * (spaceBetweenSlideAndPreviousNextNavigation + previousNextNavigationButtonWidth);
76
+ }
77
+
78
+ return styles;
79
+ };
80
+
81
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
82
+ /**
83
+ * Carousel is a general-purpose content slider that can be used to render content in terms of slides.
84
+
85
+ ## Usage
86
+ - `Carousel` is a top-level export from `@telus-uds/components-base` which is used to render a Carousel
87
+ - Immediately within `Carousel`, individual slides are wrapped in `Carousel.Item` for the top-level `Carousel` to know how to identify an individual slide
88
+ - You can use any UDS component or other platform-specific component, (based on the platform you're rendering) to achieve any desired layout
89
+ - By default, Carousel takea all the `width` available to it and the `height` is determined based on the content in the slide with more content
90
+ - You may want to wrap Carousel in other layout components like `Box`, `FlexGrid` etc, to achieve a responsive layout of your need
91
+
92
+ ## `useCarousel` custom hook
93
+
94
+ ```jsx
95
+ import { useCarousel } from '@telus-uds/components-base'
96
+
97
+ const SomeComponentWithinCarouselItem = () => {
98
+ const {
99
+ activeIndex,
100
+ totalItems,
101
+ width,
102
+ goTo
103
+ } = useCarousel()
104
+ return <Text>Hi!</Text>
105
+ }
106
+ ```
107
+
108
+ You can use `useCarousel` to hook into internal state of the Carousel component like:
109
+ - `activeIndex`: Index of the current slide
110
+ - `totalItems`: Total number of items/slides passed to the Carousel
111
+ - `width`: Width of the individual carousel slide
112
+ - `goTo`: A function to go to a particular slide by passing the index of that slide, e.g: goTo(0) where `0` is the index of the first slide
113
+
114
+ ## Accessibility
115
+
116
+ - Top-level `Carousel` and `Carousel.Item` can take all possible React Native's `View` and `a11y` props
117
+ - If your slide contains input elements like buttons, you may want to configure them to be only focusable when `activeIndex` is equal to the current slide index in order to avoid tabbing going between slides
118
+
119
+ ## Platform considerations
120
+ The component is available on both native platforms and web.
121
+
122
+ ## Other considerations
123
+ - You may want to use the same kind of layout in all your slides to avoid visual and height differences
124
+ - `previous` and `next` navigation buttons are automatically removed in `sm` and `xs` viewports, as these smaller viewports offers swipe functionality
125
+
126
+ ## Tokens
127
+
128
+ You can override the following tokens in exceptional circumstances:
129
+ - `previousIcon` - Icon of the previous button
130
+ - `nextIcon` - Icon of the next button
131
+ - `showPreviousNextNavigation` - If you want to show/hide the previous/next navigation
132
+ - `showPanelNavigation` - If you want to show/hide the panel navigation
133
+ - `spaceBetweenSlideAndPreviousNextNavigation` - Horizontal space between slide and previous/next navigational buttons
134
+ - `spaceBetweenSlideAndPanelNavigation` - Vertical space between slide area and panel navigation area
135
+ */
136
+
137
+ const Carousel = /*#__PURE__*/React.forwardRef((_ref, ref) => {
138
+ let {
139
+ tokens,
140
+ variant,
141
+ children,
142
+ itemLabel = 'item',
143
+ previousNextNavigationPosition = 'inside',
144
+ previousNextIconSize = 'default',
145
+ minDistanceToCapture = 5,
146
+ minDistanceForAction = 0.2,
147
+ onAnimationStart,
148
+ onAnimationEnd,
149
+ onIndexChanged,
150
+ springConfig = undefined,
151
+ onRenderPanelNavigation,
152
+ tag = 'ul',
153
+ accessibilityRole = 'adjustable',
154
+ accessibilityLabel = 'carousel',
155
+ copy,
156
+ ...rest
157
+ } = _ref;
158
+ const viewport = useViewport();
159
+ const {
160
+ previousIcon,
161
+ nextIcon,
162
+ showPreviousNextNavigation,
163
+ showPanelNavigation,
164
+ spaceBetweenSlideAndPreviousNextNavigation,
165
+ spaceBetweenSlideAndPanelNavigation
166
+ } = useThemeTokens('Carousel', tokens, variant, {
167
+ viewport
168
+ });
169
+ const [activeIndex, setActiveIndex] = React.useState(0);
170
+ const [isAnimating, setIsAnimating] = React.useState(false);
171
+ const handleAnimationStart = React.useCallback(function () {
172
+ if (typeof onAnimationStart === 'function') onAnimationStart(...arguments);
173
+ setIsAnimating(true);
174
+ }, [onAnimationStart]);
175
+ const handleAnimationEnd = React.useCallback(function () {
176
+ if (typeof onAnimationEnd === 'function') onAnimationEnd(...arguments);
177
+ setIsAnimating(false);
178
+ }, [onAnimationEnd]);
179
+ const getCopy = useCopy({
180
+ dictionary,
181
+ copy
182
+ });
183
+ const childrenArray = React.Children.toArray(children);
184
+ const systemProps = selectProps({ ...rest,
185
+ accessibilityRole,
186
+ accessibilityLabel,
187
+ accessibilityValue: {
188
+ min: 1,
189
+ max: childrenArray.length,
190
+ now: activeIndex + 1
191
+ }
192
+ });
193
+ const {
194
+ reduceMotionEnabled
195
+ } = useA11yInfo();
196
+ const [containerLayout, setContainerLayout] = React.useState({
197
+ x: 0,
198
+ y: 0,
199
+ width: 0
200
+ });
201
+ const [previousNextNavigationButtonWidth, setPreviousNextNavigationButtonWidth] = React.useState(0);
202
+ const pan = React.useRef(new Animated.ValueXY()).current;
203
+ const animatedX = React.useRef(0);
204
+ const animatedY = React.useRef(0);
205
+ const isFirstSlide = !activeIndex;
206
+ const isLastSlide = activeIndex + 1 >= children.length;
207
+ const panelNavigationTokens = { ...staticTokens.stepTracker,
208
+ containerPaddingTop: spaceBetweenSlideAndPanelNavigation
209
+ };
210
+
211
+ const onContainerLayout = _ref2 => {
212
+ let {
213
+ nativeEvent: {
214
+ layout: {
215
+ x,
216
+ y,
217
+ width
218
+ }
219
+ }
220
+ } = _ref2;
221
+ return setContainerLayout(prevState => ({ ...prevState,
222
+ x,
223
+ y,
224
+ width
225
+ }));
226
+ };
227
+
228
+ const onPreviousNextNavigationButtonLayout = _ref3 => {
229
+ let {
230
+ nativeEvent: {
231
+ layout: {
232
+ width
233
+ }
234
+ }
235
+ } = _ref3;
236
+ return setPreviousNextNavigationButtonWidth(width);
237
+ };
238
+
239
+ const updateOffset = React.useCallback(() => {
240
+ animatedX.current = containerLayout.width * activeIndex * -1;
241
+ animatedY.current = 0;
242
+ pan.setOffset({
243
+ x: animatedX.current,
244
+ y: animatedY.current
245
+ });
246
+ pan.setValue({
247
+ x: 0,
248
+ y: 0
249
+ });
250
+ }, [activeIndex, containerLayout.width, pan, animatedX]);
251
+ const animate = React.useCallback((toValue, toIndex) => {
252
+ const handleAnimationEndToIndex = function () {
253
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
254
+ args[_key] = arguments[_key];
255
+ }
256
+
257
+ return handleAnimationEnd(toIndex, ...args);
258
+ };
259
+
260
+ if (reduceMotionEnabled) {
261
+ Animated.timing(pan, {
262
+ toValue,
263
+ duration: 1,
264
+ useNativeDriver: false
265
+ }).start(handleAnimationEndToIndex);
266
+ } else {
267
+ Animated.spring(pan, { ...springConfig,
268
+ toValue,
269
+ useNativeDriver: false
270
+ }).start(handleAnimationEndToIndex);
271
+ }
272
+ }, [pan, springConfig, reduceMotionEnabled, handleAnimationEnd]);
273
+ const updateIndex = React.useCallback(function () {
274
+ let delta = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
275
+ const toValue = {
276
+ x: 0,
277
+ y: 0
278
+ };
279
+ let skipChanges = !delta;
280
+ let calcDelta = delta;
281
+
282
+ if (activeIndex <= 0 && delta < 0) {
283
+ skipChanges = true;
284
+ calcDelta = children.length + delta;
285
+ } else if (activeIndex + 1 >= children.length && delta > 0) {
286
+ skipChanges = true;
287
+ calcDelta = -1 * activeIndex + delta - 1;
288
+ }
289
+
290
+ const index = activeIndex + calcDelta;
291
+
292
+ if (skipChanges) {
293
+ animate(toValue, index);
294
+ return calcDelta;
295
+ }
296
+
297
+ setActiveIndex(index);
298
+ toValue.x = containerLayout.width * -1 * calcDelta;
299
+ animate(toValue, index);
300
+ if (onIndexChanged) onIndexChanged(calcDelta);
301
+ return calcDelta;
302
+ }, [containerLayout.width, activeIndex, animate, children.length, onIndexChanged]);
303
+ const fixOffsetAndGo = React.useCallback(delta => {
304
+ updateOffset();
305
+ handleAnimationStart(activeIndex);
306
+ updateIndex(delta);
307
+ }, [updateIndex, updateOffset, activeIndex, handleAnimationStart]);
308
+ const goToNeighboring = React.useCallback(function () {
309
+ let toPrev = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
310
+ fixOffsetAndGo(toPrev ? -1 : 1);
311
+ }, [fixOffsetAndGo]);
312
+ const isSwipeAllowed = React.useCallback(() => {
313
+ if (Platform.OS === 'web') {
314
+ return !!(viewport === 'xs' || viewport === 'sm');
315
+ }
316
+
317
+ return true;
318
+ }, [viewport]);
319
+ const panResponder = React.useMemo(() => PanResponder.create({
320
+ onPanResponderTerminationRequest: () => false,
321
+ onMoveShouldSetResponderCapture: () => true,
322
+ onMoveShouldSetPanResponderCapture: (_, gestureState) => {
323
+ if (!isSwipeAllowed()) {
324
+ return false;
325
+ }
326
+
327
+ handleAnimationStart(activeIndex);
328
+ return Math.abs(gestureState.dx) > minDistanceToCapture;
329
+ },
330
+ onPanResponderGrant: () => updateOffset(),
331
+ onPanResponderMove: Animated.event([null, {
332
+ dx: pan.x
333
+ }], {
334
+ useNativeDriver: false
335
+ }),
336
+ onPanResponderRelease: (_, gesture) => {
337
+ const correction = gesture.moveX - gesture.x0;
338
+
339
+ if (Math.abs(correction) < containerLayout.width * minDistanceForAction) {
340
+ animate({
341
+ x: 0,
342
+ y: 0
343
+ }, 0);
344
+ } else {
345
+ const delta = correction > 0 ? -1 : 1;
346
+ updateIndex(delta);
347
+ }
348
+ }
349
+ }), [containerLayout.width, updateIndex, updateOffset, animate, isSwipeAllowed, activeIndex, minDistanceForAction, handleAnimationStart, minDistanceToCapture, pan.x]);
350
+ React.useEffect(() => {
351
+ pan.x.addListener(_ref4 => {
352
+ let {
353
+ value
354
+ } = _ref4;
355
+ animatedX.current = value;
356
+ });
357
+ pan.y.addListener(_ref5 => {
358
+ let {
359
+ value
360
+ } = _ref5;
361
+ animatedY.current = value;
362
+ });
363
+ return () => {
364
+ pan.x.removeAllListeners();
365
+ pan.y.removeAllListeners();
366
+ };
367
+ }, [pan.x, pan.y]);
368
+ const goToNext = React.useCallback(() => {
369
+ goToNeighboring();
370
+ }, [goToNeighboring]);
371
+ const goToPrev = React.useCallback(() => {
372
+ goToNeighboring(true);
373
+ }, [goToNeighboring]);
374
+ const goTo = React.useCallback(function () {
375
+ let index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
376
+ const delta = index - activeIndex;
377
+
378
+ if (delta) {
379
+ fixOffsetAndGo(delta);
380
+ }
381
+ }, [fixOffsetAndGo, activeIndex]); // @TODO: - these are Allium-theme variants and won't have any effect in themes that don't implement them.
382
+ // Normally we avoid setting variants of subcomponents, however this could be re-considered.
383
+ // Related discussion - https://github.com/telus/universal-design-system/issues/1549
384
+
385
+ const previousNextIconButtonVariants = {
386
+ size: previousNextIconSize,
387
+ raised: true
388
+ };
389
+
390
+ const getCopyWithPlaceholders = copyKey => {
391
+ const copyText = getCopy(copyKey).replace(/%\{itemLabel\}/g, itemLabel).replace(/%\{stepNumber\}/g, activeIndex + 1).replace(/%\{stepCount\}/g, childrenArray.length); // First word might be a lowercase placeholder: capitalize the first letter
392
+
393
+ return "".concat(copyText[0].toUpperCase()).concat(copyText.slice(1));
394
+ };
395
+
396
+ return /*#__PURE__*/_jsxs(CarouselProvider, {
397
+ activeIndex: activeIndex,
398
+ totalItems: childrenArray.length,
399
+ width: containerLayout.width,
400
+ goTo: goTo,
401
+ children: [/*#__PURE__*/_jsxs(View, {
402
+ style: staticStyles.root,
403
+ onLayout: onContainerLayout,
404
+ ref: ref,
405
+ ...systemProps,
406
+ children: [showPreviousNextNavigation && /*#__PURE__*/_jsx(View, {
407
+ style: selectPreviousNextNavigationButtonStyles(previousNextNavigationButtonWidth, previousNextNavigationPosition, spaceBetweenSlideAndPreviousNextNavigation, isFirstSlide, isLastSlide, true),
408
+ testID: "previous-button-container",
409
+ children: /*#__PURE__*/_jsx(IconButton, {
410
+ onLayout: onPreviousNextNavigationButtonLayout,
411
+ icon: previousIcon,
412
+ onPress: goToPrev,
413
+ variant: previousNextIconButtonVariants,
414
+ accessibilityLabel: getCopyWithPlaceholders('iconButtonLabel').replace('%{targetStep}', activeIndex)
415
+ })
416
+ }), /*#__PURE__*/_jsx(View, {
417
+ style: selectContainerStyles(containerLayout.width),
418
+ children: /*#__PURE__*/_jsx(Animated.View, {
419
+ style: StyleSheet.flatten([selectSwipeAreaStyles(children.length, containerLayout.width), {
420
+ transform: [{
421
+ translateX: pan.x
422
+ }, {
423
+ translateY: pan.y
424
+ }]
425
+ }]),
426
+ ...panResponder.panHandlers,
427
+ ...getA11yPropsFromHtmlTag(tag),
428
+ children: childrenArray.map((element, index) => {
429
+ const hidden = !isAnimating && index !== activeIndex;
430
+ const clonedElement = /*#__PURE__*/React.cloneElement(element, {
431
+ elementIndex: index,
432
+ hidden
433
+ });
434
+ return /*#__PURE__*/_jsx(React.Fragment, {
435
+ children: clonedElement
436
+ }, index.toFixed(2));
437
+ })
438
+ })
439
+ }), showPreviousNextNavigation && /*#__PURE__*/_jsx(View, {
440
+ style: selectPreviousNextNavigationButtonStyles(previousNextNavigationButtonWidth, previousNextNavigationPosition, spaceBetweenSlideAndPreviousNextNavigation, isFirstSlide, isLastSlide, false),
441
+ testID: "next-button-container",
442
+ children: /*#__PURE__*/_jsx(IconButton, {
443
+ onLayout: onPreviousNextNavigationButtonLayout,
444
+ icon: nextIcon,
445
+ onPress: goToNext,
446
+ variant: previousNextIconButtonVariants,
447
+ accessibilityLabel: getCopyWithPlaceholders('iconButtonLabel').replace('%{targetStep}', activeIndex + 2)
448
+ })
449
+ })]
450
+ }), showPanelNavigation ? /*#__PURE__*/_jsx(StackView, {
451
+ direction: "row",
452
+ tokens: staticTokens.stackView,
453
+ children: onRenderPanelNavigation ? onRenderPanelNavigation({
454
+ activeIndex,
455
+ totalItems: childrenArray.length
456
+ }) : /*#__PURE__*/_jsx(StepTracker, {
457
+ current: activeIndex,
458
+ steps: childrenArray.map((_, index) => String(index)),
459
+ copy: {
460
+ // Give StepTracker copy from Carousel's language and dictionary
461
+ stepLabel: getCopyWithPlaceholders('stepLabel'),
462
+ stepTrackerLabel: getCopyWithPlaceholders('stepTrackerLabel')
463
+ },
464
+ tokens: panelNavigationTokens
465
+ })
466
+ }) : null]
467
+ });
468
+ });
469
+ Carousel.propTypes = { ...selectedSystemPropTypes,
470
+ tokens: getTokensPropType('Carousel'),
471
+ variant: variantProp.propType,
472
+
473
+ /**
474
+ * Slides to render in Carousel. Wrap individual slides in `Carousel.Item`
475
+ */
476
+ children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
477
+
478
+ /**
479
+ * Lowercase language-appropriate user-facing description of what each Carousel slide represents.
480
+ * This is used when generating item labels. For example, if a carousel contains offers,
481
+ * pass itemLabel="summer offer" (or copy="fr" and an appropriate French translation) to genereate
482
+ * accessible labels such as "Summer offer 1 of 3" and "Show summer offer 2 of 3".
483
+ */
484
+ itemLabel: PropTypes.string,
485
+
486
+ /**
487
+ * `inside` renders the previous and next buttons inside the slide
488
+ * `outside` renders the previous and next buttons outside the slide
489
+ * `edge` renders the previous and next buttons at the edge of the slide
490
+ */
491
+ previousNextNavigationPosition: PropTypes.oneOf(['inside', 'outside', 'edge']),
492
+
493
+ /**
494
+ * Defines the size of the `IconButton` which is being used to render next and previous buttons
495
+ */
496
+ previousNextIconSize: PropTypes.oneOf(['default', 'small', 'large']),
497
+
498
+ /**
499
+ * Carousel uses `Animated.spring` to animate slide changes, use this option to pass custom animation configuration
500
+ */
501
+ springConfig: PropTypes.object,
502
+
503
+ /**
504
+ * Minimal part of slide width must be swiped for changing index.
505
+ * Otherwise animation restore current slide. Default value 0.2 means that 20% must be swiped for change index
506
+ */
507
+ minDistanceForAction: PropTypes.number,
508
+
509
+ /**
510
+ * Initiate animation after swipe this distance.
511
+ */
512
+ minDistanceToCapture: PropTypes.number,
513
+
514
+ /**
515
+ * Called when active index changed
516
+ * This function is also provided with a parameter indicating changed index (either 1, or -1)
517
+ * Use it as follows:
518
+ * ```js
519
+ * const onIndexChangedCallback = React.useCallback((changedIndex) => {
520
+ * console.log(changedIndex)
521
+ * }, []) // pass local dependencies as per your component
522
+ * <Carousel
523
+ * onIndexChanged={onIndexChangedCallback}
524
+ * >
525
+ * <Carousel.Item>First Slide</Carousel.Item>
526
+ * </Carousel>
527
+ * ```
528
+ * Caution: Always consider wrapping your callback for `onIndexChanged` in `useCallback` in order to avoid bugs and performance issues
529
+ */
530
+ onIndexChanged: PropTypes.func,
531
+
532
+ /**
533
+ * Use this to render a custom panel navigation element instead of dots navigation
534
+ * This function is also provided with an object with the following properties
535
+ * activeIndex: index of current slide
536
+ * totalItems: total number of slides
537
+ * Use it as follows:
538
+ * ```js
539
+ * <Carousel
540
+ * onRenderPanelNavigation={({ totalItems, activeIndex }) => <Text>Showing {activeIndex + 1}</Text>}
541
+ * >
542
+ * <Carousel.Item>First Slide</Carousel.Item>
543
+ * </Carousel>
544
+ * ```
545
+ */
546
+ onRenderPanelNavigation: PropTypes.func,
547
+
548
+ /**
549
+ * When slide animation start
550
+ * This function is also provided with a parameter indicating the current slide index before animation starts
551
+ * Use it as follows:
552
+ * ```js
553
+ * const onAnimationStartCallback = React.useCallback((currentIndex) => {
554
+ * console.log(currentIndex)
555
+ * }, []) // pass local dependencies as per your component
556
+ * <Carousel
557
+ * onAnimationStart={onAnimationStartCallback}
558
+ * >
559
+ * <Carousel.Item>First Slide</Carousel.Item>
560
+ * </Carousel>
561
+ * ```
562
+ * Caution: Always consider wrapping your callback for `onAnimationStart` in `useCallback` in order to avoid bugs and performance issues
563
+ */
564
+ onAnimationStart: PropTypes.func,
565
+
566
+ /**
567
+ * When slide animation end with parameter of current index (after animation ends)
568
+ * This function is also provided with a parameter indicating the updated slide index after animation ends
569
+ * Use it as follows:
570
+ * ```js
571
+ * const onAnimationEndCallback = React.useCallback((changedIndex) => {
572
+ * console.log(changedIndex)
573
+ * }, []) // pass local dependencies as per your component
574
+ * <Carousel
575
+ * onAnimationEnd={onAnimationEndCallback}
576
+ * >
577
+ * <Carousel.Item>First Slide</Carousel.Item>
578
+ * </Carousel>
579
+ * ```
580
+ * Caution: Always consider wrapping your callback for `onAnimationEnd` in `useCallback` in order to avoid bugs and performance issues
581
+ */
582
+ onAnimationEnd: PropTypes.func,
583
+
584
+ /**
585
+ * Use this to override the default text for panel navigation
586
+ */
587
+ panelNavigationTextDictionary: PropTypes.shape({
588
+ en: PropTypes.shape({
589
+ stepTrackerLabel: PropTypes.string.isRequired
590
+ }),
591
+ fr: PropTypes.shape({
592
+ stepTrackerLabel: PropTypes.string.isRequired
593
+ })
594
+ }),
595
+
596
+ /**
597
+ * Provide custom accessibilityRole for Carousel container
598
+ */
599
+ accessibilityRole: PropTypes.string,
600
+
601
+ /**
602
+ * Provide custom accessibilityLabel for Carousel container
603
+ */
604
+ accessibilityLabel: PropTypes.string,
605
+
606
+ /**
607
+ * HTML tag to use for the Carousel item's immediate parent. Defaults to `'ul'` so that
608
+ * assistive technology tools know to intepret the carousel as a list.
609
+ *
610
+ * Note that if the immediate Carousel children do not all render as `'li'` elements,
611
+ * this should be changed (e.g. pass tag="div") because only 'li' is a valid child of 'ul'.
612
+ */
613
+ tag: PropTypes.oneOf(layoutTags)
614
+ };
615
+ Carousel.Item = CarouselItem;
616
+ Carousel.displayName = 'Carousel';
617
+ export default Carousel;
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { jsx as _jsx } from "react/jsx-runtime";
4
+ const CarouselContext = /*#__PURE__*/React.createContext();
5
+
6
+ const CarouselProvider = _ref => {
7
+ let {
8
+ children,
9
+ activeIndex,
10
+ totalItems,
11
+ width,
12
+ goTo
13
+ } = _ref;
14
+ const value = React.useMemo(() => ({
15
+ activeIndex,
16
+ totalItems,
17
+ width,
18
+ goTo
19
+ }), [activeIndex, totalItems, width, goTo]);
20
+ return /*#__PURE__*/_jsx(CarouselContext.Provider, {
21
+ value: value,
22
+ children: children
23
+ });
24
+ };
25
+
26
+ function useCarousel() {
27
+ const context = React.useContext(CarouselContext);
28
+
29
+ if (context === undefined) {
30
+ throw new Error("'useCarousel' must be used within a 'CarouselProvider'");
31
+ }
32
+
33
+ return context;
34
+ }
35
+
36
+ CarouselProvider.propTypes = {
37
+ children: PropTypes.arrayOf(PropTypes.element).isRequired,
38
+ activeIndex: PropTypes.number.isRequired,
39
+ totalItems: PropTypes.number.isRequired,
40
+ width: PropTypes.number.isRequired,
41
+ goTo: PropTypes.func.isRequired
42
+ };
43
+ export { CarouselProvider, useCarousel };