@utilitywarehouse/hearth-react-native 0.7.0 → 0.8.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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +18 -0
- package/build/components/Banner/Banner.d.ts +6 -0
- package/build/components/Banner/Banner.js +161 -0
- package/build/components/Banner/Banner.props.d.ts +82 -0
- package/build/components/Banner/Banner.props.js +1 -0
- package/build/components/Banner/index.d.ts +2 -0
- package/build/components/Banner/index.js +1 -0
- package/build/components/BottomSheet/BottomSheetBackdrop.js +1 -5
- package/build/components/BottomSheet/BottomSheetFlatList.js +1 -5
- package/build/components/BottomSheet/BottomSheetHandle.js +1 -5
- package/build/components/BottomSheet/useBottomSheetLogic.d.ts +1 -1
- package/build/components/Expandable/Expandable.d.ts +6 -0
- package/build/components/Expandable/Expandable.js +44 -0
- package/build/components/Expandable/Expandable.props.d.ts +38 -0
- package/build/components/Expandable/Expandable.props.js +1 -0
- package/build/components/Expandable/index.d.ts +2 -0
- package/build/components/Expandable/index.js +1 -0
- package/build/components/ExpandableCard/ExpandableCard.d.ts +6 -0
- package/build/components/ExpandableCard/ExpandableCard.js +23 -0
- package/build/components/ExpandableCard/ExpandableCard.props.d.ts +69 -0
- package/build/components/ExpandableCard/ExpandableCard.props.js +1 -0
- package/build/components/ExpandableCard/ExpandableCardContent.d.ts +6 -0
- package/build/components/ExpandableCard/ExpandableCardContent.js +14 -0
- package/build/components/ExpandableCard/ExpandableCardExpandedContent.d.ts +11 -0
- package/build/components/ExpandableCard/ExpandableCardExpandedContent.js +18 -0
- package/build/components/ExpandableCard/ExpandableCardGroup.d.ts +6 -0
- package/build/components/ExpandableCard/ExpandableCardGroup.js +17 -0
- package/build/components/ExpandableCard/ExpandableCardGroup.props.d.ts +25 -0
- package/build/components/ExpandableCard/ExpandableCardGroup.props.js +1 -0
- package/build/components/ExpandableCard/ExpandableCardHelperText.d.ts +6 -0
- package/build/components/ExpandableCard/ExpandableCardHelperText.js +13 -0
- package/build/components/ExpandableCard/ExpandableCardIcon.d.ts +9 -0
- package/build/components/ExpandableCard/ExpandableCardIcon.js +19 -0
- package/build/components/ExpandableCard/ExpandableCardLeadingContent.d.ts +6 -0
- package/build/components/ExpandableCard/ExpandableCardLeadingContent.js +5 -0
- package/build/components/ExpandableCard/ExpandableCardText.d.ts +6 -0
- package/build/components/ExpandableCard/ExpandableCardText.js +7 -0
- package/build/components/ExpandableCard/ExpandableCardTrailingContent.d.ts +6 -0
- package/build/components/ExpandableCard/ExpandableCardTrailingContent.js +5 -0
- package/build/components/ExpandableCard/ExpandableCardTrailingIcon.d.ts +9 -0
- package/build/components/ExpandableCard/ExpandableCardTrailingIcon.js +17 -0
- package/build/components/ExpandableCard/ExpandableCardTrigger.d.ts +17 -0
- package/build/components/ExpandableCard/ExpandableCardTrigger.js +7 -0
- package/build/components/ExpandableCard/ExpandableCardTrigger.props.d.ts +44 -0
- package/build/components/ExpandableCard/ExpandableCardTrigger.props.js +1 -0
- package/build/components/ExpandableCard/ExpandableCardTriggerRoot.d.ts +11 -0
- package/build/components/ExpandableCard/ExpandableCardTriggerRoot.js +91 -0
- package/build/components/ExpandableCard/index.d.ts +14 -0
- package/build/components/ExpandableCard/index.js +11 -0
- package/build/components/HighlightBanner/HighlightBanner.d.ts +6 -0
- package/build/components/HighlightBanner/HighlightBanner.js +96 -0
- package/build/components/HighlightBanner/HighlightBanner.props.d.ts +14 -0
- package/build/components/HighlightBanner/HighlightBanner.props.js +1 -0
- package/build/components/HighlightBanner/index.d.ts +2 -0
- package/build/components/HighlightBanner/index.js +1 -0
- package/build/components/Spinner/Spinner.js +0 -2
- package/build/components/Spinner/Spinner.web.d.ts +2 -1
- package/build/components/Spinner/Spinner.web.js +0 -2
- package/build/components/Switch/Switch.web.js +0 -1
- package/build/components/Tabs/TabsList.js +1 -6
- package/build/components/index.d.ts +4 -0
- package/build/components/index.js +4 -0
- package/docs/components/AllComponents.web.tsx +75 -4
- package/docs/components/VariantTitle.tsx +1 -1
- package/package.json +4 -4
- package/src/components/Banner/Banner.docs.mdx +402 -0
- package/src/components/Banner/Banner.props.ts +106 -0
- package/src/components/Banner/Banner.stories.tsx +494 -0
- package/src/components/Banner/Banner.tsx +264 -0
- package/src/components/Banner/index.ts +2 -0
- package/src/components/BottomSheet/BottomSheetBackdrop.tsx +0 -1
- package/src/components/BottomSheet/BottomSheetFlatList.tsx +0 -1
- package/src/components/BottomSheet/BottomSheetHandle.tsx +0 -1
- package/src/components/Card/Card.docs.mdx +10 -2
- package/src/components/Expandable/Expandable.docs.mdx +201 -0
- package/src/components/Expandable/Expandable.props.ts +46 -0
- package/src/components/Expandable/Expandable.stories.tsx +284 -0
- package/src/components/Expandable/Expandable.tsx +92 -0
- package/src/components/Expandable/index.ts +2 -0
- package/src/components/ExpandableCard/ExpandableCard.docs.mdx +312 -0
- package/src/components/ExpandableCard/ExpandableCard.props.ts +85 -0
- package/src/components/ExpandableCard/ExpandableCard.stories.tsx +326 -0
- package/src/components/ExpandableCard/ExpandableCard.tsx +76 -0
- package/src/components/ExpandableCard/ExpandableCardContent.tsx +21 -0
- package/src/components/ExpandableCard/ExpandableCardExpandedContent.tsx +42 -0
- package/src/components/ExpandableCard/ExpandableCardGroup.props.ts +31 -0
- package/src/components/ExpandableCard/ExpandableCardGroup.tsx +40 -0
- package/src/components/ExpandableCard/ExpandableCardHelperText.tsx +21 -0
- package/src/components/ExpandableCard/ExpandableCardIcon.tsx +32 -0
- package/src/components/ExpandableCard/ExpandableCardLeadingContent.tsx +9 -0
- package/src/components/ExpandableCard/ExpandableCardText.tsx +14 -0
- package/src/components/ExpandableCard/ExpandableCardTrailingContent.tsx +9 -0
- package/src/components/ExpandableCard/ExpandableCardTrailingIcon.tsx +30 -0
- package/src/components/ExpandableCard/ExpandableCardTrigger.props.ts +47 -0
- package/src/components/ExpandableCard/ExpandableCardTrigger.tsx +10 -0
- package/src/components/ExpandableCard/ExpandableCardTriggerRoot.tsx +154 -0
- package/src/components/ExpandableCard/index.ts +14 -0
- package/src/components/HighlightBanner/HighlightBanner.docs.mdx +296 -0
- package/src/components/HighlightBanner/HighlightBanner.props.ts +29 -0
- package/src/components/HighlightBanner/HighlightBanner.stories.tsx +275 -0
- package/src/components/HighlightBanner/HighlightBanner.tsx +134 -0
- package/src/components/HighlightBanner/index.ts +2 -0
- package/src/components/Spinner/Spinner.tsx +0 -2
- package/src/components/Spinner/Spinner.web.tsx +0 -2
- package/src/components/Switch/Switch.web.tsx +1 -5
- package/src/components/Tabs/TabsList.tsx +0 -2
- package/src/components/index.ts +4 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { ChevronRightSmallIcon, CloseSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
2
|
+
import { Pressable, View } from 'react-native';
|
|
3
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
4
|
+
import { BodyText } from '../BodyText';
|
|
5
|
+
import { Card } from '../Card';
|
|
6
|
+
import { Heading } from '../Heading';
|
|
7
|
+
import { IconContainer } from '../IconContainer';
|
|
8
|
+
import { ThemedImage } from '../ThemedImage';
|
|
9
|
+
import { UnstyledIconButton } from '../UnstyledIconButton';
|
|
10
|
+
import type BannerProps from './Banner.props';
|
|
11
|
+
|
|
12
|
+
const Banner = ({
|
|
13
|
+
icon,
|
|
14
|
+
iconContainerVariant = 'subtle',
|
|
15
|
+
iconContainerSize = 'md',
|
|
16
|
+
iconContainerColor = 'pig',
|
|
17
|
+
illustration,
|
|
18
|
+
image,
|
|
19
|
+
heading,
|
|
20
|
+
description,
|
|
21
|
+
direction = 'horizontal',
|
|
22
|
+
link,
|
|
23
|
+
button,
|
|
24
|
+
onPress,
|
|
25
|
+
onClose,
|
|
26
|
+
variant = 'subtle',
|
|
27
|
+
colorScheme = 'pig',
|
|
28
|
+
style,
|
|
29
|
+
...props
|
|
30
|
+
}: BannerProps) => {
|
|
31
|
+
const hasIllustration = Boolean(illustration);
|
|
32
|
+
styles.useVariants({ direction, hasIllustration });
|
|
33
|
+
|
|
34
|
+
const renderIconOrImage = () => {
|
|
35
|
+
if (icon) {
|
|
36
|
+
return (
|
|
37
|
+
<IconContainer
|
|
38
|
+
icon={icon}
|
|
39
|
+
variant={iconContainerVariant}
|
|
40
|
+
size={iconContainerSize}
|
|
41
|
+
color={iconContainerColor}
|
|
42
|
+
style={styles.media}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
if (illustration) {
|
|
47
|
+
return (
|
|
48
|
+
<ThemedImage
|
|
49
|
+
light={illustration.light}
|
|
50
|
+
dark={illustration.dark}
|
|
51
|
+
style={styles.image}
|
|
52
|
+
accessible
|
|
53
|
+
accessibilityLabel={heading}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
if (image) {
|
|
58
|
+
return (
|
|
59
|
+
<View style={[styles.media, styles.imageWrapper]}>
|
|
60
|
+
<ThemedImage
|
|
61
|
+
light={image.light}
|
|
62
|
+
dark={image.dark}
|
|
63
|
+
style={styles.image}
|
|
64
|
+
accessible
|
|
65
|
+
accessibilityLabel={heading}
|
|
66
|
+
/>
|
|
67
|
+
</View>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const renderAction = () => {
|
|
74
|
+
if (link) {
|
|
75
|
+
return <View style={styles.action}>{link}</View>;
|
|
76
|
+
}
|
|
77
|
+
if (button) {
|
|
78
|
+
return <View style={styles.action}>{button}</View>;
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const content = (
|
|
84
|
+
<View style={styles.container}>
|
|
85
|
+
{renderIconOrImage()}
|
|
86
|
+
<View style={styles.contentContainer}>
|
|
87
|
+
<View style={styles.textContainer}>
|
|
88
|
+
<Heading
|
|
89
|
+
size="sm"
|
|
90
|
+
style={styles.heading}
|
|
91
|
+
textAlign={hasIllustration && direction === 'vertical' ? 'center' : 'left'}
|
|
92
|
+
>
|
|
93
|
+
{heading}
|
|
94
|
+
</Heading>
|
|
95
|
+
<BodyText
|
|
96
|
+
size="md"
|
|
97
|
+
style={styles.description}
|
|
98
|
+
textAlign={hasIllustration && direction === 'vertical' ? 'center' : 'left'}
|
|
99
|
+
>
|
|
100
|
+
{description}
|
|
101
|
+
</BodyText>
|
|
102
|
+
{renderAction()}
|
|
103
|
+
</View>
|
|
104
|
+
{onPress && (
|
|
105
|
+
<UnstyledIconButton
|
|
106
|
+
icon={ChevronRightSmallIcon}
|
|
107
|
+
size="sm"
|
|
108
|
+
onPress={onPress}
|
|
109
|
+
style={styles.chevron}
|
|
110
|
+
/>
|
|
111
|
+
)}
|
|
112
|
+
{onClose && (
|
|
113
|
+
<UnstyledIconButton
|
|
114
|
+
icon={CloseSmallIcon}
|
|
115
|
+
size="sm"
|
|
116
|
+
onPress={onClose}
|
|
117
|
+
style={styles.closeButton}
|
|
118
|
+
accessibilityLabel="Close banner"
|
|
119
|
+
/>
|
|
120
|
+
)}
|
|
121
|
+
</View>
|
|
122
|
+
</View>
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
if (onPress) {
|
|
126
|
+
return (
|
|
127
|
+
<Card variant={variant} style={[styles.card, style]} {...props}>
|
|
128
|
+
<Pressable onPress={onPress} accessibilityRole="button" style={styles.pressable}>
|
|
129
|
+
{content}
|
|
130
|
+
</Pressable>
|
|
131
|
+
</Card>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<Card variant={variant} style={[styles.card, style]} {...props}>
|
|
137
|
+
{content}
|
|
138
|
+
</Card>
|
|
139
|
+
);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
Banner.displayName = 'Banner';
|
|
143
|
+
|
|
144
|
+
const styles = StyleSheet.create(theme => ({
|
|
145
|
+
card: {},
|
|
146
|
+
pressable: {
|
|
147
|
+
width: '100%',
|
|
148
|
+
},
|
|
149
|
+
container: {
|
|
150
|
+
flexDirection: 'row',
|
|
151
|
+
gap: theme.space.lg,
|
|
152
|
+
variants: {
|
|
153
|
+
direction: {
|
|
154
|
+
horizontal: {
|
|
155
|
+
flexDirection: 'row',
|
|
156
|
+
alignItems: 'flex-start',
|
|
157
|
+
},
|
|
158
|
+
vertical: {
|
|
159
|
+
flexDirection: 'column',
|
|
160
|
+
alignItems: 'stretch',
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
hasIllustration: {
|
|
164
|
+
true: {},
|
|
165
|
+
false: {},
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
compoundVariants: [
|
|
169
|
+
{
|
|
170
|
+
direction: 'vertical',
|
|
171
|
+
hasIllustration: false,
|
|
172
|
+
styles: {
|
|
173
|
+
alignItems: 'flex-start',
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
direction: 'vertical',
|
|
178
|
+
hasIllustration: true,
|
|
179
|
+
styles: {
|
|
180
|
+
alignItems: 'center',
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
},
|
|
185
|
+
media: {
|
|
186
|
+
flexShrink: 0,
|
|
187
|
+
variants: {
|
|
188
|
+
direction: {
|
|
189
|
+
horizontal: {},
|
|
190
|
+
vertical: {
|
|
191
|
+
alignSelf: 'flex-start',
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
imageWrapper: {
|
|
197
|
+
variants: {
|
|
198
|
+
direction: {
|
|
199
|
+
horizontal: {},
|
|
200
|
+
vertical: {
|
|
201
|
+
width: '100%',
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
image: {
|
|
207
|
+
borderRadius: theme.borderRadius.md,
|
|
208
|
+
borderWidth: theme.borderWidth[1],
|
|
209
|
+
borderColor: theme.color.border.strong,
|
|
210
|
+
variants: {
|
|
211
|
+
direction: {
|
|
212
|
+
horizontal: { width: 160, height: 95 },
|
|
213
|
+
vertical: {
|
|
214
|
+
width: '100%',
|
|
215
|
+
height: 160,
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
contentContainer: {
|
|
221
|
+
flex: 1,
|
|
222
|
+
flexDirection: 'row',
|
|
223
|
+
alignItems: 'flex-start',
|
|
224
|
+
justifyContent: 'space-between',
|
|
225
|
+
gap: theme.space.lg,
|
|
226
|
+
},
|
|
227
|
+
textContainer: {
|
|
228
|
+
flex: 1,
|
|
229
|
+
gap: theme.space.lg,
|
|
230
|
+
},
|
|
231
|
+
heading: {
|
|
232
|
+
compoundVariants: [
|
|
233
|
+
{
|
|
234
|
+
direction: 'vertical',
|
|
235
|
+
hasIllustration: true,
|
|
236
|
+
styles: {
|
|
237
|
+
textAlign: 'center',
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
description: {
|
|
243
|
+
compoundVariants: [
|
|
244
|
+
{
|
|
245
|
+
direction: 'vertical',
|
|
246
|
+
hasIllustration: true,
|
|
247
|
+
styles: {
|
|
248
|
+
textAlign: 'center',
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
action: {
|
|
254
|
+
alignSelf: 'flex-start',
|
|
255
|
+
},
|
|
256
|
+
chevron: {
|
|
257
|
+
alignSelf: 'center',
|
|
258
|
+
},
|
|
259
|
+
closeButton: {
|
|
260
|
+
alignSelf: 'flex-start',
|
|
261
|
+
},
|
|
262
|
+
}));
|
|
263
|
+
|
|
264
|
+
export default Banner;
|
|
@@ -9,7 +9,6 @@ const BottomSheetBackdrop = ({ style, ...props }: BottomSheetDefaultBackdropProp
|
|
|
9
9
|
const theme = useTheme();
|
|
10
10
|
return (
|
|
11
11
|
<StyledBottomSheetBackdrop
|
|
12
|
-
// @ts-expect-error - style prop type issue
|
|
13
12
|
style={[styles.backdrop, style]}
|
|
14
13
|
opacity={theme.components.overlay.opacity / 100}
|
|
15
14
|
appearsOnIndex={0}
|
|
@@ -7,7 +7,6 @@ const StyledBottomSheetHandle = withUnistyles(Handle);
|
|
|
7
7
|
const BottomSheetHandle = ({ style, indicatorStyle, ...props }: BottomSheetDefaultHandleProps) => {
|
|
8
8
|
return (
|
|
9
9
|
<StyledBottomSheetHandle
|
|
10
|
-
// @ts-expect-error - style prop type issue
|
|
11
10
|
style={[styles.handle, style]}
|
|
12
11
|
indicatorStyle={[styles.indicator, indicatorStyle]}
|
|
13
12
|
{...props}
|
|
@@ -17,10 +17,18 @@ A Card component serves as a visual container that groups related content and ac
|
|
|
17
17
|
- [Playground](#playground)
|
|
18
18
|
- [Usage](#usage)
|
|
19
19
|
- [Props](#props)
|
|
20
|
+
- [`CardPressHandler` Props](#cardpresshandler-props)
|
|
21
|
+
- [`CardAction` Props](#cardaction-props)
|
|
20
22
|
- [Variants](#variants)
|
|
21
23
|
- [Examples](#examples)
|
|
22
24
|
- [Interactive](#interactive)
|
|
23
25
|
- [With `CardAction`](#with-cardaction)
|
|
26
|
+
- [`CardAction` Playground](#cardaction-playground)
|
|
27
|
+
- [`CardAction` With Badge](#cardaction-with-badge)
|
|
28
|
+
- [`CardAction` Size Variants](#cardaction-size-variants)
|
|
29
|
+
- [`CardAction` Advanced Usage with Children](#cardaction-advanced-usage-with-children)
|
|
30
|
+
- [`CardAction` Component Parts](#cardaction-component-parts)
|
|
31
|
+
- [`CardAction` Accessibility](#cardaction-accessibility)
|
|
24
32
|
|
|
25
33
|
## Playground
|
|
26
34
|
|
|
@@ -69,13 +77,13 @@ const MyComponent = () => (
|
|
|
69
77
|
| rowGap | `number` | The row gap between the content. | `0` |
|
|
70
78
|
| columnGap | `number` | The column gap between the content. | `0` |
|
|
71
79
|
|
|
72
|
-
### `CardPressHandler`
|
|
80
|
+
### `CardPressHandler` Props
|
|
73
81
|
|
|
74
82
|
| Property | Type | Description | Default |
|
|
75
83
|
| ---------------- | -------- | --------------------------------------------------------------------------- | --------- |
|
|
76
84
|
| handlerToInherit | `string` | The handler to inherit from the child component when the `Card` is pressed. | `onPress` |
|
|
77
85
|
|
|
78
|
-
### `CardAction`
|
|
86
|
+
### `CardAction` Props
|
|
79
87
|
|
|
80
88
|
| Property | Type | Description | Default |
|
|
81
89
|
| -------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ---------- |
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { Canvas, Controls, Meta } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import { BodyText, Button, Card, Center } from '../../';
|
|
3
|
+
import { BackToTopButton, UsageWrap } from '../../../docs/components';
|
|
4
|
+
import * as Stories from './Expandable.stories';
|
|
5
|
+
|
|
6
|
+
<Meta title="Utility Components / Expandable" />
|
|
7
|
+
|
|
8
|
+
<BackToTopButton />
|
|
9
|
+
|
|
10
|
+
# Expandable
|
|
11
|
+
|
|
12
|
+
The Expandable component is a primitive for creating expandable content with smooth animations. It's commonly used as a building block for components like accordions, collapsible sections, and other interactive content that needs to expand and collapse.
|
|
13
|
+
|
|
14
|
+
- [Playground](#playground)
|
|
15
|
+
- [Usage](#usage)
|
|
16
|
+
- [Props](#props)
|
|
17
|
+
- [Animation Duration](#animation-duration)
|
|
18
|
+
- [Examples](#examples)
|
|
19
|
+
- [Basic Example](#basic-example)
|
|
20
|
+
- [With Different Animation Speeds](#with-different-animation-speeds)
|
|
21
|
+
- [Multiple Expandables](#multiple-expandables)
|
|
22
|
+
- [Controlled Example](#controlled-example)
|
|
23
|
+
- [Accessibility](#accessibility)
|
|
24
|
+
|
|
25
|
+
## Playground
|
|
26
|
+
|
|
27
|
+
<Canvas of={Stories.Playground} />
|
|
28
|
+
|
|
29
|
+
<Controls of={Stories.Playground} />
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
<Canvas of={Stories.BasicExample} />
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { useState } from 'react';
|
|
37
|
+
import { View } from 'react-native';
|
|
38
|
+
import { Expandable, Button, Card, BodyText } from '@utilitywarehouse/hearth-react-native';
|
|
39
|
+
|
|
40
|
+
const MyComponent = () => {
|
|
41
|
+
const [expanded, setExpanded] = useState(false);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<View>
|
|
45
|
+
<Button onPress={() => setExpanded(!expanded)}>Toggle Content</Button>
|
|
46
|
+
<Expandable expanded={expanded}>
|
|
47
|
+
<Card>
|
|
48
|
+
<BodyText>This content expands and collapses with a smooth animation.</BodyText>
|
|
49
|
+
</Card>
|
|
50
|
+
</Expandable>
|
|
51
|
+
</View>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Props
|
|
57
|
+
|
|
58
|
+
| Prop | Type | Default | Description |
|
|
59
|
+
| -------------------- | ----------------------------- | ------- | ---------------------------------------------------- |
|
|
60
|
+
| `expanded` | `boolean` | `false` | Whether the content is expanded |
|
|
61
|
+
| `onExpandedChange` | `(expanded: boolean) => void` | - | Callback when expanded state changes |
|
|
62
|
+
| `children` | `ReactNode` | - | The content to expand/collapse |
|
|
63
|
+
| `duration` | `number` | `200` | Duration of the animation in milliseconds |
|
|
64
|
+
| `animateOpacity` | `boolean` | `true` | Whether to animate opacity during expansion/collapse |
|
|
65
|
+
| `style` | `ViewStyle` | - | Additional style for the container |
|
|
66
|
+
| `accessibilityLabel` | `string` | - | Accessibility label for screen readers |
|
|
67
|
+
| `testID` | `string` | - | Test ID for testing purposes |
|
|
68
|
+
|
|
69
|
+
## Animation Duration
|
|
70
|
+
|
|
71
|
+
The `duration` prop controls how long the expand/collapse animation takes. The default is 200ms, which provides a smooth, natural feel. You can adjust this for faster or slower animations.
|
|
72
|
+
|
|
73
|
+
<Canvas of={Stories.FastAnimation} />
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
// Fast animation (150ms)
|
|
77
|
+
<Expandable expanded={expanded} duration={150}>
|
|
78
|
+
<Card>
|
|
79
|
+
<BodyText>Quick animation</BodyText>
|
|
80
|
+
</Card>
|
|
81
|
+
</Expandable>
|
|
82
|
+
|
|
83
|
+
// Slow animation (600ms)
|
|
84
|
+
<Expandable expanded={expanded} duration={600}>
|
|
85
|
+
<Card>
|
|
86
|
+
<BodyText>Slow animation</BodyText>
|
|
87
|
+
</Card>
|
|
88
|
+
</Expandable>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Examples
|
|
92
|
+
|
|
93
|
+
### Basic Example
|
|
94
|
+
|
|
95
|
+
<Canvas of={Stories.BasicExample} />
|
|
96
|
+
|
|
97
|
+
### With Different Animation Speeds
|
|
98
|
+
|
|
99
|
+
<Canvas of={Stories.FastAnimation} />
|
|
100
|
+
<Canvas of={Stories.SlowAnimation} />
|
|
101
|
+
|
|
102
|
+
### Multiple Expandables
|
|
103
|
+
|
|
104
|
+
<Canvas of={Stories.MultipleExpandables} />
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
import { useState } from 'react';
|
|
108
|
+
import { View } from 'react-native';
|
|
109
|
+
import { Expandable, Button, Card, BodyText } from '@utilitywarehouse/hearth-react-native';
|
|
110
|
+
|
|
111
|
+
const MyComponent = () => {
|
|
112
|
+
const [expanded1, setExpanded1] = useState(false);
|
|
113
|
+
const [expanded2, setExpanded2] = useState(false);
|
|
114
|
+
const [expanded3, setExpanded3] = useState(false);
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<View style={{ gap: 16 }}>
|
|
118
|
+
<View>
|
|
119
|
+
<Button onPress={() => setExpanded1(!expanded1)}>Section 1 {expanded1 ? '▲' : '▼'}</Button>
|
|
120
|
+
<Expandable expanded={expanded1}>
|
|
121
|
+
<Card>
|
|
122
|
+
<BodyText>Content for section 1</BodyText>
|
|
123
|
+
</Card>
|
|
124
|
+
</Expandable>
|
|
125
|
+
</View>
|
|
126
|
+
|
|
127
|
+
<View>
|
|
128
|
+
<Button onPress={() => setExpanded2(!expanded2)}>Section 2 {expanded2 ? '▲' : '▼'}</Button>
|
|
129
|
+
<Expandable expanded={expanded2}>
|
|
130
|
+
<Card>
|
|
131
|
+
<BodyText>Content for section 2</BodyText>
|
|
132
|
+
</Card>
|
|
133
|
+
</Expandable>
|
|
134
|
+
</View>
|
|
135
|
+
|
|
136
|
+
<View>
|
|
137
|
+
<Button onPress={() => setExpanded3(!expanded3)}>Section 3 {expanded3 ? '▲' : '▼'}</Button>
|
|
138
|
+
<Expandable expanded={expanded3}>
|
|
139
|
+
<Card>
|
|
140
|
+
<BodyText>Content for section 3</BodyText>
|
|
141
|
+
</Card>
|
|
142
|
+
</Expandable>
|
|
143
|
+
</View>
|
|
144
|
+
</View>
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Controlled Example
|
|
150
|
+
|
|
151
|
+
<Canvas of={Stories.ControlledExample} />
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
import { useState } from 'react';
|
|
155
|
+
import { View } from 'react-native';
|
|
156
|
+
import { Expandable, Button, Card, BodyText } from '@utilitywarehouse/hearth-react-native';
|
|
157
|
+
|
|
158
|
+
const MyComponent = () => {
|
|
159
|
+
const [expanded, setExpanded] = useState(false);
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<View>
|
|
163
|
+
<View style={{ flexDirection: 'row', gap: 8 }}>
|
|
164
|
+
<Button onPress={() => setExpanded(true)}>Expand</Button>
|
|
165
|
+
<Button onPress={() => setExpanded(false)}>Collapse</Button>
|
|
166
|
+
</View>
|
|
167
|
+
<Expandable
|
|
168
|
+
expanded={expanded}
|
|
169
|
+
onExpandedChange={setExpanded}
|
|
170
|
+
accessibilityLabel="Expandable content section"
|
|
171
|
+
>
|
|
172
|
+
<Card>
|
|
173
|
+
<BodyText>Controlled expandable content</BodyText>
|
|
174
|
+
</Card>
|
|
175
|
+
</Expandable>
|
|
176
|
+
</View>
|
|
177
|
+
);
|
|
178
|
+
};
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Accessibility
|
|
182
|
+
|
|
183
|
+
The Expandable component includes built-in accessibility support:
|
|
184
|
+
|
|
185
|
+
- Uses `accessibilityState` to communicate expanded/collapsed state to screen readers
|
|
186
|
+
- Set `accessibilityLabel` to provide context about the expandable content
|
|
187
|
+
- Automatically announces state changes to assistive technologies
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
<Expandable expanded={expanded} accessibilityLabel="Additional information section">
|
|
191
|
+
{/* content */}
|
|
192
|
+
</Expandable>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Screen Reader Announcements
|
|
196
|
+
|
|
197
|
+
When using the Expandable component:
|
|
198
|
+
|
|
199
|
+
- The `accessibilityState` prop automatically includes the `expanded` state
|
|
200
|
+
- Screen readers will announce "expanded" or "collapsed" based on the current state
|
|
201
|
+
- Provide descriptive `accessibilityLabel` values to give users context
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ViewProps, ViewStyle } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export interface ExpandableProps extends ViewProps {
|
|
5
|
+
/**
|
|
6
|
+
* Whether the content is expanded
|
|
7
|
+
*/
|
|
8
|
+
expanded?: boolean;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Callback when expanded state changes
|
|
12
|
+
*/
|
|
13
|
+
onExpandedChange?: (expanded: boolean) => void;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The content to expand/collapse
|
|
17
|
+
*/
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Duration of the animation in milliseconds
|
|
22
|
+
* @default 200
|
|
23
|
+
*/
|
|
24
|
+
duration?: number;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Additional style for the container
|
|
28
|
+
*/
|
|
29
|
+
style?: ViewStyle;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Accessibility label for screen readers
|
|
33
|
+
*/
|
|
34
|
+
accessibilityLabel?: string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Test ID for testing
|
|
38
|
+
*/
|
|
39
|
+
testID?: string;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Whether to animate opacity during expansion/collapse
|
|
43
|
+
* @default true
|
|
44
|
+
*/
|
|
45
|
+
animateOpacity?: boolean;
|
|
46
|
+
}
|