@hero-design/rn 8.2.2 → 8.3.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 (57) hide show
  1. package/.turbo/turbo-build.log +9 -9
  2. package/es/index.js +409 -80
  3. package/lib/index.js +407 -77
  4. package/package.json +5 -5
  5. package/src/components/Box/StyledBox.tsx +8 -6
  6. package/src/components/Card/StyledCard.tsx +1 -1
  7. package/src/components/Card/__tests__/__snapshots__/index.spec.tsx.snap +74 -0
  8. package/src/components/Card/__tests__/index.spec.tsx +2 -0
  9. package/src/components/Card/index.tsx +1 -1
  10. package/src/components/Carousel/CarouselItem.tsx +49 -0
  11. package/src/components/Carousel/CarouselPaginator/StyledCarouselPaginator.tsx +19 -0
  12. package/src/components/Carousel/CarouselPaginator/index.tsx +58 -0
  13. package/src/components/Carousel/StyledCarousel.tsx +55 -0
  14. package/src/components/Carousel/__tests__/StyledCarousel.spec.tsx +13 -0
  15. package/src/components/Carousel/__tests__/__snapshots__/StyledCarousel.spec.tsx.snap +21 -0
  16. package/src/components/Carousel/__tests__/index.spec.tsx +86 -0
  17. package/src/components/Carousel/index.tsx +176 -0
  18. package/src/components/Carousel/types.ts +10 -0
  19. package/src/components/Icon/index.tsx +28 -2
  20. package/src/components/TextInput/__tests__/__snapshots__/index.spec.tsx.snap +160 -0
  21. package/src/components/TextInput/__tests__/index.spec.tsx +50 -3
  22. package/src/components/TextInput/index.tsx +65 -30
  23. package/src/components/Toolbar/ToolbarGroup.tsx +20 -14
  24. package/src/components/Toolbar/ToolbarItem.tsx +9 -1
  25. package/src/components/Toolbar/__tests__/__snapshots__/ToolbarGroup.spec.tsx.snap +12 -0
  26. package/src/components/Toolbar/__tests__/__snapshots__/ToolbarItem.spec.tsx.snap +6 -0
  27. package/src/index.ts +2 -0
  28. package/src/theme/__tests__/__snapshots__/index.spec.ts.snap +27 -0
  29. package/src/theme/components/carousel.ts +39 -0
  30. package/src/theme/getTheme.ts +3 -0
  31. package/src/types.ts +4 -1
  32. package/src/utils/__tests__/helpers.spec.ts +22 -4
  33. package/src/utils/helpers.ts +10 -9
  34. package/types/components/BottomNavigation/StyledBottomNavigation.d.ts +1 -1
  35. package/types/components/Box/StyledBox.d.ts +2 -2
  36. package/types/components/Button/StyledButton.d.ts +1 -1
  37. package/types/components/Button/UtilityButton/StyledUtilityButton.d.ts +1 -1
  38. package/types/components/Card/StyledCard.d.ts +1 -1
  39. package/types/components/Card/index.d.ts +1 -1
  40. package/types/components/Carousel/CarouselItem.d.ts +7 -0
  41. package/types/components/Carousel/CarouselPaginator/StyledCarouselPaginator.d.ts +16 -0
  42. package/types/components/Carousel/CarouselPaginator/index.d.ts +7 -0
  43. package/types/components/Carousel/StyledCarousel.d.ts +34 -0
  44. package/types/components/Carousel/index.d.ts +46 -0
  45. package/types/components/Carousel/types.d.ts +9 -0
  46. package/types/components/Checkbox/StyledCheckbox.d.ts +1 -1
  47. package/types/components/ContentNavigator/StyledContentNavigator.d.ts +1 -1
  48. package/types/components/FAB/ActionGroup/StyledActionItem.d.ts +1 -1
  49. package/types/components/Icon/index.d.ts +3 -3
  50. package/types/components/TextInput/StyledTextInput.d.ts +7 -7
  51. package/types/components/TextInput/index.d.ts +25 -3
  52. package/types/components/Toolbar/ToolbarItem.d.ts +6 -1
  53. package/types/index.d.ts +2 -1
  54. package/types/theme/components/carousel.d.ts +29 -0
  55. package/types/theme/getTheme.d.ts +2 -0
  56. package/types/types.d.ts +3 -2
  57. package/types/utils/helpers.d.ts +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hero-design/rn",
3
- "version": "8.2.2",
3
+ "version": "8.3.0",
4
4
  "license": "MIT",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.js",
@@ -21,7 +21,7 @@
21
21
  "dependencies": {
22
22
  "@emotion/native": "^11.9.3",
23
23
  "@emotion/react": "^11.9.3",
24
- "@hero-design/colors": "8.2.2",
24
+ "@hero-design/colors": "8.3.0",
25
25
  "date-fns": "^2.16.1",
26
26
  "events": "^3.2.0",
27
27
  "hero-editor": "^1.9.21"
@@ -44,7 +44,7 @@
44
44
  "@babel/preset-typescript": "^7.17.12",
45
45
  "@babel/runtime": "^7.18.9",
46
46
  "@emotion/jest": "^11.9.3",
47
- "@hero-design/eslint-plugin": "8.2.2",
47
+ "@hero-design/eslint-plugin": "8.3.0",
48
48
  "@react-native-community/datetimepicker": "^3.5.2",
49
49
  "@react-native-community/slider": "4.1.12",
50
50
  "@rollup/plugin-babel": "^5.3.1",
@@ -60,9 +60,9 @@
60
60
  "@types/react-native": "^0.67.7",
61
61
  "@types/react-native-vector-icons": "^6.4.10",
62
62
  "babel-plugin-inline-import": "^3.0.0",
63
- "eslint-config-hd": "8.2.2",
63
+ "eslint-config-hd": "8.3.0",
64
64
  "jest": "^27.3.1",
65
- "prettier-config-hd": "8.2.2",
65
+ "prettier-config-hd": "8.3.0",
66
66
  "react": "18.0.0",
67
67
  "react-native": "0.69.7",
68
68
  "react-native-gesture-handler": "~2.1.0",
@@ -1,7 +1,7 @@
1
1
  import styled from '@emotion/native';
2
2
  import { Theme } from '@emotion/react';
3
3
  import { View } from 'react-native';
4
- import { StyleProps, ThemeScale } from './types';
4
+ import { FlexStyleProps, StyleProps, ThemeScale } from './types';
5
5
  import { pick } from '../../utils/helpers';
6
6
  import config, { ConfigType, flexPropsKey } from './config';
7
7
 
@@ -44,10 +44,12 @@ const mapStylePropToThemeValue = (theme: Theme, props: StyleProps) => {
44
44
 
45
45
  const configKeys = Object.keys(config) as Array<keyof ConfigType>;
46
46
 
47
- const StyledBox = styled(View)<StyleProps>(({ theme, ...otherProps }) => {
48
- const styleProps = pick(configKeys, otherProps);
49
- const flexProps = pick([...flexPropsKey], otherProps);
50
- return { ...mapStylePropToThemeValue(theme, styleProps), ...flexProps };
51
- });
47
+ const StyledBox = styled(View)<StyleProps & FlexStyleProps>(
48
+ ({ theme, ...otherProps }) => {
49
+ const styleProps = pick(configKeys, otherProps);
50
+ const flexProps = pick([...flexPropsKey], otherProps);
51
+ return { ...mapStylePropToThemeValue(theme, styleProps), ...flexProps };
52
+ }
53
+ );
52
54
 
53
55
  export { StyledBox };
@@ -2,7 +2,7 @@ import { View } from 'react-native';
2
2
  import styled from '@emotion/native';
3
3
 
4
4
  const StyledCard = styled(View)<{
5
- themeIntent?: 'primary' | 'success' | 'warning';
5
+ themeIntent?: 'primary' | 'success' | 'warning' | 'danger' | 'archived';
6
6
  }>(({ theme, themeIntent }) => ({
7
7
  ...(themeIntent !== undefined && {
8
8
  backgroundColor: theme.__hd__.card.colors[themeIntent],
@@ -1,5 +1,79 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
+ exports[`Card renders correctly when intent is archived 1`] = `
4
+ <View
5
+ style={
6
+ Array [
7
+ Object {
8
+ "backgroundColor": "#abacaf",
9
+ "borderRadius": 12,
10
+ "overflow": "hidden",
11
+ },
12
+ undefined,
13
+ ]
14
+ }
15
+ themeIntent="archived"
16
+ >
17
+ <Text
18
+ style={
19
+ Array [
20
+ Object {
21
+ "color": "#001f23",
22
+ "fontFamily": "BeVietnamPro-Regular",
23
+ "fontSize": 14,
24
+ "letterSpacing": 0.42,
25
+ "lineHeight": 22,
26
+ },
27
+ undefined,
28
+ ]
29
+ }
30
+ themeFontSize="medium"
31
+ themeFontWeight="regular"
32
+ themeIntent="body"
33
+ themeTypeface="neutral"
34
+ >
35
+ Card Content
36
+ </Text>
37
+ </View>
38
+ `;
39
+
40
+ exports[`Card renders correctly when intent is danger 1`] = `
41
+ <View
42
+ style={
43
+ Array [
44
+ Object {
45
+ "backgroundColor": "#f46363",
46
+ "borderRadius": 12,
47
+ "overflow": "hidden",
48
+ },
49
+ undefined,
50
+ ]
51
+ }
52
+ themeIntent="danger"
53
+ >
54
+ <Text
55
+ style={
56
+ Array [
57
+ Object {
58
+ "color": "#001f23",
59
+ "fontFamily": "BeVietnamPro-Regular",
60
+ "fontSize": 14,
61
+ "letterSpacing": 0.42,
62
+ "lineHeight": 22,
63
+ },
64
+ undefined,
65
+ ]
66
+ }
67
+ themeFontSize="medium"
68
+ themeFontWeight="regular"
69
+ themeIntent="body"
70
+ themeTypeface="neutral"
71
+ >
72
+ Card Content
73
+ </Text>
74
+ </View>
75
+ `;
76
+
3
77
  exports[`Card renders correctly when intent is info 1`] = `
4
78
  <View
5
79
  style={
@@ -23,6 +23,8 @@ describe('Card', () => {
23
23
  ${'primary'}
24
24
  ${'success'}
25
25
  ${'info'}
26
+ ${'danger'}
27
+ ${'archived'}
26
28
  `('renders correctly when intent is $intent', ({ intent }) => {
27
29
  const { toJSON, getByText } = renderWithTheme(
28
30
  <Card intent={intent}>
@@ -12,7 +12,7 @@ interface CardProps extends ViewProps {
12
12
  /**
13
13
  * Visual intent color to apply to card.
14
14
  */
15
- intent?: 'primary' | 'success' | 'warning';
15
+ intent?: 'primary' | 'success' | 'warning' | 'danger' | 'archived';
16
16
  /**
17
17
  * Additional style.
18
18
  */
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+
3
+ import {
4
+ StyledCarouselContentWrapper,
5
+ StyledCarouselHeading,
6
+ StyledCarouselImage,
7
+ } from './StyledCarousel';
8
+ import Box from '../Box';
9
+ import Typography from '../Typography';
10
+ import { CarouselData } from './types';
11
+
12
+ interface CarouselItemProps extends Omit<CarouselData, 'background'> {
13
+ width: number;
14
+ minHeight: number;
15
+ }
16
+
17
+ const CarouselItem = ({
18
+ width,
19
+ image,
20
+ content,
21
+ heading,
22
+ body,
23
+ minHeight,
24
+ }: CarouselItemProps) => {
25
+ return (
26
+ <Box style={{ width }}>
27
+ {image && (
28
+ <StyledCarouselImage
29
+ source={typeof image === 'string' ? { uri: image } : image}
30
+ />
31
+ )}
32
+
33
+ <StyledCarouselContentWrapper
34
+ paddingHorizontal="large"
35
+ marginTop="large"
36
+ width={width}
37
+ style={{ minHeight }}
38
+ >
39
+ {content}
40
+ <StyledCarouselHeading>{heading}</StyledCarouselHeading>
41
+ {body ? (
42
+ <Typography.Text fontSize="large">{body}</Typography.Text>
43
+ ) : null}
44
+ </StyledCarouselContentWrapper>
45
+ </Box>
46
+ );
47
+ };
48
+
49
+ export default CarouselItem;
@@ -0,0 +1,19 @@
1
+ import { Animated, View } from 'react-native';
2
+ import styled from '@emotion/native';
3
+
4
+ const StyledCarouselPaginator = styled(View)(() => ({
5
+ flexDirection: 'row',
6
+ alignItems: 'center',
7
+ }));
8
+
9
+ const StyledCarouselPaginatorAnimatedView = styled(Animated.View)<{
10
+ indicatorWidth: Animated.AnimatedInterpolation;
11
+ }>(({ theme }) => ({
12
+ height: theme.__hd__.carousel.sizes.paginatorHeight,
13
+ width: theme.__hd__.carousel.sizes.paginatorWidth,
14
+ borderRadius: theme.__hd__.carousel.radii.paginatorBorderRadius,
15
+ backgroundColor: theme.__hd__.carousel.colors.paginatorBackgroundColor,
16
+ marginHorizontal: theme.__hd__.carousel.space.paginatorMarginHorizontal,
17
+ }));
18
+
19
+ export { StyledCarouselPaginator, StyledCarouselPaginatorAnimatedView };
@@ -0,0 +1,58 @@
1
+ import React from 'react';
2
+ import { Animated, useWindowDimensions } from 'react-native';
3
+
4
+ import {
5
+ StyledCarouselPaginatorAnimatedView,
6
+ StyledCarouselPaginator,
7
+ } from './StyledCarouselPaginator';
8
+ import { useTheme } from '../../../theme';
9
+
10
+ interface CarouselPaginatorProps {
11
+ numberOfSlides: number;
12
+ scrollX: Animated.Value;
13
+ }
14
+ const CarouselPaginator = ({
15
+ numberOfSlides,
16
+ scrollX,
17
+ }: CarouselPaginatorProps) => {
18
+ const { width } = useWindowDimensions();
19
+ const theme = useTheme();
20
+
21
+ return (
22
+ <StyledCarouselPaginator>
23
+ {new Array(numberOfSlides).fill('').map((_, index) => {
24
+ const inputRange = [
25
+ (index - 1) * width,
26
+ index * width,
27
+ (index + 1) * width,
28
+ ];
29
+
30
+ const indicatorWidth = scrollX.interpolate({
31
+ inputRange,
32
+ outputRange: [
33
+ theme.space.small,
34
+ theme.space.large,
35
+ theme.space.small,
36
+ ],
37
+ extrapolate: 'clamp',
38
+ });
39
+
40
+ const opacity = scrollX.interpolate({
41
+ inputRange,
42
+ outputRange: [0.5, 1, 0.5],
43
+ extrapolate: 'clamp',
44
+ });
45
+
46
+ return (
47
+ <StyledCarouselPaginatorAnimatedView
48
+ indicatorWidth={indicatorWidth}
49
+ style={[{ width: indicatorWidth, opacity }]}
50
+ key={index.toString()}
51
+ />
52
+ );
53
+ })}
54
+ </StyledCarouselPaginator>
55
+ );
56
+ };
57
+
58
+ export default CarouselPaginator;
@@ -0,0 +1,55 @@
1
+ import { View } from 'react-native';
2
+ import styled from '@emotion/native';
3
+ import Typography from '../Typography';
4
+ import Image from '../Image';
5
+ import Box from '../Box';
6
+
7
+ const StyledCarousel = styled(View)<{
8
+ themeSlideBackground: string;
9
+ }>(({ themeSlideBackground }) => ({
10
+ zIndex: 99999,
11
+ position: 'absolute',
12
+ backgroundColor: themeSlideBackground,
13
+ top: 0,
14
+ bottom: 0,
15
+ right: 0,
16
+ left: 0,
17
+ }));
18
+
19
+ const StyledCarouselView = styled(View)(() => ({
20
+ justifyContent: 'space-between',
21
+ height: '100%',
22
+ }));
23
+
24
+ const StyledCarouselHeading = styled(Typography.Text)(({ theme }) => ({
25
+ marginTop: theme.__hd__.carousel.space.headingMarginTop,
26
+ marginBottom: theme.__hd__.carousel.space.headingMarginBottom,
27
+ fontFamily: theme.__hd__.carousel.fonts.heading,
28
+ fontSize: theme.__hd__.carousel.fontSizes.heading,
29
+ lineHeight: theme.__hd__.carousel.lineHeights.heading,
30
+ }));
31
+
32
+ const StyledCarouselImage = styled(Image)(() => ({
33
+ flex: 1,
34
+ width: '100%',
35
+ resizeMode: 'contain',
36
+ }));
37
+
38
+ const StyledCarouselContentWrapper = styled(Box)<{
39
+ width: number;
40
+ }>(({ width }) => ({
41
+ width,
42
+ }));
43
+
44
+ const StyledCarouselFooterWrapper = styled(Box)(() => ({
45
+ width: '100%',
46
+ }));
47
+
48
+ export {
49
+ StyledCarousel,
50
+ StyledCarouselView,
51
+ StyledCarouselHeading,
52
+ StyledCarouselImage,
53
+ StyledCarouselContentWrapper,
54
+ StyledCarouselFooterWrapper,
55
+ };
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import renderWithTheme from '../../../testHelpers/renderWithTheme';
3
+ import { StyledCarousel } from '../StyledCarousel';
4
+ import theme from '../../../theme';
5
+
6
+ describe('StyledCard', () => {
7
+ it('renders correct basic style', () => {
8
+ const { toJSON } = renderWithTheme(
9
+ <StyledCarousel themeSlideBackground={theme.colors.highlightedSurface} />
10
+ );
11
+ expect(toJSON()).toMatchSnapshot();
12
+ });
13
+ });
@@ -0,0 +1,21 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`StyledCard renders correct basic style 1`] = `
4
+ <View
5
+ style={
6
+ Array [
7
+ Object {
8
+ "backgroundColor": "#ece8ef",
9
+ "bottom": 0,
10
+ "left": 0,
11
+ "position": "absolute",
12
+ "right": 0,
13
+ "top": 0,
14
+ "zIndex": 99999,
15
+ },
16
+ undefined,
17
+ ]
18
+ }
19
+ themeSlideBackground="#ece8ef"
20
+ />
21
+ `;
@@ -0,0 +1,86 @@
1
+ import React from 'react';
2
+ import { fireEvent } from '@testing-library/react-native';
3
+
4
+ import renderWithTheme from '../../../testHelpers/renderWithTheme';
5
+ import Carousel from '..';
6
+ import Image from '../../Image';
7
+ import { theme } from '../../..';
8
+
9
+ const carouselData = [
10
+ {
11
+ image: 'https://picsum.photos/800/1200',
12
+ heading: 'Welcome to the new Employment Hero app',
13
+ body: 'Access your Work, Money and Benefits in the palm of your hand.',
14
+ background: theme.colors.highlightedSurface,
15
+ },
16
+ {
17
+ image: 'https://picsum.photos/800/1200',
18
+ content: <Image source={{ uri: 'https://picsum.photos/30' }} />,
19
+ heading: 'Same app with a new look!',
20
+ body: 'Our app now has a new bright clean modern look and feel with the same great features.',
21
+ background: theme.colors.decorativePrimarySurface,
22
+ },
23
+ {
24
+ image: 'https://picsum.photos/800/1200',
25
+ heading: 'Easier to get around',
26
+ body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vitae pulvinar quam, ac facilisis massa. Aliquam facilisis nisi eu justo dignissim, vel tempus justo iaculis.',
27
+ background: theme.colors.decorativePrimarySurface,
28
+ },
29
+ ];
30
+
31
+ describe('Carousel', () => {
32
+ it('renders basic carousel', async () => {
33
+ const onSkip = jest.fn();
34
+ const onFinish = jest.fn();
35
+
36
+ const { getByTestId, queryByText } = renderWithTheme(
37
+ <Carousel
38
+ testID="carousel"
39
+ items={carouselData}
40
+ onSkipPress={onSkip}
41
+ onFinishPress={onFinish}
42
+ />
43
+ );
44
+
45
+ expect(getByTestId('carousel')).toBeTruthy();
46
+
47
+ expect(queryByText('Skip')).toBeTruthy();
48
+
49
+ // Slide 1
50
+ expect(queryByText('Welcome to the new Employment Hero app')).toBeTruthy();
51
+ expect(
52
+ queryByText(
53
+ 'Access your Work, Money and Benefits in the palm of your hand.'
54
+ )
55
+ ).toBeTruthy();
56
+
57
+ // Slide 2
58
+ expect(queryByText('Same app with a new look!')).toBeTruthy();
59
+ expect(
60
+ queryByText(
61
+ 'Our app now has a new bright clean modern look and feel with the same great features.'
62
+ )
63
+ ).toBeTruthy();
64
+
65
+ // Slide 3
66
+ expect(queryByText('Easier to get around')).toBeTruthy();
67
+ expect(
68
+ queryByText(
69
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vitae pulvinar quam, ac facilisis massa. Aliquam facilisis nisi eu justo dignissim, vel tempus justo iaculis.'
70
+ )
71
+ ).toBeTruthy();
72
+ });
73
+
74
+ it('should call skip call back when press skip', () => {
75
+ const onSkip = jest.fn();
76
+
77
+ const { queryByText } = renderWithTheme(
78
+ <Carousel testID="carousel" items={carouselData} onSkipPress={onSkip} />
79
+ );
80
+
81
+ expect(queryByText('Skip')).toBeTruthy();
82
+
83
+ fireEvent.press(queryByText('Skip'));
84
+ expect(onSkip).toBeCalled();
85
+ });
86
+ });
@@ -0,0 +1,176 @@
1
+ import React, { useCallback, useRef, useState } from 'react';
2
+ import {
3
+ Animated,
4
+ FlatList,
5
+ StyleProp,
6
+ useWindowDimensions,
7
+ ViewProps,
8
+ ViewStyle,
9
+ } from 'react-native';
10
+
11
+ import { CarouselData } from './types';
12
+ import CarouselPaginator from './CarouselPaginator';
13
+ import {
14
+ StyledCarousel,
15
+ StyledCarouselFooterWrapper,
16
+ StyledCarouselView,
17
+ } from './StyledCarousel';
18
+ import Button from '../Button';
19
+ import CarouselItem from './CarouselItem';
20
+
21
+ interface CarouselProps extends ViewProps {
22
+ /**
23
+ * Additional style.
24
+ */
25
+ style?: StyleProp<ViewStyle>;
26
+ /**
27
+ * Testing id of the component.
28
+ */
29
+ testID?: string;
30
+
31
+ /**
32
+ * Callback to be called when the user slides to the next slide.
33
+ */
34
+ onSlideChange?: (currentSlide?: CarouselData) => void;
35
+
36
+ /**
37
+ * Callback to be called when the user skips the intro carousel.
38
+ */
39
+ onSkipPress?: (currentSlide?: CarouselData) => void;
40
+
41
+ /**
42
+ * Callback to be called when the user finishes the intro carousel.
43
+ */
44
+ onFinishPress?: (currentSlide?: CarouselData) => void;
45
+
46
+ /**
47
+ * Carousel data.
48
+ */
49
+ items: CarouselData[];
50
+
51
+ /**
52
+ * Finish button label.
53
+ */
54
+ finishButtonLabel?: string;
55
+
56
+ /**
57
+ * Skip button label.
58
+ */
59
+ skipButtonLabel?: string;
60
+
61
+ /**
62
+ * Footer height.
63
+ */
64
+ footerHeight?: number;
65
+
66
+ /**
67
+ * Min content height.
68
+ */
69
+ minContentHeight?: number;
70
+ }
71
+
72
+ const Carousel = ({
73
+ items,
74
+ onFinishPress,
75
+ onSkipPress,
76
+ onSlideChange,
77
+ finishButtonLabel = `Let's go!`,
78
+ skipButtonLabel = 'Skip',
79
+ footerHeight = 70,
80
+ minContentHeight = 250,
81
+ ...nativeProps
82
+ }: CarouselProps) => {
83
+ const carouselRef = useRef(null);
84
+ const [currentSlideIndex, setCurrentSlideIndex] = useState(0);
85
+ const { width } = useWindowDimensions();
86
+
87
+ const scrollX = useRef(new Animated.Value(0)).current;
88
+
89
+ const visibleSlideChanged = useCallback(
90
+ ({ viewableItems }) => {
91
+ if (!viewableItems || (viewableItems && !viewableItems.length)) return;
92
+ const currentIndex = viewableItems[0].index;
93
+ setCurrentSlideIndex(viewableItems[0].index);
94
+ const currentSlide = items[currentIndex];
95
+ if (onSlideChange) {
96
+ onSlideChange(currentSlide);
97
+ }
98
+ },
99
+ [items, onSlideChange]
100
+ );
101
+
102
+ const skipCarousel = useCallback(() => {
103
+ const currentSlide = items[currentSlideIndex];
104
+ if (onSkipPress) {
105
+ onSkipPress(currentSlide);
106
+ }
107
+ }, [currentSlideIndex, onSkipPress, items]);
108
+
109
+ const finishCarousel = useCallback(() => {
110
+ const currentSlide = items[currentSlideIndex];
111
+ if (onFinishPress) {
112
+ onFinishPress(currentSlide);
113
+ }
114
+ }, [onFinishPress, items, currentSlideIndex]);
115
+
116
+ const viewConfig = useRef({ viewAreaCoveragePercentThreshold: 50 }).current;
117
+
118
+ const isLastSlide = currentSlideIndex === items.length - 1;
119
+
120
+ return (
121
+ <StyledCarousel
122
+ themeSlideBackground={items[currentSlideIndex].background}
123
+ {...nativeProps}
124
+ >
125
+ <StyledCarouselView>
126
+ <FlatList
127
+ horizontal
128
+ showsHorizontalScrollIndicator={false}
129
+ pagingEnabled
130
+ bounces={false}
131
+ data={items}
132
+ onViewableItemsChanged={visibleSlideChanged}
133
+ viewabilityConfig={viewConfig}
134
+ scrollEventThrottle={32}
135
+ ref={carouselRef}
136
+ onScroll={Animated.event(
137
+ [{ nativeEvent: { contentOffset: { x: scrollX } } }],
138
+ { useNativeDriver: false }
139
+ )}
140
+ renderItem={({ item }) => {
141
+ if (!item) return null;
142
+
143
+ const { image, heading, body, content } = item;
144
+
145
+ return (
146
+ <CarouselItem
147
+ image={image}
148
+ heading={heading}
149
+ body={body}
150
+ content={content}
151
+ minHeight={minContentHeight}
152
+ width={width}
153
+ />
154
+ );
155
+ }}
156
+ />
157
+ <StyledCarouselFooterWrapper
158
+ paddingHorizontal="large"
159
+ flexDirection="row"
160
+ justifyContent="space-between"
161
+ style={{ height: footerHeight }}
162
+ >
163
+ <Button
164
+ variant={isLastSlide ? 'filled' : 'text'}
165
+ intent="primary"
166
+ onPress={isLastSlide ? finishCarousel : skipCarousel}
167
+ text={isLastSlide ? finishButtonLabel : skipButtonLabel}
168
+ />
169
+ <CarouselPaginator numberOfSlides={items.length} scrollX={scrollX} />
170
+ </StyledCarouselFooterWrapper>
171
+ </StyledCarouselView>
172
+ </StyledCarousel>
173
+ );
174
+ };
175
+
176
+ export default Carousel;
@@ -0,0 +1,10 @@
1
+ import { ReactNode } from 'react';
2
+ import { ImageSourcePropType } from 'react-native';
3
+
4
+ export type CarouselData = {
5
+ image: ImageSourcePropType | string;
6
+ content?: ReactNode;
7
+ heading: string;
8
+ body?: string;
9
+ background: string;
10
+ };