@@ -3,8 +3,8 @@ import { addons } from '@storybook/core/manager-api';
33import { type API_IndexHash , type Args , type StoryContext } from '@storybook/core/types' ;
44import type { ReactRenderer } from '@storybook/react' ;
55import { 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' ;
88import { useSafeAreaInsets } from 'react-native-safe-area-context' ;
99import { IconButton } from './IconButton' ;
1010import { useLayout } from './LayoutProvider' ;
@@ -18,6 +18,39 @@ import { BottomBarToggleIcon } from './icon/BottomBarToggleIcon';
1818import { CloseFullscreenIcon } from './icon/CloseFullscreenIcon' ;
1919import { FullscreenIcon } from './icon/FullscreenIcon' ;
2020import { 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 ;
2154
2255export const Layout = ( {
2356 storyHash,
@@ -46,58 +79,114 @@ export const Layout = ({
4679
4780 const [ uiHidden , setUiHidden ] = useState ( false ) ;
4881
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+
49168 if ( isDesktop ) {
50169 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 } >
67172 { desktopSidebarOpen ? (
68173 < ScrollView
69174 keyboardShouldPersistTaps = "handled"
70- contentContainerStyle = { {
71- paddingBottom : insets . bottom ,
72- } }
175+ contentContainerStyle = { desktopScrollViewContentContainerStyle }
73176 >
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 } >
85178 < StorybookLogo theme = { theme } />
86179
87180 < IconButton onPress = { ( ) => setDesktopSidebarOpen ( false ) } Icon = { MenuIcon } />
88181 </ View >
89182
90183 < Sidebar
91- extra = { [ ] }
184+ extra = { placeholderArray }
92185 previewInitialized
93186 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 }
101190 index = { storyHash }
102191 storyId = { story ?. id }
103192 refId = { DEFAULT_REF_ID }
@@ -108,23 +197,15 @@ export const Layout = ({
108197 ) }
109198 </ View >
110199
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 } >
123204 { desktopAddonsPanelOpen ? (
124205 < AddonsTabs storyId = { story ?. id } onClose = { ( ) => setDesktopAddonsPanelOpen ( false ) } />
125206 ) : (
126207 < IconButton
127- style = { { marginLeft : 'auto' } }
208+ style = { iconFloatRightStyle }
128209 onPress = { ( ) => setDesktopAddonsPanelOpen ( true ) }
129210 Icon = { BottomBarToggleIcon }
130211 />
@@ -136,28 +217,13 @@ export const Layout = ({
136217 }
137218
138219 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 } >
147222 { children }
148223
149224 { story ?. parameters ?. hideFullScreenButton ? null : (
150225 < 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 }
161227 onPress = { ( ) => setUiHidden ( ( prev ) => ! prev ) }
162228 >
163229 { uiHidden ? (
@@ -170,18 +236,16 @@ export const Layout = ({
170236 </ View >
171237
172238 { ! uiHidden ? (
173- < Container style = { { marginBottom : insets . bottom } } >
239+ < Container style = { containerStyle } >
174240 < Nav >
175241 < Button
176242 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 }
182246 >
183247 < MenuIcon color = { theme . color . mediumdark } />
184- < Text style = { { flexShrink : 1 , color : theme . color . defaultText } } numberOfLines = { 1 } >
248+ < Text style = { navButtonTextStyle } numberOfLines = { 1 } >
185249 { story ?. title } /{ story ?. name }
186250 </ Text >
187251 </ Button >
@@ -196,21 +260,17 @@ export const Layout = ({
196260 ) : null }
197261
198262 < MobileMenuDrawer ref = { mobileMenuDrawerRef } >
199- < View style = { { paddingLeft : 16 , paddingTop : 4 , paddingBottom : 4 } } >
263+ < View style = { mobileMenuDrawerContentStyle } >
200264 < StorybookLogo theme = { theme } />
201265 </ View >
202266
203267 < Sidebar
204- extra = { [ ] }
268+ extra = { placeholderArray }
205269 previewInitialized
206270 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 }
214274 index = { storyHash }
215275 storyId = { story ?. id }
216276 refId = { DEFAULT_REF_ID }
0 commit comments