@storybook/react-native-ui 8.5.2-alpha.1 → 8.5.2-alpha.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storybook/react-native-ui",
3
- "version": "8.5.2-alpha.1",
3
+ "version": "8.5.2-alpha.2",
4
4
  "description": "ui components for react native storybook",
5
5
  "keywords": [
6
6
  "react",
@@ -60,7 +60,7 @@
60
60
  "dependencies": {
61
61
  "@storybook/core": "^8.4.2",
62
62
  "@storybook/react": "^8.4.2",
63
- "@storybook/react-native-theming": "^8.5.2-alpha.1",
63
+ "@storybook/react-native-theming": "^8.5.2-alpha.2",
64
64
  "fuse.js": "^7.0.0",
65
65
  "memoizerific": "^1.11.3",
66
66
  "polished": "^4.3.1",
@@ -81,5 +81,5 @@
81
81
  "publishConfig": {
82
82
  "access": "public"
83
83
  },
84
- "gitHead": "6829dc629b4ca18e2869267a9c9e0afed094f2f3"
84
+ "gitHead": "051e4d60e1274baab7bd81afd8414d55a5c41f4b"
85
85
  }
package/src/Layout.tsx CHANGED
@@ -3,8 +3,8 @@ import { addons } from '@storybook/core/manager-api';
3
3
  import { type API_IndexHash, type Args, type StoryContext } from '@storybook/core/types';
4
4
  import type { ReactRenderer } from '@storybook/react';
5
5
  import { styled, useTheme } from '@storybook/react-native-theming';
6
- import { ReactNode, useRef, useState } from 'react';
7
- import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
6
+ import { ReactNode, useRef, useState, useCallback } from 'react';
7
+ import { ScrollView, Text, TouchableOpacity, View, ViewStyle } from 'react-native';
8
8
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
9
9
  import { IconButton } from './IconButton';
10
10
  import { useLayout } from './LayoutProvider';
@@ -18,6 +18,39 @@ import { BottomBarToggleIcon } from './icon/BottomBarToggleIcon';
18
18
  import { CloseFullscreenIcon } from './icon/CloseFullscreenIcon';
19
19
  import { FullscreenIcon } from './icon/FullscreenIcon';
20
20
  import { MenuIcon } from './icon/MenuIcon';
21
+ import { useStyle } from './util/useStyle';
22
+
23
+ const desktopLogoContainer = {
24
+ flexDirection: 'row',
25
+ alignItems: 'center',
26
+ paddingTop: 10,
27
+ paddingLeft: 16,
28
+ paddingBottom: 4,
29
+ paddingRight: 10,
30
+ justifyContent: 'space-between',
31
+ } satisfies ViewStyle;
32
+
33
+ const desktopContentContainerStyle = { flex: 1 } satisfies ViewStyle;
34
+
35
+ const desktopContentStyle = { flex: 1, overflow: 'hidden' } satisfies ViewStyle;
36
+
37
+ const mobileContentStyle = { flex: 1, overflow: 'hidden' } satisfies ViewStyle;
38
+
39
+ const placeholderObject = {};
40
+
41
+ const placeholderArray = [];
42
+
43
+ const iconFloatRightStyle = { marginLeft: 'auto' } satisfies ViewStyle;
44
+
45
+ const navButtonStyle = { flexShrink: 1 } satisfies ViewStyle;
46
+
47
+ const navButtonHitSlop = { bottom: 10, left: 10, right: 10, top: 10 };
48
+
49
+ const mobileMenuDrawerContentStyle = {
50
+ paddingLeft: 16,
51
+ paddingTop: 4,
52
+ paddingBottom: 4,
53
+ } satisfies ViewStyle;
21
54
 
22
55
  export const Layout = ({
23
56
  storyHash,
@@ -46,58 +79,114 @@ export const Layout = ({
46
79
 
47
80
  const [uiHidden, setUiHidden] = useState(false);
48
81
 
82
+ const desktopContainerStyle = useStyle(
83
+ () => ({
84
+ flex: 1,
85
+ paddingTop: insets.top,
86
+ backgroundColor: theme.background.content,
87
+ flexDirection: 'row',
88
+ }),
89
+ [theme.background.content, insets.top]
90
+ );
91
+
92
+ const desktopSidebarStyle = useStyle(
93
+ () => ({
94
+ width: desktopSidebarOpen ? 240 : undefined,
95
+ padding: desktopSidebarOpen ? 0 : 10,
96
+ borderColor: theme.appBorderColor,
97
+ borderRightWidth: 1,
98
+ }),
99
+ [desktopSidebarOpen, theme.appBorderColor]
100
+ );
101
+
102
+ const desktopScrollViewContentContainerStyle = useStyle(
103
+ () => ({
104
+ paddingBottom: insets.bottom,
105
+ }),
106
+ [insets.bottom]
107
+ );
108
+
109
+ const desktopAddonsPanelStyle = useStyle(
110
+ () => ({
111
+ height: desktopAddonsPanelOpen ? 300 : undefined,
112
+ borderTopWidth: 1,
113
+ borderColor: theme.appBorderColor,
114
+ paddingTop: desktopAddonsPanelOpen ? 4 : 0,
115
+ padding: desktopAddonsPanelOpen ? 0 : 10,
116
+ }),
117
+ [desktopAddonsPanelOpen, theme.appBorderColor]
118
+ );
119
+
120
+ const mobileContainerStyle = useStyle(
121
+ () => ({
122
+ flex: 1,
123
+ paddingTop: story?.parameters?.noSafeArea ? 0 : insets.top,
124
+ backgroundColor: theme.background.content,
125
+ }),
126
+ [theme.background.content, insets.top, story?.parameters?.noSafeArea]
127
+ );
128
+
129
+ const fullScreenButtonStyle = useStyle(
130
+ () => ({
131
+ position: 'absolute',
132
+ bottom: uiHidden ? 56 + insets.bottom : 16,
133
+ right: 16,
134
+ backgroundColor: theme.background.content,
135
+ padding: 4,
136
+ borderRadius: 4,
137
+ borderWidth: 1,
138
+ borderColor: theme.appBorderColor,
139
+ }),
140
+ [uiHidden, insets.bottom, theme.background.content, theme.appBorderColor]
141
+ );
142
+
143
+ const containerStyle = useStyle(
144
+ () => ({
145
+ marginBottom: insets.bottom,
146
+ }),
147
+ [insets.bottom]
148
+ );
149
+
150
+ const navButtonTextStyle = useStyle(
151
+ () => ({
152
+ flexShrink: 1,
153
+ color: theme.color.defaultText,
154
+ }),
155
+ [theme.color.defaultText]
156
+ );
157
+
158
+ const openMobileMenu = useCallback(() => {
159
+ mobileMenuDrawerRef.current.setMobileMenuOpen(true);
160
+ }, [mobileMenuDrawerRef]);
161
+
162
+ const setSelection = useCallback(({ storyId: newStoryId }: { storyId: string }) => {
163
+ const channel = addons.getChannel();
164
+
165
+ channel.emit(SET_CURRENT_STORY, { storyId: newStoryId });
166
+ }, []);
167
+
49
168
  if (isDesktop) {
50
169
  return (
51
- <View
52
- style={{
53
- flex: 1,
54
- paddingTop: insets.top,
55
- backgroundColor: theme.background.content,
56
- flexDirection: 'row',
57
- }}
58
- >
59
- <View
60
- style={{
61
- width: desktopSidebarOpen ? 240 : undefined,
62
- padding: desktopSidebarOpen ? 0 : 10,
63
- borderColor: theme.appBorderColor,
64
- borderRightWidth: 1,
65
- }}
66
- >
170
+ <View style={desktopContainerStyle}>
171
+ <View style={desktopSidebarStyle}>
67
172
  {desktopSidebarOpen ? (
68
173
  <ScrollView
69
174
  keyboardShouldPersistTaps="handled"
70
- contentContainerStyle={{
71
- paddingBottom: insets.bottom,
72
- }}
175
+ contentContainerStyle={desktopScrollViewContentContainerStyle}
73
176
  >
74
- <View
75
- style={{
76
- flexDirection: 'row',
77
- alignItems: 'center',
78
- paddingTop: 10,
79
- paddingLeft: 16,
80
- paddingBottom: 4,
81
- paddingRight: 10,
82
- justifyContent: 'space-between',
83
- }}
84
- >
177
+ <View style={desktopLogoContainer}>
85
178
  <StorybookLogo theme={theme} />
86
179
 
87
180
  <IconButton onPress={() => setDesktopSidebarOpen(false)} Icon={MenuIcon} />
88
181
  </View>
89
182
 
90
183
  <Sidebar
91
- extra={[]}
184
+ extra={placeholderArray}
92
185
  previewInitialized
93
186
  indexError={undefined}
94
- refs={{}}
95
- setSelection={({ storyId: newStoryId }) => {
96
- const channel = addons.getChannel();
97
-
98
- channel.emit(SET_CURRENT_STORY, { storyId: newStoryId });
99
- }}
100
- status={{}}
187
+ refs={placeholderObject}
188
+ setSelection={setSelection}
189
+ status={placeholderObject}
101
190
  index={storyHash}
102
191
  storyId={story?.id}
103
192
  refId={DEFAULT_REF_ID}
@@ -108,23 +197,15 @@ export const Layout = ({
108
197
  )}
109
198
  </View>
110
199
 
111
- <View style={{ flex: 1 }}>
112
- <View style={{ flex: 1, overflow: 'hidden' }}>{children}</View>
113
-
114
- <View
115
- style={{
116
- height: desktopAddonsPanelOpen ? 300 : undefined,
117
- borderTopWidth: 1,
118
- borderColor: theme.appBorderColor,
119
- paddingTop: desktopAddonsPanelOpen ? 4 : 0,
120
- padding: desktopAddonsPanelOpen ? 0 : 10,
121
- }}
122
- >
200
+ <View style={desktopContentContainerStyle}>
201
+ <View style={desktopContentStyle}>{children}</View>
202
+
203
+ <View style={desktopAddonsPanelStyle}>
123
204
  {desktopAddonsPanelOpen ? (
124
205
  <AddonsTabs storyId={story?.id} onClose={() => setDesktopAddonsPanelOpen(false)} />
125
206
  ) : (
126
207
  <IconButton
127
- style={{ marginLeft: 'auto' }}
208
+ style={iconFloatRightStyle}
128
209
  onPress={() => setDesktopAddonsPanelOpen(true)}
129
210
  Icon={BottomBarToggleIcon}
130
211
  />
@@ -136,28 +217,13 @@ export const Layout = ({
136
217
  }
137
218
 
138
219
  return (
139
- <View
140
- style={{
141
- flex: 1,
142
- paddingTop: story?.parameters?.noSafeArea ? 0 : insets.top,
143
- backgroundColor: theme.background.content,
144
- }}
145
- >
146
- <View style={{ flex: 1, overflow: 'hidden' }}>
220
+ <View style={mobileContainerStyle}>
221
+ <View style={mobileContentStyle}>
147
222
  {children}
148
223
 
149
224
  {story?.parameters?.hideFullScreenButton ? null : (
150
225
  <TouchableOpacity
151
- style={{
152
- position: 'absolute',
153
- bottom: uiHidden ? 56 + insets.bottom : 16,
154
- right: 16,
155
- backgroundColor: theme.background.content,
156
- padding: 4,
157
- borderRadius: 4,
158
- borderWidth: 1,
159
- borderColor: theme.appBorderColor,
160
- }}
226
+ style={fullScreenButtonStyle}
161
227
  onPress={() => setUiHidden((prev) => !prev)}
162
228
  >
163
229
  {uiHidden ? (
@@ -170,18 +236,16 @@ export const Layout = ({
170
236
  </View>
171
237
 
172
238
  {!uiHidden ? (
173
- <Container style={{ marginBottom: insets.bottom }}>
239
+ <Container style={containerStyle}>
174
240
  <Nav>
175
241
  <Button
176
242
  testID="mobile-menu-button"
177
- style={{ flexShrink: 1 }}
178
- hitSlop={{ bottom: 10, left: 10, right: 10, top: 10 }}
179
- onPress={() => {
180
- mobileMenuDrawerRef.current.setMobileMenuOpen(true);
181
- }}
243
+ style={navButtonStyle}
244
+ hitSlop={navButtonHitSlop}
245
+ onPress={openMobileMenu}
182
246
  >
183
247
  <MenuIcon color={theme.color.mediumdark} />
184
- <Text style={{ flexShrink: 1, color: theme.color.defaultText }} numberOfLines={1}>
248
+ <Text style={navButtonTextStyle} numberOfLines={1}>
185
249
  {story?.title}/{story?.name}
186
250
  </Text>
187
251
  </Button>
@@ -196,21 +260,17 @@ export const Layout = ({
196
260
  ) : null}
197
261
 
198
262
  <MobileMenuDrawer ref={mobileMenuDrawerRef}>
199
- <View style={{ paddingLeft: 16, paddingTop: 4, paddingBottom: 4 }}>
263
+ <View style={mobileMenuDrawerContentStyle}>
200
264
  <StorybookLogo theme={theme} />
201
265
  </View>
202
266
 
203
267
  <Sidebar
204
- extra={[]}
268
+ extra={placeholderArray}
205
269
  previewInitialized
206
270
  indexError={undefined}
207
- refs={{}}
208
- setSelection={({ storyId: newStoryId }) => {
209
- const channel = addons.getChannel();
210
-
211
- channel.emit(SET_CURRENT_STORY, { storyId: newStoryId });
212
- }}
213
- status={{}}
271
+ refs={placeholderObject}
272
+ setSelection={setSelection}
273
+ status={placeholderObject}
214
274
  index={storyHash}
215
275
  storyId={story?.id}
216
276
  refId={DEFAULT_REF_ID}
@@ -2,8 +2,8 @@ import { BottomSheetModal } from '@gorhom/bottom-sheet';
2
2
  import { addons } from '@storybook/core/manager-api';
3
3
  import { styled } from '@storybook/react-native-theming';
4
4
  import { Addon_TypesEnum } from '@storybook/core/types';
5
- import { forwardRef, useImperativeHandle, useRef, useState } from 'react';
6
- import { Platform, Text, View, useWindowDimensions } from 'react-native';
5
+ import { forwardRef, useImperativeHandle, useMemo, useRef, useState } from 'react';
6
+ import { Platform, StyleProp, Text, View, ViewStyle, useWindowDimensions } from 'react-native';
7
7
  import { ScrollView } from 'react-native-gesture-handler';
8
8
  import Animated, {
9
9
  useAnimatedKeyboard,
@@ -15,11 +15,20 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
15
15
  import { useTheme } from '@storybook/react-native-theming';
16
16
  import { IconButton } from './IconButton';
17
17
  import { CloseIcon } from './icon/CloseIcon';
18
+ import { useStyle } from './util/useStyle';
18
19
 
19
20
  export interface MobileAddonsPanelRef {
20
21
  setAddonsPanelOpen: (isOpen: boolean) => void;
21
22
  }
22
23
 
24
+ const bottomSheetStyle = {
25
+ paddingTop: 8,
26
+ } satisfies StyleProp<ViewStyle>;
27
+
28
+ const contentStyle = {
29
+ flex: 1,
30
+ } satisfies StyleProp<ViewStyle>;
31
+
23
32
  export const MobileAddonsPanel = forwardRef<MobileAddonsPanelRef, { storyId?: string }>(
24
33
  ({ storyId }, ref) => {
25
34
  const theme = useTheme();
@@ -53,24 +62,31 @@ export const MobileAddonsPanel = forwardRef<MobileAddonsPanelRef, { storyId?: st
53
62
  };
54
63
  }, [animatedPosition, height, insets.bottom]);
55
64
 
65
+ const backgroundStyle = useStyle(() => {
66
+ return {
67
+ borderRadius: 0,
68
+ borderTopColor: theme.appBorderColor,
69
+ borderTopWidth: 1,
70
+ backgroundColor: theme.background.content,
71
+ };
72
+ });
73
+
74
+ const handleIndicatorStyle = useStyle(() => {
75
+ return {
76
+ backgroundColor: theme.textMutedColor,
77
+ };
78
+ });
79
+
56
80
  return (
57
81
  <BottomSheetModal
58
82
  ref={addonsPanelBottomSheetRef}
59
83
  index={1}
60
84
  animateOnMount={!reducedMotion}
61
85
  snapPoints={['25%', '50%', '75%']}
62
- style={{
63
- paddingTop: 8,
64
- }}
86
+ style={bottomSheetStyle}
65
87
  animatedPosition={animatedPosition}
66
- containerStyle={{}}
67
- backgroundStyle={{
68
- borderRadius: 0,
69
- borderTopColor: theme.appBorderColor,
70
- borderTopWidth: 1,
71
- backgroundColor: theme.background.content,
72
- }}
73
- handleIndicatorStyle={{ backgroundColor: theme.textMutedColor }}
88
+ backgroundStyle={backgroundStyle}
89
+ handleIndicatorStyle={handleIndicatorStyle}
74
90
  keyboardBehavior="extend"
75
91
  // keyboardBlurBehavior="restore"
76
92
  enableDismissOnClose
@@ -79,7 +95,7 @@ export const MobileAddonsPanel = forwardRef<MobileAddonsPanelRef, { storyId?: st
79
95
  stackBehavior="replace"
80
96
  enableDynamicSizing={false}
81
97
  >
82
- <Animated.View style={[{ flex: 1 }, adjustedBottomSheetSize]}>
98
+ <Animated.View style={[contentStyle, adjustedBottomSheetSize]}>
83
99
  <AddonsTabs
84
100
  onClose={() => {
85
101
  addonsPanelBottomSheetRef.current?.dismiss();
@@ -92,6 +108,38 @@ export const MobileAddonsPanel = forwardRef<MobileAddonsPanelRef, { storyId?: st
92
108
  }
93
109
  );
94
110
 
111
+ const addonsTabsContainerStyle = {
112
+ flex: 1,
113
+ } satisfies StyleProp<ViewStyle>;
114
+
115
+ const addonsTabsStyle = {
116
+ flexDirection: 'row',
117
+ borderBottomWidth: 1,
118
+ borderBottomColor: 'lightgrey',
119
+ } satisfies StyleProp<ViewStyle>;
120
+
121
+ const addonsTabsContentContainerStyle = {
122
+ justifyContent: 'center',
123
+ } satisfies StyleProp<ViewStyle>;
124
+
125
+ const closeIconStyle = {
126
+ marginRight: 4,
127
+ marginBottom: 4,
128
+ alignItems: 'center',
129
+ justifyContent: 'center',
130
+ } satisfies StyleProp<ViewStyle>;
131
+
132
+ const addonsScrollStyle = {
133
+ flex: 1,
134
+ } satisfies StyleProp<ViewStyle>;
135
+
136
+ const centeredStyle = {
137
+ alignItems: 'center',
138
+ justifyContent: 'center',
139
+ } satisfies StyleProp<ViewStyle>;
140
+
141
+ const hitSlop = { top: 10, right: 10, bottom: 10, left: 10 };
142
+
95
143
  export const AddonsTabs = ({ onClose, storyId }: { onClose?: () => void; storyId?: string }) => {
96
144
  const panels = addons.getElements(Addon_TypesEnum.PANEL);
97
145
 
@@ -99,15 +147,39 @@ export const AddonsTabs = ({ onClose, storyId }: { onClose?: () => void; storyId
99
147
 
100
148
  const insets = useSafeAreaInsets();
101
149
 
150
+ const scrollContentContainerStyle = useStyle(() => {
151
+ return {
152
+ paddingBottom: insets.bottom + 16,
153
+ };
154
+ });
155
+
156
+ const panel = useMemo(() => {
157
+ if (!storyId) {
158
+ return (
159
+ <View style={centeredStyle}>
160
+ <Text>No Story Selected</Text>
161
+ </View>
162
+ );
163
+ }
164
+
165
+ if (Object.keys(panels).length === 0) {
166
+ return (
167
+ <View style={centeredStyle}>
168
+ <Text>No addons loaded.</Text>
169
+ </View>
170
+ );
171
+ }
172
+
173
+ return panels[addonSelected].render({ active: true });
174
+ }, [addonSelected, panels, storyId]);
175
+
102
176
  return (
103
- <View style={{ flex: 1 }}>
104
- <View style={{ flexDirection: 'row', borderBottomWidth: 1, borderBottomColor: 'lightgrey' }}>
177
+ <View style={addonsTabsContainerStyle}>
178
+ <View style={addonsTabsStyle}>
105
179
  <ScrollView
106
180
  horizontal
107
181
  showsHorizontalScrollIndicator={false}
108
- contentContainerStyle={{
109
- justifyContent: 'center',
110
- }}
182
+ contentContainerStyle={addonsTabsContentContainerStyle}
111
183
  >
112
184
  {Object.values(panels).map(({ id, title }) => {
113
185
  const resolvedTitle = typeof title === 'function' ? title({}) : title;
@@ -124,43 +196,18 @@ export const AddonsTabs = ({ onClose, storyId }: { onClose?: () => void; storyId
124
196
  </ScrollView>
125
197
 
126
198
  <IconButton
127
- style={{
128
- marginRight: 4,
129
- marginBottom: 4,
130
- alignItems: 'center',
131
- justifyContent: 'center',
132
- }}
133
- hitSlop={{ top: 10, right: 10, bottom: 10, left: 10 }}
199
+ style={closeIconStyle}
200
+ hitSlop={hitSlop}
134
201
  Icon={CloseIcon}
135
202
  onPress={() => onClose?.()}
136
203
  />
137
204
  </View>
138
205
  <ScrollView
139
- style={{ flex: 1 }}
206
+ style={addonsScrollStyle}
140
207
  // keyboardShouldPersistTaps="handled"
141
- contentContainerStyle={{
142
- paddingBottom: insets.bottom + 16,
143
- }}
208
+ contentContainerStyle={scrollContentContainerStyle}
144
209
  >
145
- {(() => {
146
- if (!storyId) {
147
- return (
148
- <View style={{ alignItems: 'center', justifyContent: 'center' }}>
149
- <Text>No Story Selected</Text>
150
- </View>
151
- );
152
- }
153
-
154
- if (Object.keys(panels).length === 0) {
155
- return (
156
- <View style={{ alignItems: 'center', justifyContent: 'center' }}>
157
- <Text>No addons loaded.</Text>
158
- </View>
159
- );
160
- }
161
-
162
- return panels[addonSelected].render({ active: true });
163
- })()}
210
+ {panel}
164
211
  </ScrollView>
165
212
  </View>
166
213
  );
@@ -1,10 +1,9 @@
1
1
  import BottomSheet, {
2
2
  BottomSheetBackdrop,
3
3
  BottomSheetBackdropProps,
4
- BottomSheetModal,
5
4
  BottomSheetScrollView,
6
5
  } from '@gorhom/bottom-sheet';
7
- import { ReactNode, forwardRef, useImperativeHandle, useRef } from 'react';
6
+ import { ReactNode, forwardRef, memo, useImperativeHandle, useMemo, useRef } from 'react';
8
7
  import { Keyboard } from 'react-native';
9
8
  import { useReducedMotion } from 'react-native-reanimated';
10
9
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
@@ -28,19 +27,21 @@ export const BottomSheetBackdropComponent = (backdropComponentProps: BottomSheet
28
27
  />
29
28
  );
30
29
 
31
- export const MobileMenuDrawer = forwardRef<MobileMenuDrawerRef, MobileMenuDrawerProps>(
32
- ({ children }, ref) => {
30
+ const snapPoints = ['50%', '75%'];
31
+
32
+ export const MobileMenuDrawer = memo(
33
+ forwardRef<MobileMenuDrawerRef, MobileMenuDrawerProps>(({ children }, ref) => {
33
34
  const reducedMotion = useReducedMotion();
34
35
  const insets = useSafeAreaInsets();
35
36
  const theme = useTheme();
36
37
 
37
- const menuBottomSheetRef = useRef<BottomSheetModal>(null);
38
+ const menuBottomSheetRef = useRef<BottomSheet>(null);
38
39
 
39
40
  useImperativeHandle(ref, () => ({
40
41
  setMobileMenuOpen: (open: boolean) => {
41
42
  if (open) {
42
43
  // menuBottomSheetRef.current?.present();
43
- menuBottomSheetRef.current?.expand();
44
+ menuBottomSheetRef.current?.snapToIndex(1);
44
45
  } else {
45
46
  Keyboard.dismiss();
46
47
 
@@ -50,32 +51,43 @@ export const MobileMenuDrawer = forwardRef<MobileMenuDrawerRef, MobileMenuDrawer
50
51
  },
51
52
  }));
52
53
 
54
+ const bgColorStyle = useMemo(() => {
55
+ return { backgroundColor: theme.background.content };
56
+ }, [theme.background.content]);
57
+
58
+ const handleIndicatorStyle = useMemo(() => {
59
+ return { backgroundColor: theme.textMutedColor };
60
+ }, [theme.textMutedColor]);
61
+
62
+ const contentContainerStyle = useMemo(() => {
63
+ return { paddingBottom: insets.bottom };
64
+ }, [insets.bottom]);
65
+
53
66
  return (
54
67
  <BottomSheet
55
68
  ref={menuBottomSheetRef}
56
69
  index={-1}
57
70
  animateOnMount={!reducedMotion}
58
- snapPoints={['50%', '75%']}
71
+ snapPoints={snapPoints}
59
72
  // enableDismissOnClose
60
73
  enableHandlePanningGesture
61
74
  enableContentPanningGesture
62
75
  enableDynamicSizing={false}
63
76
  keyboardBehavior="extend"
64
77
  keyboardBlurBehavior="restore"
78
+ enablePanDownToClose
65
79
  // stackBehavior="replace"
66
80
  backdropComponent={BottomSheetBackdropComponent}
67
- backgroundStyle={{ backgroundColor: theme.background.content }}
68
- handleIndicatorStyle={{ backgroundColor: theme.textMutedColor }}
81
+ backgroundStyle={bgColorStyle}
82
+ handleIndicatorStyle={handleIndicatorStyle}
69
83
  >
70
84
  <BottomSheetScrollView
71
85
  keyboardShouldPersistTaps="handled"
72
- contentContainerStyle={{
73
- paddingBottom: insets.bottom,
74
- }}
86
+ contentContainerStyle={contentContainerStyle}
75
87
  >
76
88
  {children}
77
89
  </BottomSheetScrollView>
78
90
  </BottomSheet>
79
91
  );
80
- }
92
+ })
81
93
  );
package/src/Search.tsx CHANGED
@@ -3,7 +3,7 @@ import { styled } from '@storybook/react-native-theming';
3
3
  import type { IFuseOptions } from 'fuse.js';
4
4
  import Fuse from 'fuse.js';
5
5
  import React, { useCallback, useDeferredValue, useRef, useState } from 'react';
6
- import { TextInput, View } from 'react-native';
6
+ import { Platform, TextInput, View } from 'react-native';
7
7
  import { CloseIcon } from './icon/CloseIcon';
8
8
  import { SearchIcon } from './icon/SearchIcon';
9
9
  import { useLayout } from './LayoutProvider';
@@ -61,6 +61,8 @@ const BottomSheetInput = styled(BottomSheetTextInput)(({ theme }) => ({
61
61
  height: 32,
62
62
  paddingLeft: 28,
63
63
  paddingRight: 28,
64
+ paddingTop: Platform.OS === 'android' ? 0 : undefined,
65
+ paddingBottom: Platform.OS === 'android' ? 0 : undefined,
64
66
  borderWidth: 1,
65
67
  borderColor: theme.appBorderColor,
66
68
  backgroundColor: 'transparent',
@@ -96,9 +96,12 @@ const BrandTitle: FC<{ theme: Theme }> = ({ theme }) => {
96
96
  };
97
97
 
98
98
  export const StorybookLogo: FC<{ theme: Theme }> = ({ theme }) => {
99
- if (theme.brand?.image) {
99
+ const image = useMemo(() => theme.brand?.image, [theme.brand?.image]);
100
+ const title = useMemo(() => theme.brand?.title, [theme.brand?.title]);
101
+
102
+ if (image) {
100
103
  return <BrandLogo theme={theme} />;
101
- } else if (theme.brand?.title) {
104
+ } else if (title) {
102
105
  return <BrandTitle theme={theme} />;
103
106
  } else {
104
107
  return <NoBrandLogo theme={theme} />;