@utilitywarehouse/hearth-react-native 0.8.2 → 0.9.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.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +8 -0
- package/build/components/Banner/Banner.js +25 -6
- package/build/components/Banner/Banner.props.d.ts +2 -2
- package/build/components/BottomSheet/BottomSheetHandle.js +8 -0
- package/build/components/Menu/Menu.context.d.ts +5 -0
- package/build/components/Menu/Menu.context.js +9 -0
- package/build/components/Menu/Menu.d.ts +4 -0
- package/build/components/Menu/Menu.js +25 -0
- package/build/components/Menu/Menu.props.d.ts +21 -0
- package/build/components/Menu/Menu.props.js +1 -0
- package/build/components/Menu/MenuItem.d.ts +18 -0
- package/build/components/Menu/MenuItem.js +115 -0
- package/build/components/Menu/MenuItem.props.d.ts +27 -0
- package/build/components/Menu/MenuItem.props.js +1 -0
- package/build/components/Menu/MenuTrigger.d.ts +9 -0
- package/build/components/Menu/MenuTrigger.js +11 -0
- package/build/components/Menu/MenuTrigger.props.d.ts +12 -0
- package/build/components/Menu/MenuTrigger.props.js +1 -0
- package/build/components/Menu/index.d.ts +7 -0
- package/build/components/Menu/index.js +4 -0
- package/build/components/Modal/Modal.d.ts +1 -1
- package/build/components/Modal/Modal.js +32 -30
- package/build/components/Modal/Modal.props.d.ts +1 -0
- package/build/components/Modal/Modal.web.d.ts +1 -1
- package/build/components/Modal/Modal.web.js +25 -25
- package/build/components/index.d.ts +1 -0
- package/build/components/index.js +1 -0
- package/build/tokens/components/dark/index.d.ts +3 -1
- package/build/tokens/components/dark/index.js +3 -1
- package/build/tokens/components/dark/input.d.ts +3 -0
- package/build/tokens/components/dark/input.js +3 -0
- package/build/tokens/components/dark/modal.d.ts +7 -4
- package/build/tokens/components/dark/modal.js +7 -4
- package/build/tokens/components/dark/rating.d.ts +8 -0
- package/build/tokens/components/dark/rating.js +7 -0
- package/build/tokens/components/dark/table.d.ts +0 -3
- package/build/tokens/components/dark/table.js +0 -3
- package/build/tokens/components/dark/time-picker.d.ts +29 -0
- package/build/tokens/components/dark/time-picker.js +28 -0
- package/build/tokens/components/dark/timeline.d.ts +27 -0
- package/build/tokens/components/dark/timeline.js +26 -0
- package/build/tokens/components/light/index.d.ts +3 -1
- package/build/tokens/components/light/index.js +3 -1
- package/build/tokens/components/light/input.d.ts +3 -0
- package/build/tokens/components/light/input.js +3 -0
- package/build/tokens/components/light/modal.d.ts +7 -4
- package/build/tokens/components/light/modal.js +7 -4
- package/build/tokens/components/light/rating.d.ts +8 -0
- package/build/tokens/components/light/rating.js +7 -0
- package/build/tokens/components/light/table.d.ts +0 -3
- package/build/tokens/components/light/table.js +0 -3
- package/build/tokens/components/light/time-picker.d.ts +29 -0
- package/build/tokens/components/light/time-picker.js +28 -0
- package/build/tokens/components/light/timeline.d.ts +27 -0
- package/build/tokens/components/light/timeline.js +26 -0
- package/docs/components/AllComponents.web.tsx +33 -0
- package/docs/components/BackToTopButton.tsx +1 -1
- package/package.json +2 -2
- package/src/components/Banner/Banner.docs.mdx +19 -10
- package/src/components/Banner/Banner.props.ts +2 -2
- package/src/components/Banner/Banner.stories.tsx +1 -4
- package/src/components/Banner/Banner.tsx +47 -7
- package/src/components/BottomSheet/BottomSheetHandle.tsx +12 -0
- package/src/components/DatePickerInput/DatePickerInput.docs.mdx +1 -1
- package/src/components/Menu/Menu.context.ts +15 -0
- package/src/components/Menu/Menu.docs.mdx +158 -0
- package/src/components/Menu/Menu.props.ts +24 -0
- package/src/components/Menu/Menu.stories.tsx +292 -0
- package/src/components/Menu/Menu.tsx +54 -0
- package/src/components/Menu/MenuItem.props.ts +29 -0
- package/src/components/Menu/MenuItem.tsx +145 -0
- package/src/components/Menu/MenuTrigger.props.ts +14 -0
- package/src/components/Menu/MenuTrigger.tsx +20 -0
- package/src/components/Menu/index.ts +7 -0
- package/src/components/Modal/Modal.docs.mdx +34 -5
- package/src/components/Modal/Modal.props.ts +1 -0
- package/src/components/Modal/Modal.stories.tsx +46 -0
- package/src/components/Modal/Modal.tsx +37 -33
- package/src/components/Modal/Modal.web.tsx +27 -27
- package/src/components/index.ts +1 -0
- package/src/tokens/components/dark/index.ts +3 -1
- package/src/tokens/components/dark/input.ts +3 -0
- package/src/tokens/components/dark/modal.ts +7 -4
- package/src/tokens/components/dark/rating.ts +8 -0
- package/src/tokens/components/dark/table.ts +0 -3
- package/src/tokens/components/dark/time-picker.ts +29 -0
- package/src/tokens/components/dark/timeline.ts +27 -0
- package/src/tokens/components/light/index.ts +3 -1
- package/src/tokens/components/light/input.ts +3 -0
- package/src/tokens/components/light/modal.ts +7 -4
- package/src/tokens/components/light/rating.ts +8 -0
- package/src/tokens/components/light/table.ts +0 -3
- package/src/tokens/components/light/time-picker.ts +29 -0
- package/src/tokens/components/light/timeline.ts +27 -0
- package/build/tokens/components/dark/dialog.d.ts +0 -25
- package/build/tokens/components/dark/dialog.js +0 -24
- package/build/tokens/components/light/dialog.d.ts +0 -25
- package/build/tokens/components/light/dialog.js +0 -24
- package/src/tokens/components/dark/dialog.ts +0 -25
- package/src/tokens/components/light/dialog.ts +0 -25
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Do not edit directly, this file was auto-generated.
|
|
3
|
+
*/
|
|
4
|
+
declare const _default: {
|
|
5
|
+
readonly bar: {
|
|
6
|
+
readonly width: 2;
|
|
7
|
+
};
|
|
8
|
+
readonly content: {
|
|
9
|
+
readonly gap: 8;
|
|
10
|
+
readonly paddingBottom: 24;
|
|
11
|
+
readonly paddingTop: 2;
|
|
12
|
+
};
|
|
13
|
+
readonly gap: 12;
|
|
14
|
+
readonly progress: {
|
|
15
|
+
readonly circle: {
|
|
16
|
+
readonly height: 28;
|
|
17
|
+
readonly width: 28;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
readonly static: {
|
|
21
|
+
readonly circle: {
|
|
22
|
+
readonly height: 12;
|
|
23
|
+
readonly width: 12;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export default _default;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Do not edit directly, this file was auto-generated.
|
|
3
|
+
*/
|
|
4
|
+
export default {
|
|
5
|
+
bar: {
|
|
6
|
+
width: 2,
|
|
7
|
+
},
|
|
8
|
+
content: {
|
|
9
|
+
gap: 8,
|
|
10
|
+
paddingBottom: 24,
|
|
11
|
+
paddingTop: 2,
|
|
12
|
+
},
|
|
13
|
+
gap: 12,
|
|
14
|
+
progress: {
|
|
15
|
+
circle: {
|
|
16
|
+
height: 28,
|
|
17
|
+
width: 28,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
static: {
|
|
21
|
+
circle: {
|
|
22
|
+
height: 12,
|
|
23
|
+
width: 12,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -6,9 +6,12 @@ import {
|
|
|
6
6
|
BroadbandMediumIcon,
|
|
7
7
|
CashbackCardMediumIcon,
|
|
8
8
|
ChevronRightMediumIcon,
|
|
9
|
+
EditSmallIcon,
|
|
9
10
|
ElectricityMediumIcon,
|
|
10
11
|
InsuranceMediumIcon,
|
|
11
12
|
MobileMediumIcon,
|
|
13
|
+
ShareSmallIcon,
|
|
14
|
+
TrashSmallIcon,
|
|
12
15
|
} from '@utilitywarehouse/hearth-react-native-icons';
|
|
13
16
|
// @ts-ignore
|
|
14
17
|
import SpotBillingDark from '@utilitywarehouse/hearth-svg-assets/lib/spot-billing-dark.svg';
|
|
@@ -60,6 +63,9 @@ import {
|
|
|
60
63
|
Link,
|
|
61
64
|
List,
|
|
62
65
|
ListItem,
|
|
66
|
+
Menu,
|
|
67
|
+
MenuItem,
|
|
68
|
+
MenuTrigger,
|
|
63
69
|
Modal,
|
|
64
70
|
OL,
|
|
65
71
|
ProgressStep,
|
|
@@ -159,6 +165,10 @@ const AllComponents: React.FC = () => {
|
|
|
159
165
|
}, [datePickerRef]);
|
|
160
166
|
const [switchEnabled, setSwitchEnabled] = React.useState(false);
|
|
161
167
|
const toggleSwitch = () => setSwitchEnabled(!switchEnabled);
|
|
168
|
+
const menuRef = useRef<any>(null);
|
|
169
|
+
const handleMenuOpenPress = useCallback(() => {
|
|
170
|
+
menuRef.current?.present();
|
|
171
|
+
}, []);
|
|
162
172
|
|
|
163
173
|
const [colorMode] = useColorMode();
|
|
164
174
|
const isDark = colorMode === 'dark';
|
|
@@ -564,6 +574,29 @@ const AllComponents: React.FC = () => {
|
|
|
564
574
|
</List>
|
|
565
575
|
</Center>
|
|
566
576
|
</ComponentWrapper>
|
|
577
|
+
<ComponentWrapper name="Menu" link="/?path=/docs/components-menu--docs">
|
|
578
|
+
<Center flex={1}>
|
|
579
|
+
<BottomSheetModalProvider>
|
|
580
|
+
<MenuTrigger onPress={handleMenuOpenPress}>
|
|
581
|
+
<Button>Open Menu</Button>
|
|
582
|
+
</MenuTrigger>
|
|
583
|
+
<Menu ref={menuRef} heading="Actions">
|
|
584
|
+
<MenuItem icon={EditSmallIcon} text="Edit" onPress={() => console.log('Edit')} />
|
|
585
|
+
<MenuItem
|
|
586
|
+
icon={ShareSmallIcon}
|
|
587
|
+
text="Share"
|
|
588
|
+
onPress={() => console.log('Share')}
|
|
589
|
+
/>
|
|
590
|
+
<MenuItem
|
|
591
|
+
icon={TrashSmallIcon}
|
|
592
|
+
text="Delete"
|
|
593
|
+
colorScheme="destructive"
|
|
594
|
+
onPress={() => console.log('Delete')}
|
|
595
|
+
/>
|
|
596
|
+
</Menu>
|
|
597
|
+
</BottomSheetModalProvider>
|
|
598
|
+
</Center>
|
|
599
|
+
</ComponentWrapper>
|
|
567
600
|
<ComponentWrapper name="Modal" link="/?path=/docs/components-modal--docs">
|
|
568
601
|
<Center flex={1}>
|
|
569
602
|
<Button onPress={handleModalOpenPress}>Open Modal</Button>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utilitywarehouse/hearth-react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Utility Warehouse React Native UI library",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"@utilitywarehouse/hearth-react-icons": "^0.7.3",
|
|
61
61
|
"@utilitywarehouse/hearth-react-native-icons": "^0.7.3",
|
|
62
62
|
"@utilitywarehouse/hearth-svg-assets": "^0.2.0",
|
|
63
|
-
"@utilitywarehouse/hearth-tokens": "^0.
|
|
63
|
+
"@utilitywarehouse/hearth-tokens": "^0.2.0"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
66
|
"@gorhom/bottom-sheet": "^5.0.0",
|
|
@@ -69,8 +69,8 @@ const MyComponent = () => (
|
|
|
69
69
|
| iconContainerVariant | `'subtle' \| 'emphasis'` | Icon container visual style | `'subtle'` |
|
|
70
70
|
| iconContainerSize | `'sm' \| 'md' \| 'lg'` | Icon container size | `'md'` |
|
|
71
71
|
| iconContainerColor | `'pig' \| 'energy' \| 'broadband' \| 'mobile' \|` <br />`'insurance' \| 'cashback' \| 'highlight'` | Icon container color scheme | `'pig'` |
|
|
72
|
-
| illustration | `
|
|
73
|
-
| image | `
|
|
72
|
+
| illustration | `ThemedImageProps \| ImageProps` | Themed illustration object (mutually exclusive with icon/image) | `-` |
|
|
73
|
+
| image | `ThemedImageProps \| ImageProps` | Themed image object (mutually exclusive with icon/illustration) | `-` |
|
|
74
74
|
| heading | `string` | Heading text | `-` (required) |
|
|
75
75
|
| description | `string` | Description text | `-` (required) |
|
|
76
76
|
| direction | `'horizontal' \| 'vertical'` | Layout direction for icon/image and content | `'horizontal'` |
|
|
@@ -156,14 +156,23 @@ Display a themed image that automatically switches between light and dark modes:
|
|
|
156
156
|
import { Banner } from '@utilitywarehouse/hearth-react-native';
|
|
157
157
|
|
|
158
158
|
const MyComponent = () => (
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
159
|
+
<>
|
|
160
|
+
<Banner
|
|
161
|
+
image={{
|
|
162
|
+
source: { uri: 'https://example.com/image.jpg' },
|
|
163
|
+
}}
|
|
164
|
+
heading="Featured Content"
|
|
165
|
+
description="Discover amazing content curated just for you."
|
|
166
|
+
/>
|
|
167
|
+
<Banner
|
|
168
|
+
image={{
|
|
169
|
+
light: { uri: 'https://example.com/light-image.jpg' },
|
|
170
|
+
dark: { uri: 'https://example.com/dark-image.jpg' },
|
|
171
|
+
}}
|
|
172
|
+
heading="Featured Content"
|
|
173
|
+
description="Discover amazing content curated just for you."
|
|
174
|
+
/>
|
|
175
|
+
</>
|
|
167
176
|
);
|
|
168
177
|
```
|
|
169
178
|
|
|
@@ -51,12 +51,12 @@ export interface BannerProps
|
|
|
51
51
|
* Illustration to display in the banner
|
|
52
52
|
* Mutually exclusive with icon and image
|
|
53
53
|
*/
|
|
54
|
-
illustration?: ThemedImageProps
|
|
54
|
+
illustration?: ThemedImageProps | ImageProps;
|
|
55
55
|
/**
|
|
56
56
|
* Image to display in the banner
|
|
57
57
|
* Mutually exclusive with icon and illustration
|
|
58
58
|
*/
|
|
59
|
-
image?: ThemedImageProps
|
|
59
|
+
image?: ThemedImageProps | ImageProps;
|
|
60
60
|
/**
|
|
61
61
|
* Heading text
|
|
62
62
|
*/
|
|
@@ -326,10 +326,7 @@ export const VerticalLayout: Story = {
|
|
|
326
326
|
<Banner
|
|
327
327
|
variant="emphasis"
|
|
328
328
|
image={{
|
|
329
|
-
|
|
330
|
-
uri: 'https://images.unsplash.com/photo-1506126613408-eca07ce68773?w=200&q=80',
|
|
331
|
-
},
|
|
332
|
-
dark: {
|
|
329
|
+
source: {
|
|
333
330
|
uri: 'https://images.unsplash.com/photo-1506126613408-eca07ce68773?w=200&q=80',
|
|
334
331
|
},
|
|
335
332
|
}}
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { ChevronRightSmallIcon, CloseSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
2
|
-
import { Pressable, View } from 'react-native';
|
|
2
|
+
import { Image, ImageProps, Pressable, View } from 'react-native';
|
|
3
3
|
import { StyleSheet } from 'react-native-unistyles';
|
|
4
4
|
import { BodyText } from '../BodyText';
|
|
5
5
|
import { Card } from '../Card';
|
|
6
6
|
import { Heading } from '../Heading';
|
|
7
7
|
import { IconContainer } from '../IconContainer';
|
|
8
|
-
import { ThemedImage } from '../ThemedImage';
|
|
8
|
+
import { ThemedImage, ThemedImageProps } from '../ThemedImage';
|
|
9
9
|
import { UnstyledIconButton } from '../UnstyledIconButton';
|
|
10
10
|
import type BannerProps from './Banner.props';
|
|
11
11
|
|
|
12
|
+
const isThemedImageProps = (props: ThemedImageProps | ImageProps): props is ThemedImageProps => {
|
|
13
|
+
return 'light' in props && 'dark' in props;
|
|
14
|
+
};
|
|
15
|
+
|
|
12
16
|
const Banner = ({
|
|
13
17
|
icon,
|
|
14
18
|
iconContainerVariant = 'subtle',
|
|
@@ -44,8 +48,17 @@ const Banner = ({
|
|
|
44
48
|
);
|
|
45
49
|
}
|
|
46
50
|
if (illustration) {
|
|
51
|
+
if (isThemedImageProps(illustration)) {
|
|
52
|
+
return (
|
|
53
|
+
<ThemedImage
|
|
54
|
+
{...illustration}
|
|
55
|
+
resizeMode="cover"
|
|
56
|
+
style={[styles.media, styles.imageWrapper, illustration.style]}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
47
60
|
return (
|
|
48
|
-
<
|
|
61
|
+
<Image
|
|
49
62
|
{...illustration}
|
|
50
63
|
resizeMode="cover"
|
|
51
64
|
style={[styles.media, styles.imageWrapper, illustration.style]}
|
|
@@ -53,9 +66,16 @@ const Banner = ({
|
|
|
53
66
|
);
|
|
54
67
|
}
|
|
55
68
|
if (image) {
|
|
69
|
+
if (isThemedImageProps(image)) {
|
|
70
|
+
return (
|
|
71
|
+
<View style={[styles.media, styles.imageWrapper]}>
|
|
72
|
+
<ThemedImage {...image} style={[styles.image, image.style]} />
|
|
73
|
+
</View>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
56
76
|
return (
|
|
57
77
|
<View style={[styles.media, styles.imageWrapper]}>
|
|
58
|
-
<
|
|
78
|
+
<Image {...image} style={[styles.image, image.style]} />
|
|
59
79
|
</View>
|
|
60
80
|
);
|
|
61
81
|
}
|
|
@@ -74,7 +94,17 @@ const Banner = ({
|
|
|
74
94
|
|
|
75
95
|
const content = (
|
|
76
96
|
<View style={styles.container}>
|
|
97
|
+
{onClose && direction === 'vertical' && (
|
|
98
|
+
<UnstyledIconButton
|
|
99
|
+
icon={CloseSmallIcon}
|
|
100
|
+
size="sm"
|
|
101
|
+
onPress={onClose}
|
|
102
|
+
style={styles.closeButton}
|
|
103
|
+
accessibilityLabel="Close banner"
|
|
104
|
+
/>
|
|
105
|
+
)}
|
|
77
106
|
{renderIconOrImage()}
|
|
107
|
+
|
|
78
108
|
<View style={styles.contentContainer}>
|
|
79
109
|
<View style={styles.contentTextContainer}>
|
|
80
110
|
<View style={styles.textContainer}>
|
|
@@ -104,7 +134,7 @@ const Banner = ({
|
|
|
104
134
|
style={styles.chevron}
|
|
105
135
|
/>
|
|
106
136
|
)}
|
|
107
|
-
{onClose && (
|
|
137
|
+
{onClose && direction === 'horizontal' && (
|
|
108
138
|
<UnstyledIconButton
|
|
109
139
|
icon={CloseSmallIcon}
|
|
110
140
|
size="sm"
|
|
@@ -137,9 +167,9 @@ const Banner = ({
|
|
|
137
167
|
Banner.displayName = 'Banner';
|
|
138
168
|
|
|
139
169
|
const styles = StyleSheet.create(theme => ({
|
|
140
|
-
card: {},
|
|
170
|
+
card: { flexDirection: 'row', _web: { flexDirection: 'column' } },
|
|
141
171
|
pressable: {
|
|
142
|
-
|
|
172
|
+
flex: 1,
|
|
143
173
|
},
|
|
144
174
|
container: {
|
|
145
175
|
flexDirection: 'row',
|
|
@@ -273,6 +303,16 @@ const styles = StyleSheet.create(theme => ({
|
|
|
273
303
|
},
|
|
274
304
|
closeButton: {
|
|
275
305
|
alignSelf: 'flex-start',
|
|
306
|
+
variants: {
|
|
307
|
+
direction: {
|
|
308
|
+
vertical: {
|
|
309
|
+
position: 'absolute',
|
|
310
|
+
top: 0,
|
|
311
|
+
right: 0,
|
|
312
|
+
},
|
|
313
|
+
horizontal: {},
|
|
314
|
+
},
|
|
315
|
+
},
|
|
276
316
|
},
|
|
277
317
|
}));
|
|
278
318
|
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { BottomSheetHandle as Handle } from '@gorhom/bottom-sheet';
|
|
2
2
|
import { BottomSheetDefaultHandleProps } from '@gorhom/bottom-sheet/lib/typescript/components/bottomSheetHandle/types';
|
|
3
|
+
import { Platform, View } from 'react-native';
|
|
3
4
|
import { StyleSheet, withUnistyles } from 'react-native-unistyles';
|
|
4
5
|
|
|
5
6
|
const StyledBottomSheetHandle = withUnistyles(Handle);
|
|
6
7
|
|
|
7
8
|
const BottomSheetHandle = ({ style, indicatorStyle, ...props }: BottomSheetDefaultHandleProps) => {
|
|
9
|
+
if (Platform.OS === 'web') {
|
|
10
|
+
return (
|
|
11
|
+
<View style={[styles.handle, style]}>
|
|
12
|
+
<View style={[styles.indicator, indicatorStyle]} />
|
|
13
|
+
</View>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
8
16
|
return (
|
|
9
17
|
<StyledBottomSheetHandle
|
|
10
18
|
style={[styles.handle, style]}
|
|
@@ -22,6 +30,10 @@ const styles = StyleSheet.create(theme => ({
|
|
|
22
30
|
paddingTop: theme.components.bottomSheet.padding,
|
|
23
31
|
paddingHorizontal: theme.components.bottomSheet.padding,
|
|
24
32
|
paddingBottom: theme.components.bottomSheet.gap,
|
|
33
|
+
_web: {
|
|
34
|
+
alignItems: 'center',
|
|
35
|
+
cursor: 'grab',
|
|
36
|
+
},
|
|
25
37
|
},
|
|
26
38
|
indicator: {
|
|
27
39
|
width: theme.components.bottomSheet.handle.width,
|
|
@@ -232,6 +232,6 @@ Selection through the calendar always returns a JavaScript `Date` that is reform
|
|
|
232
232
|
### Best practices
|
|
233
233
|
|
|
234
234
|
- Always pair with `FormField` for proper label, helper text, and error message announcements.
|
|
235
|
-
- Use `openButtonLabel` to
|
|
235
|
+
- Use `openButtonLabel` to customise the calendar button announcement if the default doesn't fit your use case.
|
|
236
236
|
- Provide clear validation feedback through `FormField` when manual date entry doesn't match the expected format.
|
|
237
237
|
- Test with VoiceOver and TalkBack to ensure the date entry flow works smoothly in your specific context.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface IMenuContext {
|
|
4
|
+
close: () => void;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const MenuContext = createContext<IMenuContext | undefined>(undefined);
|
|
8
|
+
|
|
9
|
+
export const useMenuContext = () => {
|
|
10
|
+
const context = useContext(MenuContext);
|
|
11
|
+
if (!context) {
|
|
12
|
+
throw new Error('useMenuContext must be used within a Menu component');
|
|
13
|
+
}
|
|
14
|
+
return context;
|
|
15
|
+
};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { Canvas, Controls, Meta, Story } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import { BackToTopButton, ViewFigmaButton } from '../../../docs/components';
|
|
3
|
+
import * as Stories from './Menu.stories';
|
|
4
|
+
|
|
5
|
+
<Meta title="Components / Menu" />
|
|
6
|
+
|
|
7
|
+
<ViewFigmaButton url="https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR/Hearth-Components---Tokens" />
|
|
8
|
+
|
|
9
|
+
<BackToTopButton />
|
|
10
|
+
|
|
11
|
+
# Menu
|
|
12
|
+
|
|
13
|
+
The `Menu` component provides a bottom sheet modal with a list of actions. It's perfect for contextual menus, action sheets, and option lists. The component uses `BottomSheetModal` under the hood and includes `MenuItem` components that can display icons, text, and support different color schemes.
|
|
14
|
+
|
|
15
|
+
- [Playground](#playground)
|
|
16
|
+
- [Usage](#usage)
|
|
17
|
+
- [Props](#props)
|
|
18
|
+
- [Features](#features)
|
|
19
|
+
- [Examples](#examples)
|
|
20
|
+
- [Basic Menu](#basic-menu)
|
|
21
|
+
- [With Destructive Action](#with-destructive-action)
|
|
22
|
+
- [Icon on Right](#icon-on-right)
|
|
23
|
+
- [Without Icons](#without-icons)
|
|
24
|
+
- [With Disabled Items](#with-disabled-items)
|
|
25
|
+
- [Without Heading](#without-heading)
|
|
26
|
+
- [Accessibility](#accessibility)
|
|
27
|
+
|
|
28
|
+
## Playground
|
|
29
|
+
|
|
30
|
+
<Canvas of={Stories.Playground} />
|
|
31
|
+
|
|
32
|
+
<Controls of={Stories.Playground} />
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### Basic Usage
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { useRef } from 'react';
|
|
40
|
+
import { Menu, MenuItem, MenuTrigger } from '@utilitywarehouse/hearth-react-native';
|
|
41
|
+
import { EditSmallIcon, TrashSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
42
|
+
|
|
43
|
+
const MyComponent = () => {
|
|
44
|
+
const menuRef = useRef(null);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<>
|
|
48
|
+
<MenuTrigger onPress={() => menuRef.current?.present()}>
|
|
49
|
+
<Button>Open Menu</Button>
|
|
50
|
+
</MenuTrigger>
|
|
51
|
+
|
|
52
|
+
<Menu ref={menuRef} heading="Actions">
|
|
53
|
+
<MenuItem icon={EditSmallIcon} text="Edit" onPress={() => console.log('Edit')} />
|
|
54
|
+
<MenuItem
|
|
55
|
+
icon={TrashSmallIcon}
|
|
56
|
+
text="Delete"
|
|
57
|
+
colorScheme="destructive"
|
|
58
|
+
onPress={() => console.log('Delete')}
|
|
59
|
+
/>
|
|
60
|
+
</Menu>
|
|
61
|
+
</>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Props
|
|
67
|
+
|
|
68
|
+
### Menu
|
|
69
|
+
|
|
70
|
+
| Prop | Type | Default | Description |
|
|
71
|
+
| ------------------ | -------------------------------- | ------- | --------------------------------------------------- |
|
|
72
|
+
| `heading` | `string` | - | Heading text displayed at the top of the menu |
|
|
73
|
+
| `children` | `ReactNode` | - | Menu items to display |
|
|
74
|
+
| `bottomSheetProps` | `Partial<BottomSheetModalProps>` | - | Optional bottom sheet modal props for customization |
|
|
75
|
+
|
|
76
|
+
### MenuItem
|
|
77
|
+
|
|
78
|
+
| Prop | Type | Default | Description |
|
|
79
|
+
| -------------- | ---------------------------------------- | -------------- | ---------------------------------- |
|
|
80
|
+
| `icon` | `ComponentType` | - | Icon component to display |
|
|
81
|
+
| `iconPosition` | `'left' \| 'right'` | `'left'` | Position of the icon |
|
|
82
|
+
| `text` | `string` | - | Text to display in the menu item |
|
|
83
|
+
| `colorScheme` | `'functional' \| 'destructive'` | `'functional'` | Color scheme for the menu item |
|
|
84
|
+
| `disabled` | `boolean` | `false` | Whether the menu item is disabled |
|
|
85
|
+
| `onPress` | `(event: GestureResponderEvent) => void` | - | Callback when menu item is pressed |
|
|
86
|
+
|
|
87
|
+
### MenuTrigger
|
|
88
|
+
|
|
89
|
+
| Prop | Type | Description |
|
|
90
|
+
| ---------- | -------------- | ------------------------------------------------------------------- |
|
|
91
|
+
| `children` | `ReactElement` | The child element that triggers the menu (must be a single element) |
|
|
92
|
+
| `onPress` | `() => void` | Function that opens the menu |
|
|
93
|
+
|
|
94
|
+
## Features
|
|
95
|
+
|
|
96
|
+
### Icon Positioning
|
|
97
|
+
|
|
98
|
+
Icons can be positioned on either the left or right side of the menu item text using the `iconPosition` prop.
|
|
99
|
+
|
|
100
|
+
### Color Schemes
|
|
101
|
+
|
|
102
|
+
Menu items support two color schemes:
|
|
103
|
+
|
|
104
|
+
- `functional` (default): Uses primary text and icon colors
|
|
105
|
+
- `destructive`: Uses danger/destructive colors for actions like delete
|
|
106
|
+
|
|
107
|
+
### Disabled State
|
|
108
|
+
|
|
109
|
+
Menu items can be disabled using the `disabled` prop, which applies reduced opacity and prevents interaction.
|
|
110
|
+
|
|
111
|
+
### Auto-closing
|
|
112
|
+
|
|
113
|
+
Menu items automatically close the menu when pressed, making it easy to handle actions without manual menu dismissal.
|
|
114
|
+
|
|
115
|
+
## Examples
|
|
116
|
+
|
|
117
|
+
### Basic Menu
|
|
118
|
+
|
|
119
|
+
A simple menu with icon-based actions:
|
|
120
|
+
|
|
121
|
+
<Canvas of={Stories.BasicMenu} />
|
|
122
|
+
|
|
123
|
+
### With Destructive Action
|
|
124
|
+
|
|
125
|
+
Menu with a destructive action styled differently:
|
|
126
|
+
|
|
127
|
+
<Canvas of={Stories.WithDestructiveAction} />
|
|
128
|
+
|
|
129
|
+
### Icon on Right
|
|
130
|
+
|
|
131
|
+
Menu items with icons positioned on the right side:
|
|
132
|
+
|
|
133
|
+
<Canvas of={Stories.IconOnRight} />
|
|
134
|
+
|
|
135
|
+
### Without Icons
|
|
136
|
+
|
|
137
|
+
Simple text-only menu items:
|
|
138
|
+
|
|
139
|
+
<Canvas of={Stories.WithoutIcons} />
|
|
140
|
+
|
|
141
|
+
### With Disabled Items
|
|
142
|
+
|
|
143
|
+
Menu with some items disabled:
|
|
144
|
+
|
|
145
|
+
<Canvas of={Stories.WithDisabledItems} />
|
|
146
|
+
|
|
147
|
+
### Without Heading
|
|
148
|
+
|
|
149
|
+
Menu without a heading section:
|
|
150
|
+
|
|
151
|
+
<Canvas of={Stories.WithoutHeading} />
|
|
152
|
+
|
|
153
|
+
## Accessibility
|
|
154
|
+
|
|
155
|
+
- Menu items have `accessibilityRole="button"` for proper screen reader support
|
|
156
|
+
- Disabled state is communicated via `accessibilityState`
|
|
157
|
+
- Keyboard navigation is supported on web platforms
|
|
158
|
+
- All interactive elements are properly focusable
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BottomSheetModalProps } from '@gorhom/bottom-sheet';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
export interface MenuMethods {
|
|
5
|
+
present: () => void;
|
|
6
|
+
dismiss: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface MenuProps {
|
|
10
|
+
/**
|
|
11
|
+
* Heading text displayed at the top of the menu
|
|
12
|
+
*/
|
|
13
|
+
heading?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Menu items to display
|
|
16
|
+
*/
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
/**
|
|
19
|
+
* Optional bottom sheet modal props to customise the menu behavior
|
|
20
|
+
*/
|
|
21
|
+
bottomSheetProps?: Partial<BottomSheetModalProps>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default MenuProps;
|