@utilitywarehouse/hearth-react-native 0.4.2 → 0.6.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 (207) hide show
  1. package/.storybook/main.ts +21 -1
  2. package/.turbo/turbo-build.log +1 -1
  3. package/.turbo/turbo-lint.log +1 -1
  4. package/CHANGELOG.md +52 -0
  5. package/build/components/Alert/AlertTitle.js +6 -6
  6. package/build/components/Badge/Badge.js +3 -3
  7. package/build/components/Badge/Badge.props.d.ts +1 -0
  8. package/build/components/Button/ButtonRoot.js +4 -0
  9. package/build/components/Button/ButtonText.js +2 -2
  10. package/build/components/Card/CardRoot.js +1 -1
  11. package/build/components/Carousel/Carousel.context.d.ts +4 -0
  12. package/build/components/Carousel/Carousel.context.js +4 -0
  13. package/build/components/Carousel/Carousel.d.ts +6 -0
  14. package/build/components/Carousel/Carousel.js +278 -0
  15. package/build/components/Carousel/Carousel.props.d.ts +65 -0
  16. package/build/components/Carousel/Carousel.props.js +1 -0
  17. package/build/components/Carousel/CarouselControlItem.d.ts +24 -0
  18. package/build/components/Carousel/CarouselControlItem.js +64 -0
  19. package/build/components/Carousel/CarouselControls.d.ts +4 -0
  20. package/build/components/Carousel/CarouselControls.js +74 -0
  21. package/build/components/Carousel/CarouselItem.d.ts +6 -0
  22. package/build/components/Carousel/CarouselItem.js +38 -0
  23. package/build/components/Carousel/index.d.ts +5 -0
  24. package/build/components/Carousel/index.js +5 -0
  25. package/build/components/Container/Container.d.ts +6 -0
  26. package/build/components/Container/Container.js +40 -0
  27. package/build/components/Container/Container.props.d.ts +85 -0
  28. package/build/components/Container/Container.props.js +1 -0
  29. package/build/components/Container/index.d.ts +2 -0
  30. package/build/components/Container/index.js +1 -0
  31. package/build/components/DescriptionList/DescriptionList.d.ts +1 -1
  32. package/build/components/DescriptionList/DescriptionList.js +2 -2
  33. package/build/components/DescriptionList/DescriptionList.props.d.ts +1 -8
  34. package/build/components/DescriptionList/DescriptionListItem.d.ts +1 -1
  35. package/build/components/DescriptionList/DescriptionListItem.js +4 -3
  36. package/build/components/DescriptionList/DescriptionListItem.props.d.ts +3 -8
  37. package/build/components/IndicatorIconButton/IndicatorIconButton.d.ts +6 -0
  38. package/build/components/IndicatorIconButton/IndicatorIconButton.js +26 -0
  39. package/build/components/IndicatorIconButton/IndicatorIconButton.props.d.ts +8 -0
  40. package/build/components/IndicatorIconButton/IndicatorIconButton.props.js +1 -0
  41. package/build/components/IndicatorIconButton/index.d.ts +2 -0
  42. package/build/components/IndicatorIconButton/index.js +1 -0
  43. package/build/components/Link/LinkText.js +3 -3
  44. package/build/components/List/List.context.d.ts +0 -2
  45. package/build/components/List/List.d.ts +1 -1
  46. package/build/components/List/List.js +5 -5
  47. package/build/components/List/List.props.d.ts +1 -9
  48. package/build/components/List/ListAction/ListAction.d.ts +18 -0
  49. package/build/components/List/ListAction/ListAction.js +103 -0
  50. package/build/components/List/ListAction/ListAction.props.d.ts +8 -0
  51. package/build/components/List/ListAction/ListAction.props.js +1 -0
  52. package/build/components/List/ListAction/ListActionContent.d.ts +6 -0
  53. package/build/components/List/ListAction/ListActionContent.js +14 -0
  54. package/build/components/List/ListAction/ListActionText.d.ts +6 -0
  55. package/build/components/List/ListAction/ListActionText.js +7 -0
  56. package/build/components/List/ListAction/ListActionTrailingContent.d.ts +6 -0
  57. package/build/components/List/ListAction/ListActionTrailingContent.js +5 -0
  58. package/build/components/List/ListAction/ListActionTrailingIcon.d.ts +9 -0
  59. package/build/components/List/ListAction/ListActionTrailingIcon.js +18 -0
  60. package/build/components/List/ListAction/index.d.ts +6 -0
  61. package/build/components/List/ListAction/index.js +5 -0
  62. package/build/components/List/ListItem/ListItem.context.d.ts +1 -1
  63. package/build/components/List/ListItem/ListItem.props.d.ts +9 -5
  64. package/build/components/List/ListItem/ListItemRoot.d.ts +1 -1
  65. package/build/components/List/ListItem/ListItemRoot.js +10 -12
  66. package/build/components/List/ListItem/index.d.ts +4 -4
  67. package/build/components/List/ListItem/index.js +3 -3
  68. package/build/components/List/index.d.ts +1 -0
  69. package/build/components/List/index.js +1 -0
  70. package/build/components/ProgressStepper/ProgressStep.d.ts +10 -0
  71. package/build/components/ProgressStepper/ProgressStep.js +100 -0
  72. package/build/components/ProgressStepper/ProgressStepper.d.ts +6 -0
  73. package/build/components/ProgressStepper/ProgressStepper.js +22 -0
  74. package/build/components/ProgressStepper/ProgressStepper.props.d.ts +22 -0
  75. package/build/components/ProgressStepper/ProgressStepper.props.js +1 -0
  76. package/build/components/ProgressStepper/ProgressStepperRoot.d.ts +6 -0
  77. package/build/components/ProgressStepper/ProgressStepperRoot.js +16 -0
  78. package/build/components/ProgressStepper/index.d.ts +3 -0
  79. package/build/components/ProgressStepper/index.js +2 -0
  80. package/build/components/SectionHeader/SectionHeader.d.ts +1 -1
  81. package/build/components/SectionHeader/SectionHeader.js +6 -3
  82. package/build/components/SectionHeader/SectionHeader.props.d.ts +9 -16
  83. package/build/components/SectionHeader/SectionHeaderTrailingContent.d.ts +6 -0
  84. package/build/components/SectionHeader/SectionHeaderTrailingContent.js +13 -0
  85. package/build/components/SectionHeader/index.d.ts +1 -0
  86. package/build/components/SectionHeader/index.js +1 -0
  87. package/build/components/Tabs/Tab.js +2 -2
  88. package/build/components/ThemedImage/ThemedImage.d.ts +12 -0
  89. package/build/components/ThemedImage/ThemedImage.js +27 -0
  90. package/build/components/ThemedImage/ThemedImage.props.d.ts +13 -0
  91. package/build/components/ThemedImage/ThemedImage.props.js +1 -0
  92. package/build/components/ThemedImage/index.d.ts +2 -0
  93. package/build/components/ThemedImage/index.js +1 -0
  94. package/build/components/ToggleButton/ToggleButtonText.js +2 -2
  95. package/build/components/UnstyledIconButton/UnstyledIconButton.props.d.ts +4 -1
  96. package/build/components/index.d.ts +5 -0
  97. package/build/components/index.js +5 -0
  98. package/build/core/themes.d.ts +12 -24
  99. package/build/hooks/useStyleProps.js +1 -1
  100. package/build/tokens/components/dark/button.d.ts +1 -1
  101. package/build/tokens/components/dark/button.js +1 -1
  102. package/build/tokens/components/dark/dialog.d.ts +1 -0
  103. package/build/tokens/components/dark/dialog.js +1 -0
  104. package/build/tokens/components/dark/illustrations.d.ts +1 -0
  105. package/build/tokens/components/dark/illustrations.js +1 -0
  106. package/build/tokens/components/dark/toast.d.ts +4 -1
  107. package/build/tokens/components/dark/toast.js +4 -1
  108. package/build/tokens/components/light/button.d.ts +1 -1
  109. package/build/tokens/components/light/button.js +1 -1
  110. package/build/tokens/components/light/dialog.d.ts +1 -0
  111. package/build/tokens/components/light/dialog.js +1 -0
  112. package/build/tokens/components/light/illustrations.d.ts +1 -0
  113. package/build/tokens/components/light/illustrations.js +1 -0
  114. package/build/tokens/components/light/toast.d.ts +4 -1
  115. package/build/tokens/components/light/toast.js +4 -1
  116. package/build/tokens/layout.d.ts +6 -12
  117. package/build/tokens/layout.js +3 -6
  118. package/docs/components/AllComponents.web.tsx +122 -5
  119. package/docs/components/BadgeList.tsx +20 -56
  120. package/docs/components/SwitchList.tsx +4 -8
  121. package/docs/getting-started.mdx +30 -14
  122. package/docs/introduction.mdx +1 -1
  123. package/docs/layout-components.docs.mdx +30 -0
  124. package/package.json +6 -4
  125. package/src/components/Alert/AlertTitle.tsx +7 -7
  126. package/src/components/Badge/Badge.props.ts +1 -0
  127. package/src/components/Badge/Badge.tsx +3 -2
  128. package/src/components/Button/ButtonRoot.tsx +4 -0
  129. package/src/components/Button/ButtonText.tsx +3 -3
  130. package/src/components/Card/CardRoot.tsx +2 -0
  131. package/src/components/Carousel/Carousel.context.tsx +8 -0
  132. package/src/components/Carousel/Carousel.docs.mdx +389 -0
  133. package/src/components/Carousel/Carousel.props.ts +89 -0
  134. package/src/components/Carousel/Carousel.stories.tsx +317 -0
  135. package/src/components/Carousel/Carousel.tsx +444 -0
  136. package/src/components/Carousel/CarouselControlItem.tsx +87 -0
  137. package/src/components/Carousel/CarouselControls.tsx +150 -0
  138. package/src/components/Carousel/CarouselItem.tsx +68 -0
  139. package/src/components/Carousel/index.ts +6 -0
  140. package/src/components/Container/Container.docs.mdx +168 -0
  141. package/src/components/Container/Container.props.ts +89 -0
  142. package/src/components/Container/Container.stories.tsx +274 -0
  143. package/src/components/Container/Container.tsx +52 -0
  144. package/src/components/Container/index.tsx +2 -0
  145. package/src/components/DescriptionList/DescriptionList.docs.mdx +24 -27
  146. package/src/components/DescriptionList/DescriptionList.props.ts +1 -8
  147. package/src/components/DescriptionList/DescriptionList.stories.tsx +13 -19
  148. package/src/components/DescriptionList/DescriptionList.tsx +2 -14
  149. package/src/components/DescriptionList/DescriptionListItem.props.ts +3 -8
  150. package/src/components/DescriptionList/DescriptionListItem.tsx +13 -21
  151. package/src/components/IndicatorIconButton/IndicatorIconButton.docs.mdx +85 -0
  152. package/src/components/IndicatorIconButton/IndicatorIconButton.props.ts +12 -0
  153. package/src/components/IndicatorIconButton/IndicatorIconButton.stories.tsx +142 -0
  154. package/src/components/IndicatorIconButton/IndicatorIconButton.tsx +36 -0
  155. package/src/components/IndicatorIconButton/index.tsx +2 -0
  156. package/src/components/Link/LinkText.tsx +4 -4
  157. package/src/components/List/List.context.ts +0 -1
  158. package/src/components/List/List.docs.mdx +376 -179
  159. package/src/components/List/List.props.ts +1 -9
  160. package/src/components/List/List.stories.tsx +289 -38
  161. package/src/components/List/List.tsx +5 -26
  162. package/src/components/List/ListAction/ListAction.props.ts +10 -0
  163. package/src/components/List/ListAction/ListAction.tsx +133 -0
  164. package/src/components/List/ListAction/ListActionContent.tsx +21 -0
  165. package/src/components/List/ListAction/ListActionText.tsx +14 -0
  166. package/src/components/List/ListAction/ListActionTrailingContent.tsx +9 -0
  167. package/src/components/List/ListAction/ListActionTrailingIcon.tsx +32 -0
  168. package/src/components/List/ListAction/index.ts +6 -0
  169. package/src/components/List/ListItem/ListItem.context.ts +1 -1
  170. package/src/components/List/ListItem/ListItem.props.ts +9 -5
  171. package/src/components/List/ListItem/ListItemRoot.tsx +18 -14
  172. package/src/components/List/ListItem/index.ts +4 -4
  173. package/src/components/List/index.ts +1 -0
  174. package/src/components/ProgressStepper/ProgressStep.tsx +134 -0
  175. package/src/components/ProgressStepper/ProgressStepper.docs.mdx +87 -0
  176. package/src/components/ProgressStepper/ProgressStepper.props.ts +27 -0
  177. package/src/components/ProgressStepper/ProgressStepper.stories.tsx +108 -0
  178. package/src/components/ProgressStepper/ProgressStepper.tsx +26 -0
  179. package/src/components/ProgressStepper/ProgressStepperRoot.tsx +32 -0
  180. package/src/components/ProgressStepper/index.ts +3 -0
  181. package/src/components/SectionHeader/SectionHeader.props.ts +9 -16
  182. package/src/components/SectionHeader/SectionHeader.stories.tsx +28 -18
  183. package/src/components/SectionHeader/SectionHeader.tsx +18 -19
  184. package/src/components/SectionHeader/SectionHeaderTrailingContent.tsx +20 -0
  185. package/src/components/SectionHeader/Sectionheader.docs.mdx +9 -24
  186. package/src/components/SectionHeader/index.ts +1 -0
  187. package/src/components/Switch/Switch.docs.mdx +0 -4
  188. package/src/components/Tabs/Tab.tsx +4 -2
  189. package/src/components/ThemedImage/ThemedImage.docs.mdx +208 -0
  190. package/src/components/ThemedImage/ThemedImage.props.ts +15 -0
  191. package/src/components/ThemedImage/ThemedImage.stories.tsx +175 -0
  192. package/src/components/ThemedImage/ThemedImage.tsx +34 -0
  193. package/src/components/ThemedImage/index.tsx +2 -0
  194. package/src/components/ToggleButton/ToggleButtonText.tsx +3 -3
  195. package/src/components/UnstyledIconButton/UnstyledIconButton.props.ts +2 -1
  196. package/src/components/index.ts +5 -0
  197. package/src/hooks/useStyleProps.ts +1 -1
  198. package/src/tokens/components/dark/button.ts +1 -1
  199. package/src/tokens/components/dark/dialog.ts +1 -0
  200. package/src/tokens/components/dark/illustrations.ts +1 -0
  201. package/src/tokens/components/dark/toast.ts +4 -1
  202. package/src/tokens/components/light/button.ts +1 -1
  203. package/src/tokens/components/light/dialog.ts +1 -0
  204. package/src/tokens/components/light/illustrations.ts +1 -0
  205. package/src/tokens/components/light/toast.ts +4 -1
  206. package/src/tokens/layout.ts +3 -6
  207. package/src/vite-env.d.ts +6 -0
@@ -1,15 +1,15 @@
1
1
  import type { TextProps, TextStyle } from 'react-native';
2
2
  import { StyleSheet } from 'react-native-unistyles';
3
- import { DetailText } from '../DetailText';
3
+ import { BodyText } from '../BodyText';
4
4
  import { useAlertContext } from './Alert.context';
5
5
 
6
6
  const AlertTitle = ({ children, style, ...props }: TextProps) => {
7
7
  const { colorScheme } = useAlertContext();
8
8
  styles.useVariants({ colorScheme });
9
9
  return (
10
- <DetailText size="md" style={[styles.title as TextStyle, style]} {...props}>
10
+ <BodyText size="md" weight="semibold" style={[styles.title as TextStyle, style]} {...props}>
11
11
  {children}
12
- </DetailText>
12
+ </BodyText>
13
13
  );
14
14
  };
15
15
 
@@ -18,16 +18,16 @@ const styles = StyleSheet.create(theme => ({
18
18
  variants: {
19
19
  colorScheme: {
20
20
  info: {
21
- color: theme.color.feedback.info.foreground,
21
+ color: theme.color.feedback.info.foreground.default,
22
22
  },
23
23
  positive: {
24
- color: theme.color.feedback.positive.foreground,
24
+ color: theme.color.feedback.positive.foreground.default,
25
25
  },
26
26
  danger: {
27
- color: theme.color.feedback.danger.foreground,
27
+ color: theme.color.feedback.danger.foreground.default,
28
28
  },
29
29
  warning: {
30
- color: theme.color.feedback.warning.foreground,
30
+ color: theme.color.feedback.warning.foreground.default,
31
31
  },
32
32
  },
33
33
  },
@@ -19,6 +19,7 @@ interface BadgeProps extends ViewProps {
19
19
  size?: 'sm' | 'md';
20
20
  icon?: ComponentType;
21
21
  flatBase?: boolean;
22
+ text?: string | number;
22
23
  }
23
24
 
24
25
  export default BadgeProps;
@@ -14,6 +14,7 @@ const Badge = ({ children, ...props }: BadgeProps) => {
14
14
  flatBase = false,
15
15
  size = 'sm',
16
16
  style,
17
+ text,
17
18
  ...rest
18
19
  } = props;
19
20
 
@@ -22,7 +23,7 @@ const Badge = ({ children, ...props }: BadgeProps) => {
22
23
  [colorScheme, flatBase, variant, size]
23
24
  );
24
25
 
25
- const childIsText = typeof children === 'string' || typeof children === 'number';
26
+ const childIsText = typeof children === 'string' || typeof children === 'number' || !!text;
26
27
 
27
28
  styles.useVariants({ colorScheme, flatBase, variant, size });
28
29
 
@@ -30,7 +31,7 @@ const Badge = ({ children, ...props }: BadgeProps) => {
30
31
  <BadgeContext.Provider value={value}>
31
32
  <View {...rest} style={[styles.container, style]}>
32
33
  {!!icon && <BadgeIcon as={icon} />}
33
- {childIsText ? <BadgeText>{children}</BadgeText> : children}
34
+ {childIsText ? <BadgeText>{text ?? children}</BadgeText> : children}
34
35
  </View>
35
36
  </BadgeContext.Provider>
36
37
  );
@@ -103,6 +103,7 @@ const styles = StyleSheet.create(theme => ({
103
103
  ghost: {
104
104
  backgroundColor: 'transparent',
105
105
  borderWidth: 0,
106
+ textDecoration: 'underline',
106
107
  },
107
108
  },
108
109
  size: {
@@ -348,6 +349,7 @@ const styles = StyleSheet.create(theme => ({
348
349
  variant: 'ghost',
349
350
  colorScheme: 'functional',
350
351
  styles: {
352
+ textDecorationColor: theme.color.interactive.functional.foreground.subtle,
351
353
  _web: {
352
354
  _hover: {
353
355
  backgroundColor: theme.color.interactive.functional.surface.subtle.hover,
@@ -395,6 +397,7 @@ const styles = StyleSheet.create(theme => ({
395
397
  variant: 'ghost',
396
398
  colorScheme: 'affirmative',
397
399
  styles: {
400
+ textDecorationColor: theme.color.interactive.affirmative.foreground.subtle,
398
401
  _web: {
399
402
  _hover: {
400
403
  backgroundColor: theme.color.interactive.affirmative.surface.subtle.hover,
@@ -418,6 +421,7 @@ const styles = StyleSheet.create(theme => ({
418
421
  variant: 'ghost',
419
422
  colorScheme: 'destructive',
420
423
  styles: {
424
+ textDecorationColor: theme.color.interactive.destructive.foreground.subtle,
421
425
  _web: {
422
426
  _hover: {
423
427
  backgroundColor: theme.color.interactive.destructive.surface.subtle.hover,
@@ -1,15 +1,15 @@
1
1
  import { TextProps } from 'react-native';
2
2
  import { StyleSheet } from 'react-native-unistyles';
3
- import { DetailText } from '../DetailText';
3
+ import { BodyText } from '../BodyText';
4
4
  import { useButtonContext } from './Button.context';
5
5
 
6
6
  const ButtonText = ({ children, ...props }: TextProps) => {
7
7
  const { colorScheme, variant, inverted, disabled } = useButtonContext();
8
8
  styles.useVariants({ colorScheme, variant, inverted, disabled });
9
9
  return (
10
- <DetailText size="md" {...props} style={[styles.text, props.style]}>
10
+ <BodyText size="md" weight="semibold" {...props} style={[styles.text, props.style]}>
11
11
  {children}
12
- </DetailText>
12
+ </BodyText>
13
13
  );
14
14
  };
15
15
 
@@ -85,6 +85,8 @@ const Card = ({
85
85
  disabled={disabled}
86
86
  style={[styles.card, computedStyles, style as ViewStyle]}
87
87
  onPress={handlePress}
88
+ accessible={showPressed}
89
+ importantForAccessibility={showPressed ? 'yes' : 'no'}
88
90
  >
89
91
  {children}
90
92
  </Pressable>
@@ -0,0 +1,8 @@
1
+ import { createContext, useContext } from 'react';
2
+ import { CarouselContextValue } from './Carousel.props';
3
+
4
+ const CarouselContext = createContext<CarouselContextValue>({} as CarouselContextValue);
5
+
6
+ export const useCarouselContext = () => useContext(CarouselContext);
7
+
8
+ export default CarouselContext;
@@ -0,0 +1,389 @@
1
+ import { Canvas, Controls, Meta, Story } from '@storybook/addon-docs/blocks';
2
+ import { BodyText, Box, Center } from '../../';
3
+ import {
4
+ BackToTopButton,
5
+ DocComponentWrap,
6
+ UsageWrap,
7
+ ViewFigmaButton,
8
+ } from '../../../docs/components';
9
+ import { Carousel, CarouselControls, CarouselItem } from './';
10
+ import * as Stories from './Carousel.stories';
11
+
12
+ <Meta title="Components / Carousel" />
13
+
14
+ <ViewFigmaButton url="https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR/Hearth-Components---Tokens?node-id=5190-621" />
15
+
16
+ <BackToTopButton />
17
+
18
+ # Carousel
19
+
20
+ Carousels provide users with a dynamic and interactive way to browse through a series of content items, such as images, articles, or products. It allows users to view multiple items within a confined space, enabling efficient content discovery and engagement.
21
+
22
+ Carousel Controls are used to clearly indicate multiple pieces of content and to allow a user to navigate between them.
23
+
24
+ - [Playground](#playground)
25
+ - [Usage](#usage)
26
+ - [Basic Usage](#basic-usage)
27
+ - [Carousel with Built-in Controls](#carousel-with-built-in-controls)
28
+ - [Carousel with Navigation Buttons](#carousel-with-navigation-buttons)
29
+ - [Carousel with Custom Controls](#carousel-with-custom-controls)
30
+ - [Controlling Carousel via Ref](#controlling-carousel-via-ref)
31
+ - [Centered Fixed-Width Items](#centered-fixed-width-items)
32
+ - [Props](#props)
33
+ - [Accessibility](#accessibility)
34
+
35
+ ## Playground
36
+
37
+ <Canvas of={Stories.Playground} />
38
+
39
+ <Controls of={Stories.Playground} />
40
+
41
+ ## Usage
42
+
43
+ - The `Carousel` component is to be used for presenting scrollable data
44
+ - The `Carousel` scrolls horizontally only and works on both web and native platforms
45
+ - Most `Carousel` behaviour is predefined for a standardised behaviour and experience across apps
46
+ - Pagination controls are built-in by default and can be customised or hidden
47
+ - The carousel prevents scrolling more than one item at a time for a consistent user experience
48
+ - Some props can be modified to offer customised behaviour
49
+
50
+ ## Basic Usage
51
+
52
+ <UsageWrap>
53
+ <Center flex={1}>
54
+ <Carousel width={300}>
55
+ <CarouselItem>
56
+ <Box p="100" aspectRatio={1.6} backgroundColor="purple800">
57
+ <BodyText color="white">I'm a Carousel item!</BodyText>
58
+ </Box>
59
+ </CarouselItem>
60
+ <CarouselItem>
61
+ <Box p="100" aspectRatio={1.6} backgroundColor="red800">
62
+ <BodyText color="white">I'm another Carousel item!</BodyText>
63
+ </Box>
64
+ </CarouselItem>
65
+ </Carousel>
66
+ </Center>
67
+ </UsageWrap>
68
+
69
+ ```jsx
70
+ import { BodyText, Box, Carousel, CarouselItem } from '@utilitywarehouse/hearth-react-native';
71
+
72
+ const MyComponent = () => (
73
+ <Carousel width={300}>
74
+ <CarouselItem>
75
+ <Box p="100" aspectRatio={1.6} backgroundColor="purple800">
76
+ <BodyText color="white">I'm a Carousel item!</BodyText>
77
+ </Box>
78
+ </CarouselItem>
79
+ <CarouselItem>
80
+ <Box p="100" aspectRatio={1.6} backgroundColor="red800">
81
+ <BodyText color="white">I'm another Carousel item!</BodyText>
82
+ </Box>
83
+ </CarouselItem>
84
+ </Carousel>
85
+ );
86
+ ```
87
+
88
+ ### Carousel with Built-in Controls
89
+
90
+ The `Carousel` component includes built-in pagination controls by default. You can customise their appearance or hide them entirely using props.
91
+
92
+ <UsageWrap>
93
+ <Center flex={1}>
94
+ <Carousel width={300}>
95
+ <CarouselItem>
96
+ <Box p="100" aspectRatio={1.6} backgroundColor="purple800">
97
+ <BodyText color="white">I'm a Carousel item!</BodyText>
98
+ </Box>
99
+ </CarouselItem>
100
+ <CarouselItem>
101
+ <Box p="100" aspectRatio={1.6} backgroundColor="red800">
102
+ <BodyText color="white">I'm another Carousel item!</BodyText>
103
+ </Box>
104
+ </CarouselItem>
105
+ </Carousel>
106
+ </Center>
107
+ </UsageWrap>
108
+
109
+ ```jsx
110
+ import { BodyText, Box, Carousel, CarouselItem } from '@utilitywarehouse/hearth-react-native';
111
+
112
+ const MyComponent = () => (
113
+ <Carousel width={300}>
114
+ <CarouselItem>
115
+ <Box p="100" aspectRatio={1.6} backgroundColor="purple800">
116
+ <BodyText color="white">I'm a Carousel item!</BodyText>
117
+ </Box>
118
+ </CarouselItem>
119
+ <CarouselItem>
120
+ <Box p="100" aspectRatio={1.6} backgroundColor="red800">
121
+ <BodyText color="white">I'm another Carousel item!</BodyText>
122
+ </Box>
123
+ </CarouselItem>
124
+ </Carousel>
125
+ );
126
+ ```
127
+
128
+ ### Carousel with Navigation Buttons
129
+
130
+ Add previous and next navigation buttons to the controls by setting `showNavigation` to `true`.
131
+
132
+ <UsageWrap>
133
+ <Center flex={1}>
134
+ <Carousel width={300} showNavigation>
135
+ <CarouselItem>
136
+ <Box p="100" aspectRatio={1.6} backgroundColor="purple800">
137
+ <BodyText color="white">I'm a Carousel item!</BodyText>
138
+ </Box>
139
+ </CarouselItem>
140
+ <CarouselItem>
141
+ <Box p="100" aspectRatio={1.6} backgroundColor="red800">
142
+ <BodyText color="white">I'm another Carousel item!</BodyText>
143
+ </Box>
144
+ </CarouselItem>
145
+ </Carousel>
146
+ </Center>
147
+ </UsageWrap>
148
+
149
+ ```jsx
150
+ import { BodyText, Box, Carousel, CarouselItem } from '@utilitywarehouse/hearth-react-native';
151
+
152
+ const MyComponent = () => (
153
+ <Carousel width={300} showNavigation>
154
+ <CarouselItem>
155
+ <Box p="100" aspectRatio={1.6} backgroundColor="purple800">
156
+ <BodyText color="white">I'm a Carousel item!</BodyText>
157
+ </Box>
158
+ </CarouselItem>
159
+ <CarouselItem>
160
+ <Box p="100" aspectRatio={1.6} backgroundColor="red800">
161
+ <BodyText color="white">I'm another Carousel item!</BodyText>
162
+ </Box>
163
+ </CarouselItem>
164
+ </Carousel>
165
+ );
166
+ ```
167
+
168
+ ### Carousel with Custom Controls
169
+
170
+ The `CarouselControls` component is to be used in conjunction with the `Carousel` component. This component requires no props and automatically utilises the carousel context. When custom controls are provided, the built-in controls are automatically hidden.
171
+
172
+ <UsageWrap>
173
+ <Center flex={1}>
174
+ <Carousel width={300}>
175
+ <CarouselItem>
176
+ <Box p="100" aspectRatio={1.6} backgroundColor="purple800">
177
+ <BodyText color="white">I'm a Carousel item!</BodyText>
178
+ </Box>
179
+ </CarouselItem>
180
+ <CarouselItem>
181
+ <Box p="100" aspectRatio={1.6} backgroundColor="red800">
182
+ <BodyText color="white">I'm another Carousel item!</BodyText>
183
+ </Box>
184
+ </CarouselItem>
185
+ <CarouselControls style={{ marginTop: 16 }} />
186
+ </Carousel>
187
+ </Center>
188
+ </UsageWrap>
189
+
190
+ ```jsx
191
+ import { BodyText, Box, Carousel, CarouselControls } from '@utilitywarehouse/hearth-react-native';
192
+
193
+ const MyComponent = () => (
194
+ <Carousel width={300}>
195
+ <CarouselItem>
196
+ <Box p="100" aspectRatio={1.6} backgroundColor="purple800">
197
+ <BodyText color="white">I'm a Carousel item!</BodyText>
198
+ </Box>
199
+ </CarouselItem>
200
+ <CarouselItem>
201
+ <Box p="100" aspectRatio={1.6} backgroundColor="red800">
202
+ <BodyText color="white">I'm another Carousel item!</BodyText>
203
+ </Box>
204
+ </CarouselItem>
205
+ <CarouselControls style={{ marginTop: 16 }} />
206
+ </Carousel>
207
+ );
208
+ ```
209
+
210
+ ### Controlling Carousel via Ref
211
+
212
+ You can control the carousel programmatically using a ref. The carousel exposes `scrollToIndex` and `scrollToOffset` methods.
213
+
214
+ ```jsx
215
+ import { useRef } from 'react';
216
+ import {
217
+ BodyText,
218
+ Box,
219
+ Carousel,
220
+ CarouselItem,
221
+ Button,
222
+ } from '@utilitywarehouse/hearth-react-native';
223
+
224
+ const MyComponent = () => {
225
+ const carouselRef = useRef(null);
226
+
227
+ const goToSlide = index => {
228
+ carouselRef.current?.scrollToIndex({ index, animated: true });
229
+ };
230
+
231
+ return (
232
+ <>
233
+ <Carousel ref={carouselRef} width={300}>
234
+ <CarouselItem>
235
+ <Box p="100" aspectRatio={1.6} backgroundColor="purple800">
236
+ <BodyText color="white">Slide 1</BodyText>
237
+ </Box>
238
+ </CarouselItem>
239
+ <CarouselItem>
240
+ <Box p="100" aspectRatio={1.6} backgroundColor="red800">
241
+ <BodyText color="white">Slide 2</BodyText>
242
+ </Box>
243
+ </CarouselItem>
244
+ </Carousel>
245
+ <Button onPress={() => goToSlide(1)}>Go to Slide 2</Button>
246
+ </>
247
+ );
248
+ };
249
+ ```
250
+
251
+ ### Centered Fixed-Width Items
252
+
253
+ You can create a carousel with centered items that are smaller than the viewport width, showing partial views of adjacent items.
254
+
255
+ ```jsx
256
+ import { BodyText, Box, Carousel, CarouselItem } from '@utilitywarehouse/hearth-react-native';
257
+
258
+ const MyComponent = () => {
259
+ const carouselWidth = 400;
260
+ const itemWidth = carouselWidth * 0.8;
261
+
262
+ return (
263
+ <Carousel width={carouselWidth} itemWidth={itemWidth} centered showOverflow>
264
+ <CarouselItem>
265
+ <Box p="100" aspectRatio={1.6} backgroundColor="purple800">
266
+ <BodyText color="white">I'm a Carousel item!</BodyText>
267
+ </Box>
268
+ </CarouselItem>
269
+ <CarouselItem>
270
+ <Box p="100" aspectRatio={1.6} backgroundColor="red800">
271
+ <BodyText color="white">I'm another Carousel item!</BodyText>
272
+ </Box>
273
+ </CarouselItem>
274
+ </Carousel>
275
+ );
276
+ };
277
+ ```
278
+
279
+ ## Props
280
+
281
+ ### `Carousel`
282
+
283
+ This wraps a carousel in dynamic context context, automatically providing all functionality for `CarouselItem`, and `CarouselControls`.
284
+
285
+ The `Carousel` component automatically updates the carousel context when interacted with.
286
+
287
+ The `Carousel` component accepts most props from the React Native `FlatList` component, while other props are predfined and cannot be modified. There are also some additional carousel specific props:
288
+
289
+ | Name | Type | Default | Description |
290
+ | ---------------------------- | --------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------ |
291
+ | children? | ReactElement[] | - | Accepts React elements to render as carousel items. To function correctly, these should be `CarouselItem` components. |
292
+ | width | number | - | Carousel viewport width (required) |
293
+ | disabled? | boolean | false | Enable or disable the carousel |
294
+ | inverted? | boolean | false | Invert the carousel control colours (for brand backgrounds) |
295
+ | centered? | boolean | false | Center items within the carousel viewport (use with `itemWidth` for peek effect) |
296
+ | itemWidth? | number | - | Specify the width of carousel items if different from `width`. Useful for revealing the previous/next item within the viewport |
297
+ | inactiveItemOpacity? | number | 1 | Fade items outside of the active carousel viewport (use in conjunction with `showOverflow`) |
298
+ | showOverflow? | boolean | false | Visually reveal the items outside of the carousel viewport |
299
+ | showControls? | boolean | true | Show or hide the built-in pagination controls |
300
+ | showNavigation? | boolean | false | Show previous/next navigation buttons in the controls |
301
+ | controlsStyle? | ViewStyle | - | Style the pagination controls container |
302
+ | controlsItemStyle? | ViewStyle | - | Style the pagination dots |
303
+ | controlsActiveItemStyle? | ViewStyle | - | Style the active pagination dot |
304
+ | controlsAccessibilityHidden? | boolean | true | Hide controls from screen readers by default |
305
+ | onSnapToItem? | (index) => void | - | Callback each time a new item is focused in the carousel viewport |
306
+ | ref? | CarouselRef | - | Ref to control carousel programmatically (exposes `scrollToIndex` and `scrollToOffset`) |
307
+
308
+ For more information about any of the above props from the `FlatList` component, see the React Native FlatList [documentation](https://reactnative.dev/docs/flatlist).
309
+
310
+ ### `CarouselItem`
311
+
312
+ The `CarouselItem` component automatically receives props from the parent `CarouselItems` component. Please ensure that you always wrap each of your carousel items in `CarouselItem`.
313
+
314
+ | Name | Type | Default | Description |
315
+ | --------- | ------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
316
+ | children? | ReactElement | - | Display any content within each carousel item. |
317
+ | key? | number/string | - | Provides a unique render key for each carousel item. If no `key` is provided then it will fall back to the `id`, then array index |
318
+
319
+ ### `CarouselControls`
320
+
321
+ The `CarouselControls` component automatically receives props from the parent `Carousel` component. Please ensure that you always wrap the `CarouselControls` component within the `Carousel` component.
322
+
323
+ The controls are interactive - clicking on any pagination dot will navigate to that carousel item.
324
+
325
+ | Name | Type | Default | Description |
326
+ | -------------------- | ---------- | ------- | ----------------------------------------------------------------------------------- |
327
+ | style? | ViewStyle | - | Style the pagination component |
328
+ | itemStyle? | ViewStyle | - | Style the pagination item |
329
+ | activeItemStyle? | ViewStyle | - | Style the active pagination item |
330
+ | showNavigation? | boolean | false | Show previous/next navigation buttons |
331
+ | accessibilityHidden? | boolean | true | Hide controls from screen readers (inherits from Carousel context if not specified) |
332
+ | onPressPrev? | () => void | - | Callback when previous button is pressed |
333
+ | onPressNext? | () => void | - | Callback when next button is pressed |
334
+
335
+ ## Accessibility
336
+
337
+ By default, carousel controls are hidden from screen readers (`controlsAccessibilityHidden={true}`). This is because the carousel items themselves should be accessible, and the controls are primarily for visual navigation.
338
+
339
+ ### Screen Reader Navigation
340
+
341
+ When using a screen reader, users will navigate through the carousel items in order automatically. The screen reader will focus on the content of each `CarouselItem` sequentially, allowing users to swipe through items naturally.
342
+
343
+ **Important:** You must add appropriate accessibility labels and hints to the content within each carousel item. The carousel itself does not automatically provide accessibility labels for item content.
344
+
345
+ ```jsx
346
+ <Carousel width={300}>
347
+ <CarouselItem>
348
+ <Box p="100" aspectRatio={1.6} backgroundColor="purple800">
349
+ <BodyText
350
+ color="white"
351
+ accessibilityLabel="Special offer: 20% off your next bill"
352
+ accessibilityHint="Double tap to view offer details"
353
+ >
354
+ 20% Off!
355
+ </BodyText>
356
+ </Box>
357
+ </CarouselItem>
358
+ <CarouselItem>
359
+ <Box p="100" aspectRatio={1.6} backgroundColor="red800">
360
+ <BodyText
361
+ color="white"
362
+ accessibilityLabel="Refer a friend and earn rewards"
363
+ accessibilityHint="Double tap to learn more about referrals"
364
+ >
365
+ Refer & Earn
366
+ </BodyText>
367
+ </Box>
368
+ </CarouselItem>
369
+ </Carousel>
370
+ ```
371
+
372
+ ### Making Controls Accessible
373
+
374
+ To make controls accessible to screen readers:
375
+
376
+ ```jsx
377
+ // Make controls visible to screen readers globally
378
+ <Carousel width={300} controlsAccessibilityHidden={false}>
379
+ {/* items */}
380
+ </Carousel>
381
+
382
+ // Or override for custom controls
383
+ <Carousel width={300}>
384
+ {/* items */}
385
+ <CarouselControls accessibilityHidden={false} />
386
+ </Carousel>
387
+ ```
388
+
389
+ The navigation buttons (when `showNavigation` is enabled) include proper accessibility labels for screen readers.
@@ -0,0 +1,89 @@
1
+ import { ReactElement } from 'react';
2
+ import { FlatListProps, PressableProps, ViewProps, ViewStyle } from 'react-native';
3
+
4
+ export interface CarouselRef {
5
+ scrollToIndex: (params: { index: number; animated?: boolean | null }) => void;
6
+ scrollToOffset: (params: { offset: number; animated?: boolean | null }) => void;
7
+ }
8
+
9
+ export interface CarouselContextValue {
10
+ activeIndex: number;
11
+ numItems: number;
12
+ setActiveIndex: (index: number) => void;
13
+ setNumItems: (count: number) => void;
14
+ controlsAccessibilityHidden?: boolean;
15
+ inverted?: boolean;
16
+ disabled?: boolean;
17
+ }
18
+
19
+ export interface CarouselItemProps extends ViewProps {
20
+ active?: boolean;
21
+ key?: string | number;
22
+ inactiveOpacity?: number;
23
+ width?: number;
24
+ }
25
+
26
+ export interface CarouselProps
27
+ extends Omit<
28
+ FlatListProps<ReactElement<CarouselItemProps>>,
29
+ | 'accessibilityActions'
30
+ | 'accessibilityLabel'
31
+ | 'accessibilityRole'
32
+ | 'accessible'
33
+ | 'bounces'
34
+ | 'children'
35
+ | 'data'
36
+ | 'decelerationRate'
37
+ | 'getItemLayout'
38
+ | 'horizontal'
39
+ | 'pagingEnabled'
40
+ | 'onAccessibilityAction'
41
+ | 'onViewableItemsChanged'
42
+ | 'overScrollMode'
43
+ | 'renderItem'
44
+ | 'showsHorizontalScrollIndicator'
45
+ | 'snapToInterval'
46
+ | 'snapToAlignment'
47
+ | 'viewabilityConfig'
48
+ | 'style'
49
+ > {
50
+ activeIndex?: number;
51
+ centered?: boolean;
52
+ disabled?: boolean;
53
+ inactiveItemOpacity?: number;
54
+ itemWidth?: number;
55
+ onSnapToItem?: (index: number) => void;
56
+ showOverflow?: boolean;
57
+ width: number;
58
+ children?: ViewProps['children'];
59
+ itemsStyle?: FlatListProps<ReactElement<CarouselItemProps>>['style'];
60
+ style?: ViewStyle;
61
+ showControls?: boolean;
62
+ showNavigation?: boolean;
63
+ controlsStyle?: ViewProps['style'];
64
+ controlsItemStyle?: ViewProps['style'];
65
+ controlsActiveItemStyle?: ViewProps['style'];
66
+ controlsAccessibilityHidden?: boolean;
67
+ ref?: React.Ref<CarouselRef>;
68
+ inverted?: boolean;
69
+ }
70
+
71
+ export interface CarouselProviderProps {
72
+ initialActiveIndex?: number;
73
+ }
74
+
75
+ export interface CarouselControlsProps extends ViewProps {
76
+ itemStyle?: ViewProps['style'];
77
+ activeItemStyle?: ViewProps['style'];
78
+ showNavigation?: boolean;
79
+ onPressPrev?: () => void;
80
+ onPressNext?: () => void;
81
+ accessibilityHidden?: boolean;
82
+ }
83
+
84
+ export interface CarouselControlsItemProps extends Omit<PressableProps, 'style'> {
85
+ active: boolean;
86
+ index: number;
87
+ style?: ViewProps['style'];
88
+ activeStyle?: ViewProps['style'];
89
+ }