@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.
Files changed (102) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/CHANGELOG.md +8 -0
  4. package/build/components/Banner/Banner.js +25 -6
  5. package/build/components/Banner/Banner.props.d.ts +2 -2
  6. package/build/components/BottomSheet/BottomSheetHandle.js +8 -0
  7. package/build/components/Menu/Menu.context.d.ts +5 -0
  8. package/build/components/Menu/Menu.context.js +9 -0
  9. package/build/components/Menu/Menu.d.ts +4 -0
  10. package/build/components/Menu/Menu.js +25 -0
  11. package/build/components/Menu/Menu.props.d.ts +21 -0
  12. package/build/components/Menu/Menu.props.js +1 -0
  13. package/build/components/Menu/MenuItem.d.ts +18 -0
  14. package/build/components/Menu/MenuItem.js +115 -0
  15. package/build/components/Menu/MenuItem.props.d.ts +27 -0
  16. package/build/components/Menu/MenuItem.props.js +1 -0
  17. package/build/components/Menu/MenuTrigger.d.ts +9 -0
  18. package/build/components/Menu/MenuTrigger.js +11 -0
  19. package/build/components/Menu/MenuTrigger.props.d.ts +12 -0
  20. package/build/components/Menu/MenuTrigger.props.js +1 -0
  21. package/build/components/Menu/index.d.ts +7 -0
  22. package/build/components/Menu/index.js +4 -0
  23. package/build/components/Modal/Modal.d.ts +1 -1
  24. package/build/components/Modal/Modal.js +32 -30
  25. package/build/components/Modal/Modal.props.d.ts +1 -0
  26. package/build/components/Modal/Modal.web.d.ts +1 -1
  27. package/build/components/Modal/Modal.web.js +25 -25
  28. package/build/components/index.d.ts +1 -0
  29. package/build/components/index.js +1 -0
  30. package/build/tokens/components/dark/index.d.ts +3 -1
  31. package/build/tokens/components/dark/index.js +3 -1
  32. package/build/tokens/components/dark/input.d.ts +3 -0
  33. package/build/tokens/components/dark/input.js +3 -0
  34. package/build/tokens/components/dark/modal.d.ts +7 -4
  35. package/build/tokens/components/dark/modal.js +7 -4
  36. package/build/tokens/components/dark/rating.d.ts +8 -0
  37. package/build/tokens/components/dark/rating.js +7 -0
  38. package/build/tokens/components/dark/table.d.ts +0 -3
  39. package/build/tokens/components/dark/table.js +0 -3
  40. package/build/tokens/components/dark/time-picker.d.ts +29 -0
  41. package/build/tokens/components/dark/time-picker.js +28 -0
  42. package/build/tokens/components/dark/timeline.d.ts +27 -0
  43. package/build/tokens/components/dark/timeline.js +26 -0
  44. package/build/tokens/components/light/index.d.ts +3 -1
  45. package/build/tokens/components/light/index.js +3 -1
  46. package/build/tokens/components/light/input.d.ts +3 -0
  47. package/build/tokens/components/light/input.js +3 -0
  48. package/build/tokens/components/light/modal.d.ts +7 -4
  49. package/build/tokens/components/light/modal.js +7 -4
  50. package/build/tokens/components/light/rating.d.ts +8 -0
  51. package/build/tokens/components/light/rating.js +7 -0
  52. package/build/tokens/components/light/table.d.ts +0 -3
  53. package/build/tokens/components/light/table.js +0 -3
  54. package/build/tokens/components/light/time-picker.d.ts +29 -0
  55. package/build/tokens/components/light/time-picker.js +28 -0
  56. package/build/tokens/components/light/timeline.d.ts +27 -0
  57. package/build/tokens/components/light/timeline.js +26 -0
  58. package/docs/components/AllComponents.web.tsx +33 -0
  59. package/docs/components/BackToTopButton.tsx +1 -1
  60. package/package.json +2 -2
  61. package/src/components/Banner/Banner.docs.mdx +19 -10
  62. package/src/components/Banner/Banner.props.ts +2 -2
  63. package/src/components/Banner/Banner.stories.tsx +1 -4
  64. package/src/components/Banner/Banner.tsx +47 -7
  65. package/src/components/BottomSheet/BottomSheetHandle.tsx +12 -0
  66. package/src/components/DatePickerInput/DatePickerInput.docs.mdx +1 -1
  67. package/src/components/Menu/Menu.context.ts +15 -0
  68. package/src/components/Menu/Menu.docs.mdx +158 -0
  69. package/src/components/Menu/Menu.props.ts +24 -0
  70. package/src/components/Menu/Menu.stories.tsx +292 -0
  71. package/src/components/Menu/Menu.tsx +54 -0
  72. package/src/components/Menu/MenuItem.props.ts +29 -0
  73. package/src/components/Menu/MenuItem.tsx +145 -0
  74. package/src/components/Menu/MenuTrigger.props.ts +14 -0
  75. package/src/components/Menu/MenuTrigger.tsx +20 -0
  76. package/src/components/Menu/index.ts +7 -0
  77. package/src/components/Modal/Modal.docs.mdx +34 -5
  78. package/src/components/Modal/Modal.props.ts +1 -0
  79. package/src/components/Modal/Modal.stories.tsx +46 -0
  80. package/src/components/Modal/Modal.tsx +37 -33
  81. package/src/components/Modal/Modal.web.tsx +27 -27
  82. package/src/components/index.ts +1 -0
  83. package/src/tokens/components/dark/index.ts +3 -1
  84. package/src/tokens/components/dark/input.ts +3 -0
  85. package/src/tokens/components/dark/modal.ts +7 -4
  86. package/src/tokens/components/dark/rating.ts +8 -0
  87. package/src/tokens/components/dark/table.ts +0 -3
  88. package/src/tokens/components/dark/time-picker.ts +29 -0
  89. package/src/tokens/components/dark/timeline.ts +27 -0
  90. package/src/tokens/components/light/index.ts +3 -1
  91. package/src/tokens/components/light/input.ts +3 -0
  92. package/src/tokens/components/light/modal.ts +7 -4
  93. package/src/tokens/components/light/rating.ts +8 -0
  94. package/src/tokens/components/light/table.ts +0 -3
  95. package/src/tokens/components/light/time-picker.ts +29 -0
  96. package/src/tokens/components/light/timeline.ts +27 -0
  97. package/build/tokens/components/dark/dialog.d.ts +0 -25
  98. package/build/tokens/components/dark/dialog.js +0 -24
  99. package/build/tokens/components/light/dialog.d.ts +0 -25
  100. package/build/tokens/components/light/dialog.js +0 -24
  101. package/src/tokens/components/dark/dialog.ts +0 -25
  102. package/src/tokens/components/light/dialog.ts +0 -25
@@ -0,0 +1,292 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-native';
2
+ import {
3
+ DownloadSmallIcon,
4
+ EditSmallIcon,
5
+ SettingsMediumIcon,
6
+ ShareSmallIcon,
7
+ TickSmallIcon,
8
+ TrashSmallIcon,
9
+ } from '@utilitywarehouse/hearth-react-native-icons';
10
+ import { useRef } from 'react';
11
+ import { Platform, View } from 'react-native';
12
+ import { Button } from '../Button';
13
+ import { Menu, MenuItem, MenuTrigger } from './';
14
+ import type { MenuMethods } from './Menu.props';
15
+
16
+ import { ViewWrap } from '../../../docs/components';
17
+
18
+ const meta: Meta<typeof Menu> = {
19
+ title: 'Stories / Menu',
20
+ component: Menu,
21
+ parameters: {
22
+ layout: 'centered',
23
+ noScroll: true,
24
+ },
25
+ argTypes: {},
26
+ args: {
27
+ heading: 'Menu Options',
28
+ },
29
+ };
30
+
31
+ export default meta;
32
+ type Story = StoryObj<typeof meta>;
33
+
34
+ export const Playground: Story = {
35
+ render: ({ ...args }) => {
36
+ const menuRef = useRef<MenuMethods>(null);
37
+
38
+ const openMenu = () => {
39
+ menuRef.current?.present();
40
+ };
41
+
42
+ return (
43
+ <View style={Platform.OS === 'web' ? { width: 400, height: 400 } : { flex: 1 }}>
44
+ <ViewWrap>
45
+ <MenuTrigger onPress={openMenu}>
46
+ <Button>Open Menu</Button>
47
+ </MenuTrigger>
48
+
49
+ <Menu ref={menuRef} {...args}>
50
+ <MenuItem
51
+ icon={EditSmallIcon}
52
+ text="Edit"
53
+ onPress={() => console.log('Edit pressed')}
54
+ />
55
+ <MenuItem
56
+ icon={ShareSmallIcon}
57
+ text="Share"
58
+ onPress={() => console.log('Share pressed')}
59
+ />
60
+ <MenuItem
61
+ icon={DownloadSmallIcon}
62
+ text="Download"
63
+ onPress={() => console.log('Download pressed')}
64
+ />
65
+ <MenuItem
66
+ icon={TrashSmallIcon}
67
+ text="Delete"
68
+ colorScheme="destructive"
69
+ onPress={() => console.log('Delete pressed')}
70
+ />
71
+ </Menu>
72
+ </ViewWrap>
73
+ </View>
74
+ );
75
+ },
76
+ };
77
+
78
+ export const BasicMenu = () => {
79
+ const menuRef = useRef<MenuMethods>(null);
80
+
81
+ const openMenu = () => {
82
+ menuRef.current?.present();
83
+ };
84
+
85
+ return (
86
+ <View style={Platform.OS === 'web' ? { width: 400, height: 400 } : { flex: 1 }}>
87
+ <ViewWrap>
88
+ <MenuTrigger onPress={openMenu}>
89
+ <Button>Actions</Button>
90
+ </MenuTrigger>
91
+
92
+ <Menu ref={menuRef} heading="Actions">
93
+ <MenuItem icon={EditSmallIcon} text="Edit" onPress={() => console.log('Edit pressed')} />
94
+ <MenuItem
95
+ icon={ShareSmallIcon}
96
+ text="Share"
97
+ onPress={() => console.log('Share pressed')}
98
+ />
99
+ <MenuItem
100
+ icon={DownloadSmallIcon}
101
+ text="Download"
102
+ onPress={() => console.log('Download pressed')}
103
+ />
104
+ </Menu>
105
+ </ViewWrap>
106
+ </View>
107
+ );
108
+ };
109
+
110
+ export const WithDestructiveAction = () => {
111
+ const menuRef = useRef<MenuMethods>(null);
112
+
113
+ const openMenu = () => {
114
+ menuRef.current?.present();
115
+ };
116
+
117
+ return (
118
+ <View style={Platform.OS === 'web' ? { width: 400, height: 400 } : {}}>
119
+ <ViewWrap>
120
+ <MenuTrigger onPress={openMenu}>
121
+ <Button>File Options</Button>
122
+ </MenuTrigger>
123
+
124
+ <Menu ref={menuRef} heading="File Options">
125
+ <MenuItem
126
+ icon={EditSmallIcon}
127
+ text="Rename"
128
+ onPress={() => console.log('Rename pressed')}
129
+ />
130
+ <MenuItem
131
+ icon={ShareSmallIcon}
132
+ text="Share"
133
+ onPress={() => console.log('Share pressed')}
134
+ />
135
+ <MenuItem
136
+ icon={DownloadSmallIcon}
137
+ text="Download"
138
+ onPress={() => console.log('Download pressed')}
139
+ />
140
+ <MenuItem
141
+ icon={TrashSmallIcon}
142
+ text="Delete"
143
+ colorScheme="destructive"
144
+ onPress={() => console.log('Delete pressed')}
145
+ />
146
+ </Menu>
147
+ </ViewWrap>
148
+ </View>
149
+ );
150
+ };
151
+
152
+ export const IconOnRight = () => {
153
+ const menuRef = useRef<MenuMethods>(null);
154
+
155
+ const openMenu = () => {
156
+ menuRef.current?.present();
157
+ };
158
+
159
+ return (
160
+ <View style={Platform.OS === 'web' ? { width: 400, height: 400 } : {}}>
161
+ <ViewWrap>
162
+ <MenuTrigger onPress={openMenu}>
163
+ <Button>Settings</Button>
164
+ </MenuTrigger>
165
+
166
+ <Menu ref={menuRef} heading="Settings">
167
+ <MenuItem
168
+ icon={TickSmallIcon}
169
+ iconPosition="right"
170
+ text="Enable Notifications"
171
+ onPress={() => console.log('Toggle notifications')}
172
+ />
173
+ <MenuItem
174
+ icon={TickSmallIcon}
175
+ iconPosition="right"
176
+ text="Dark Mode"
177
+ onPress={() => console.log('Toggle dark mode')}
178
+ />
179
+ <MenuItem
180
+ icon={SettingsMediumIcon}
181
+ iconPosition="right"
182
+ text="Advanced Settings"
183
+ onPress={() => console.log('Open advanced settings')}
184
+ />
185
+ </Menu>
186
+ </ViewWrap>
187
+ </View>
188
+ );
189
+ };
190
+
191
+ export const WithoutIcons = () => {
192
+ const menuRef = useRef<MenuMethods>(null);
193
+
194
+ const openMenu = () => {
195
+ menuRef.current?.present();
196
+ };
197
+
198
+ return (
199
+ <View style={Platform.OS === 'web' ? { width: 400, height: 400 } : {}}>
200
+ <ViewWrap>
201
+ <MenuTrigger onPress={openMenu}>
202
+ <Button>Sort By</Button>
203
+ </MenuTrigger>
204
+
205
+ <Menu ref={menuRef} heading="Sort By">
206
+ <MenuItem text="Name" onPress={() => console.log('Sort by name')} />
207
+ <MenuItem text="Date" onPress={() => console.log('Sort by date')} />
208
+ <MenuItem text="Size" onPress={() => console.log('Sort by size')} />
209
+ <MenuItem text="Type" onPress={() => console.log('Sort by type')} />
210
+ </Menu>
211
+ </ViewWrap>
212
+ </View>
213
+ );
214
+ };
215
+
216
+ export const WithDisabledItems = () => {
217
+ const menuRef = useRef<MenuMethods>(null);
218
+
219
+ const openMenu = () => {
220
+ menuRef.current?.present();
221
+ };
222
+
223
+ return (
224
+ <View style={Platform.OS === 'web' ? { width: 400, height: 400 } : {}}>
225
+ <ViewWrap>
226
+ <MenuTrigger onPress={openMenu}>
227
+ <Button>Document Actions</Button>
228
+ </MenuTrigger>
229
+
230
+ <Menu ref={menuRef} heading="Document Actions">
231
+ <MenuItem icon={EditSmallIcon} text="Edit" onPress={() => console.log('Edit pressed')} />
232
+ <MenuItem
233
+ icon={ShareSmallIcon}
234
+ text="Share"
235
+ disabled
236
+ onPress={() => console.log('Share pressed')}
237
+ />
238
+ <MenuItem
239
+ icon={DownloadSmallIcon}
240
+ text="Download"
241
+ disabled
242
+ onPress={() => console.log('Download pressed')}
243
+ />
244
+ <MenuItem
245
+ icon={TrashSmallIcon}
246
+ text="Delete"
247
+ colorScheme="destructive"
248
+ onPress={() => console.log('Delete pressed')}
249
+ />
250
+ </Menu>
251
+ </ViewWrap>
252
+ </View>
253
+ );
254
+ };
255
+
256
+ export const WithoutHeading = () => {
257
+ const menuRef = useRef<MenuMethods>(null);
258
+
259
+ const openMenu = () => {
260
+ menuRef.current?.present();
261
+ };
262
+
263
+ return (
264
+ <View style={Platform.OS === 'web' ? { width: 400, height: 400 } : {}}>
265
+ <ViewWrap>
266
+ <MenuTrigger onPress={openMenu}>
267
+ <Button>Quick Actions</Button>
268
+ </MenuTrigger>
269
+
270
+ <Menu ref={menuRef}>
271
+ <MenuItem icon={EditSmallIcon} text="Edit" onPress={() => console.log('Edit pressed')} />
272
+ <MenuItem
273
+ icon={ShareSmallIcon}
274
+ text="Share"
275
+ onPress={() => console.log('Share pressed')}
276
+ />
277
+ <MenuItem
278
+ icon={DownloadSmallIcon}
279
+ text="Download"
280
+ onPress={() => console.log('Download pressed')}
281
+ />
282
+ <MenuItem
283
+ icon={TrashSmallIcon}
284
+ text="Delete"
285
+ colorScheme="destructive"
286
+ onPress={() => console.log('Delete pressed')}
287
+ />
288
+ </Menu>
289
+ </ViewWrap>
290
+ </View>
291
+ );
292
+ };
@@ -0,0 +1,54 @@
1
+ import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef } from 'react';
2
+ import { StyleSheet } from 'react-native-unistyles';
3
+ import { BodyText } from '../BodyText';
4
+ import { BottomSheetModal, BottomSheetProps, BottomSheetScrollView } from '../BottomSheet';
5
+ import { MenuContext } from './Menu.context';
6
+ import type MenuProps from './Menu.props';
7
+ import type { MenuMethods } from './Menu.props';
8
+
9
+ const Menu = forwardRef<MenuMethods, MenuProps>(({ heading, children, bottomSheetProps }, ref) => {
10
+ const bottomSheetModalRef = useRef<BottomSheetModal>(null);
11
+
12
+ useImperativeHandle(
13
+ ref,
14
+ () => ({
15
+ present: () => bottomSheetModalRef.current?.present(),
16
+ dismiss: () => bottomSheetModalRef.current?.dismiss(),
17
+ }),
18
+ []
19
+ );
20
+
21
+ const handleClose = useCallback(() => {
22
+ bottomSheetModalRef.current?.dismiss();
23
+ }, []);
24
+
25
+ const contextValue = useMemo(() => ({ close: handleClose }), [handleClose]);
26
+
27
+ return (
28
+ <BottomSheetModal
29
+ ref={bottomSheetModalRef}
30
+ {...(bottomSheetProps as Partial<BottomSheetProps>)}
31
+ >
32
+ <BottomSheetScrollView contentContainerStyle={styles.container}>
33
+ <MenuContext.Provider value={contextValue}>
34
+ {heading && (
35
+ <BodyText size="md" weight="semibold">
36
+ {heading}
37
+ </BodyText>
38
+ )}
39
+ {children}
40
+ </MenuContext.Provider>
41
+ </BottomSheetScrollView>
42
+ </BottomSheetModal>
43
+ );
44
+ });
45
+
46
+ Menu.displayName = 'Menu';
47
+
48
+ const styles = StyleSheet.create(theme => ({
49
+ container: {
50
+ gap: theme.components.bottomSheet.gap,
51
+ },
52
+ }));
53
+
54
+ export default Menu;
@@ -0,0 +1,29 @@
1
+ import type { ComponentType } from 'react';
2
+ import type { PressableProps } from 'react-native';
3
+
4
+ export interface MenuItemProps extends Omit<PressableProps, 'children'> {
5
+ /**
6
+ * Icon component to display
7
+ */
8
+ icon?: ComponentType;
9
+ /**
10
+ * Position of the icon
11
+ * @default 'left'
12
+ */
13
+ iconPosition?: 'left' | 'right';
14
+ /**
15
+ * Text to display in the menu item
16
+ */
17
+ text: string;
18
+ /**
19
+ * Color scheme for the menu item
20
+ * @default 'functional'
21
+ */
22
+ colorScheme?: 'functional' | 'destructive';
23
+ /**
24
+ * Whether the menu item is disabled
25
+ */
26
+ disabled?: boolean;
27
+ }
28
+
29
+ export default MenuItemProps;
@@ -0,0 +1,145 @@
1
+ import { createPressable } from '@gluestack-ui/pressable';
2
+ import { Pressable } from 'react-native';
3
+ import { StyleSheet } from 'react-native-unistyles';
4
+ import { BodyText } from '../BodyText';
5
+ import { Icon } from '../Icon';
6
+ import { useMenuContext } from './Menu.context';
7
+ import type MenuItemProps from './MenuItem.props';
8
+
9
+ const MenuItemRoot = ({
10
+ icon,
11
+ iconPosition = 'left',
12
+ text,
13
+ colorScheme = 'functional',
14
+ disabled = false,
15
+ onPress,
16
+ states = {},
17
+ ...props
18
+ }: MenuItemProps & { states?: { active?: boolean; disabled?: boolean } }) => {
19
+ const { active } = states;
20
+ const { close } = useMenuContext();
21
+
22
+ styles.useVariants({ colorScheme, disabled, iconPosition, active });
23
+
24
+ const handlePress = (event: any) => {
25
+ if (disabled) return;
26
+ onPress?.(event);
27
+ close();
28
+ };
29
+
30
+ return (
31
+ <Pressable
32
+ {...props}
33
+ onPress={handlePress}
34
+ disabled={disabled}
35
+ style={styles.container}
36
+ accessibilityRole="button"
37
+ accessibilityState={{ disabled }}
38
+ >
39
+ {!!icon && <Icon as={icon} style={styles.icon} />}
40
+ <BodyText size="lg" style={styles.text}>
41
+ {text}
42
+ </BodyText>
43
+ </Pressable>
44
+ );
45
+ };
46
+
47
+ const MenuItem = createPressable({ Root: MenuItemRoot });
48
+
49
+ MenuItem.displayName = 'MenuItem';
50
+
51
+ const styles = StyleSheet.create(theme => ({
52
+ container: {
53
+ flexDirection: 'row',
54
+ alignItems: 'center',
55
+ paddingVertical: theme.components.menu.item.padding,
56
+ paddingHorizontal: theme.components.menu.mobile.item.padding,
57
+ gap: theme.components.menu.item.gap,
58
+ borderRadius: theme.components.menu.item.borderRadius,
59
+ variants: {
60
+ active: {
61
+ true: {
62
+ backgroundColor: theme.color.interactive.functional.surface.subtle.active,
63
+ },
64
+ },
65
+ disabled: {
66
+ true: {
67
+ opacity: theme.opacity.disabled,
68
+ cursor: 'auto',
69
+ },
70
+ false: {
71
+ _web: {
72
+ _hover: {
73
+ backgroundColor: theme.color.interactive.functional.surface.subtle.hover,
74
+ },
75
+ _active: {
76
+ backgroundColor: theme.color.interactive.functional.surface.subtle.active,
77
+ },
78
+ },
79
+ },
80
+ },
81
+ iconPosition: {
82
+ left: {
83
+ flexDirection: 'row',
84
+ },
85
+ right: {
86
+ flexDirection: 'row-reverse',
87
+ },
88
+ },
89
+ colorScheme: {
90
+ functional: {},
91
+ destructive: {},
92
+ },
93
+ },
94
+ compoundVariants: [
95
+ {
96
+ colorScheme: 'destructive',
97
+ active: true,
98
+ styles: {
99
+ backgroundColor: theme.color.interactive.destructive.surface.subtle.active,
100
+ },
101
+ },
102
+ {
103
+ colorScheme: 'destructive',
104
+ disabled: false,
105
+ styles: {
106
+ _web: {
107
+ _hover: {
108
+ backgroundColor: theme.color.interactive.destructive.surface.subtle.hover,
109
+ },
110
+ _active: {
111
+ backgroundColor: theme.color.interactive.destructive.surface.subtle.active,
112
+ },
113
+ },
114
+ },
115
+ },
116
+ ],
117
+ },
118
+ text: {
119
+ flex: 1,
120
+ variants: {
121
+ colorScheme: {
122
+ functional: {
123
+ color: theme.color.text.primary,
124
+ },
125
+ destructive: {
126
+ color: theme.color.interactive.destructive.foreground.subtle,
127
+ },
128
+ },
129
+ },
130
+ },
131
+ icon: {
132
+ variants: {
133
+ colorScheme: {
134
+ functional: {
135
+ color: theme.color.icon.primary,
136
+ },
137
+ destructive: {
138
+ color: theme.color.interactive.destructive.foreground.subtle,
139
+ },
140
+ },
141
+ },
142
+ },
143
+ }));
144
+
145
+ export default MenuItem;
@@ -0,0 +1,14 @@
1
+ import type { ReactElement } from 'react';
2
+
3
+ export interface MenuTriggerProps {
4
+ /**
5
+ * The child element that triggers the menu (should be a single pressable element like Button)
6
+ */
7
+ children: ReactElement;
8
+ /**
9
+ * Called when the trigger is pressed.
10
+ */
11
+ onPress?: () => void;
12
+ }
13
+
14
+ export default MenuTriggerProps;
@@ -0,0 +1,20 @@
1
+ import { cloneElement, isValidElement } from 'react';
2
+ import type MenuTriggerProps from './MenuTrigger.props';
3
+
4
+ interface MenuTriggerInternalProps extends MenuTriggerProps {
5
+ onPress: () => void;
6
+ }
7
+
8
+ const MenuTrigger = ({ children, onPress }: MenuTriggerInternalProps) => {
9
+ if (!isValidElement(children)) {
10
+ throw new Error('MenuTrigger: children must be a valid React element');
11
+ }
12
+
13
+ return cloneElement(children, {
14
+ onPress,
15
+ } as { onPress?: () => void });
16
+ };
17
+
18
+ MenuTrigger.displayName = 'MenuTrigger';
19
+
20
+ export default MenuTrigger;
@@ -0,0 +1,7 @@
1
+ export { default as Menu } from './Menu';
2
+ export { useMenuContext } from './Menu.context';
3
+ export type { MenuMethods, default as MenuProps } from './Menu.props';
4
+ export { default as MenuItem } from './MenuItem';
5
+ export type { default as MenuItemProps } from './MenuItem.props';
6
+ export { default as MenuTrigger } from './MenuTrigger';
7
+ export type { default as MenuTriggerProps } from './MenuTrigger.props';
@@ -25,11 +25,12 @@ The Modal component is ideal for displaying important information, collecting us
25
25
  - [Examples](#examples)
26
26
  - [Basic Modal](#basic-modal)
27
27
  - [Modal with Image](#modal-with-image)
28
+ - [Fullscreen Modal](#fullscreen-modal)
28
29
  - [Modal with Custom Content](#modal-with-custom-content)
29
30
  - [Loading State](#loading-state)
30
31
  - [Without Close Button](#without-close-button)
31
32
  - [Single Action Modal](#single-action-modal)
32
- - [Fullscreen Modal (Usage with navigation modal)](#fullscreen-modal-usage-with-navigation-modal)
33
+ - [Modal in Navigation Modal](#modal-in-navigation-modal)
33
34
  - [Integration Notes](#integration-notes)
34
35
  - [External Resources](#external-resources)
35
36
 
@@ -104,7 +105,8 @@ The Modal component extends the `BottomSheetModal` component and accepts all of
104
105
  | `primaryButtonProps` | `Omit<ButtonWithoutChildrenProps, 'children'>` | Additional props to pass to the primary button (colorScheme defaults to 'highlight', variant to 'solid') | - |
105
106
  | `secondaryButtonProps` | `Omit<ButtonWithoutChildrenProps, 'children'>` | Additional props to pass to the secondary button (colorScheme defaults to 'functional', variant to 'outline') | - |
106
107
  | `closeButtonProps` | `Omit<UnstyledIconButtonProps, 'children'>` | Additional props to pass to the close button | - |
107
- | `fullscreen` | `boolean` | Whether the modal should take up the full screen height (useful for navigation modals) | `false` |
108
+ | `fullscreen` | `boolean` | Whether the modal should take up the full screen height | `false` |
109
+ | `inNavModal` | `boolean` | Renders the modal correctly when used inside a navigation modal | `false` |
108
110
 
109
111
  ## Features
110
112
 
@@ -292,6 +294,33 @@ const ImageModal = () => {
292
294
  };
293
295
  ```
294
296
 
297
+ ### Fullscreen Modal
298
+
299
+ Create a modal that takes up the full screen height:
300
+
301
+ <Canvas of={Stories.FullscreenModal} />
302
+
303
+ ```tsx
304
+ const FullscreenModal = () => {
305
+ const modalRef = useRef<BottomSheetModal>(null);
306
+
307
+ return (
308
+ <>
309
+ <Button onPress={() => modalRef.current?.present()}>Show Fullscreen Modal</Button>
310
+
311
+ <Modal
312
+ ref={modalRef}
313
+ heading="Fullscreen Modal"
314
+ description="This modal takes up the full screen height"
315
+ primaryButtonText="Close"
316
+ onPressPrimaryButton={() => modalRef.current?.dismiss()}
317
+ fullscreen
318
+ />
319
+ </>
320
+ );
321
+ };
322
+ ```
323
+
295
324
  ### Modal with Custom Content
296
325
 
297
326
  Add custom content between the header and footer sections:
@@ -414,9 +443,9 @@ const AlertModal = () => {
414
443
  };
415
444
  ```
416
445
 
417
- ### Fullscreen Modal (Usage with navigation modal)
446
+ ### Modal In Navigation Modal
418
447
 
419
- When using the Modal component in a navigation context, you can set it to fullscreen mode, this will make it behave like a standard modal screen.
448
+ When using the Modal component in a navigation context, you can set it to `inNavModal` mode, this will make it behave like a standard modal screen.
420
449
  Here's an example of how to implement this with custom close animations for Android:
421
450
 
422
451
  ```tsx
@@ -465,7 +494,7 @@ export default function ModalScreen() {
465
494
  return (
466
495
  <Modal
467
496
  ref={modalRef}
468
- fullscreen
497
+ inNavModal
469
498
  onPressCloseButton={handleClose}
470
499
  primaryButtonText="Action"
471
500
  onPressPrimaryButton={handleClose}
@@ -9,6 +9,7 @@ interface ModalProps extends Omit<BottomSheetProps, 'children'> {
9
9
  showCloseButton?: boolean;
10
10
  heading?: string;
11
11
  description?: string;
12
+ inNavModal?: boolean;
12
13
  fullscreen?: boolean;
13
14
  children?: ViewProps['children'];
14
15
  onPressPrimaryButton?: () => void;