@storybook/react-native-ui 8.0.0-alpha.4 → 8.2.0-alpha.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storybook/react-native-ui",
3
- "version": "8.0.0-alpha.4",
3
+ "version": "8.2.0-alpha.0",
4
4
  "description": "ui components for react native storybook",
5
5
  "keywords": [
6
6
  "react",
@@ -48,23 +48,22 @@
48
48
  "preset": "react-native"
49
49
  },
50
50
  "devDependencies": {
51
- "@storybook/react": "^8",
52
- "@storybook/types": "^8",
53
51
  "@types/jest": "^29.4.3",
54
- "@types/react": "~18.2.14",
52
+ "@types/react": "~18.2.79",
55
53
  "babel-jest": "^29.4.3",
56
- "jest": "^29.7.0",
54
+ "jest": "29.7.0",
57
55
  "react-test-renderer": "18.2.0",
58
56
  "tsup": "^7.2.0",
59
- "typescript": "^5.3.3"
57
+ "typescript": "~5.3.3"
60
58
  },
61
59
  "dependencies": {
62
- "@storybook/core-events": "^8",
63
- "@storybook/manager-api": "^8",
64
- "@storybook/react-native-theming": "^8.0.0-alpha.4",
60
+ "@storybook/core": "8.2.0-beta.0",
61
+ "@storybook/react": "8.2.0-beta.0",
62
+ "@storybook/react-native-theming": "^8.2.0-alpha.0",
65
63
  "fuse.js": "^7.0.0",
66
64
  "memoizerific": "^1.11.3",
67
- "polished": "^4.3.1"
65
+ "polished": "^4.3.1",
66
+ "store2": "^2.14.3"
68
67
  },
69
68
  "peerDependencies": {
70
69
  "@gorhom/bottom-sheet": ">=4",
@@ -81,5 +80,5 @@
81
80
  "publishConfig": {
82
81
  "access": "public"
83
82
  },
84
- "gitHead": "01c91fd5e53822bcbc1350a154d609c7c0092e69"
83
+ "gitHead": "56cad3ccee28ceae70cc7e2fc9b63ec28384eecf"
85
84
  }
package/src/Button.tsx CHANGED
@@ -18,7 +18,6 @@ export interface ButtonProps extends TouchableOpacityProps {
18
18
  export const Button = forwardRef<TouchableOpacity, ButtonProps>(
19
19
  (
20
20
  {
21
- // asChild = false,
22
21
  Icon,
23
22
  animation = 'none',
24
23
  size = 'small',
@@ -54,23 +53,8 @@ export const Button = forwardRef<TouchableOpacity, ButtonProps>(
54
53
  return () => clearTimeout(timer);
55
54
  }, [isAnimating]);
56
55
 
57
- // Match the old API with the new API.
58
- // TODO: Remove this after 9.0.
59
- // if (props.primary) {
60
- // localVariant = 'solid';
61
- // localSize = 'medium';
62
- // }
63
-
64
- // Match the old API with the new API.
65
- // TODO: Remove this after 9.0.
66
- // if (props.secondary || props.tertiary || props.gray || props.outline || props.inForm) {
67
- // localVariant = 'outline';
68
- // localSize = 'medium';
69
- // }
70
-
71
56
  return (
72
57
  <StyledButton
73
- // as={Comp}
74
58
  ref={ref}
75
59
  variant={variant}
76
60
  size={size}
@@ -96,19 +80,11 @@ export const Button = forwardRef<TouchableOpacity, ButtonProps>(
96
80
 
97
81
  Button.displayName = 'Button';
98
82
 
99
- // const StyledButton = styled('TouchableOpacity', {
100
- // shouldForwardProp: (prop) => isPropValid(prop),
101
- // })<
102
- // ButtonProps & {
103
- // animating: boolean;
104
- // animation: ButtonProps['animation'];
105
- // }
106
- // >(({ theme, variant, size, disabled, active, animating, animation, padding }) => ({
107
83
  const StyledButton = styled.TouchableOpacity<
108
84
  ButtonProps & { animating: boolean; animation: ButtonProps['animation'] }
109
- >(({ theme, variant, size, disabled, active, /* animating, animation, */ padding }) => ({
85
+ >(({ theme, variant, size, disabled, active, padding }) => ({
110
86
  border: 0,
111
- cursor: disabled ? 'not-allowed' : 'pointer',
87
+ // cursor: disabled ? 'not-allowed' : 'pointer',
112
88
  display: 'flex',
113
89
  flexDirection: 'row',
114
90
  gap: 6,
@@ -125,12 +101,9 @@ const StyledButton = styled.TouchableOpacity<
125
101
  paddingVertical: 0,
126
102
  height: size === 'small' ? 28 : 32,
127
103
  position: 'relative',
128
-
129
- // textDecoration: 'none',
130
104
  transitionProperty: 'background, box-shadow',
131
105
  transitionDuration: '150ms',
132
106
  transitionTimingFunction: 'ease-out',
133
- // verticalAlign: 'top',
134
107
  whiteSpace: 'nowrap',
135
108
  userSelect: 'none',
136
109
  opacity: disabled ? 0.5 : 1,
@@ -148,40 +121,6 @@ const StyledButton = styled.TouchableOpacity<
148
121
  borderRadius: theme.input.borderRadius,
149
122
  // Making sure that the button never shrinks below its minimum size
150
123
  flexShrink: 0,
151
-
152
- // '&:hover': {
153
- // color: variant === 'ghost' ? theme.color.secondary : null,
154
- // background: (() => {
155
- // let bgColor = theme.color.secondary;
156
- // if (variant === 'solid') bgColor = theme.color.secondary;
157
- // if (variant === 'outline') bgColor = theme.button.background;
158
-
159
- // if (variant === 'ghost') return transparentize(0.86, theme.color.secondary);
160
- // return theme.base === 'light' ? darken(0.02, bgColor) : lighten(0.03, bgColor);
161
- // })(),
162
- // },
163
-
164
- // '&:active': {
165
- // color: variant === 'ghost' ? theme.color.secondary : null,
166
- // background: (() => {
167
- // let bgColor = theme.color.secondary;
168
- // if (variant === 'solid') bgColor = theme.color.secondary;
169
- // if (variant === 'outline') bgColor = theme.button.background;
170
-
171
- // if (variant === 'ghost') return theme.background.hoverable;
172
- // return theme.base === 'light' ? darken(0.02, bgColor) : lighten(0.03, bgColor);
173
- // })(),
174
- // },
175
-
176
- // '&:focus': {
177
- // boxShadow: `${rgba(theme.color.secondary, 1)} 0 0 0 1px inset`,
178
- // outline: 'none',
179
- // },
180
-
181
- // '> svg': {
182
- // animation:
183
- // animating && animation !== 'none' ? `${theme.animation[animation]} 1000ms ease-out` : '',
184
- // },
185
124
  }));
186
125
 
187
126
  export const ButtonText = styled.Text<{
@@ -198,7 +137,6 @@ export const ButtonText = styled.Text<{
198
137
  flexDirection: 'row',
199
138
  gap: 6,
200
139
  textAlign: 'center',
201
- // lineHeight: theme.typography.size.s1,
202
140
  fontSize: theme.typography.size.s1,
203
141
  fontWeight: theme.typography.weight.bold,
204
142
  }));
@@ -231,13 +169,3 @@ export const ButtonIcon = ({
231
169
 
232
170
  return <Icon color={color} />;
233
171
  };
234
-
235
- // color: variant === 'ghost' ? theme.color.secondary : null,
236
- // background: (() => {
237
- // let bgColor = theme.color.secondary;
238
- // if (variant === 'solid') bgColor = theme.color.secondary;
239
- // if (variant === 'outline') bgColor = theme.button.background;
240
-
241
- // if (variant === 'ghost') return theme.background.hoverable;
242
- // return theme.base === 'light' ? darken(0.02, bgColor) : lighten(0.03, bgColor);
243
- // })(),
@@ -1,4 +1,3 @@
1
- import { ManagerContext } from '@storybook/manager-api';
2
1
  import { Explorer } from './Explorer';
3
2
  import { mockDataset } from './mockdata';
4
3
  import type { RefType } from './types';
@@ -9,11 +8,6 @@ export default {
9
8
  title: 'UI/Sidebar/Explorer',
10
9
  parameters: { layout: 'fullscreen', theme: 'side-by-side' },
11
10
  decorators: [
12
- (storyFn: any) => (
13
- <ManagerContext.Provider value={{ state: { docsOptions: {} } } as any}>
14
- {storyFn()}
15
- </ManagerContext.Provider>
16
- ),
17
11
  (storyFn: any) => <View style={{ paddingHorizontal: 20 }}>{storyFn()}</View>,
18
12
  (storyFn: any) => <View style={{ paddingHorizontal: 20 }}>{storyFn()}</View>,
19
13
  ],
package/src/Explorer.tsx CHANGED
@@ -1,11 +1,7 @@
1
1
  import type { FC } from 'react';
2
2
  import React, { useRef } from 'react';
3
-
4
3
  import { Ref } from './Refs';
5
4
  import type { CombinedDataset, Selection } from './types';
6
-
7
- // import { useHighlighted } from './useHighlighted';
8
- // import { HighlightStyles } from './HighlightStyles';
9
5
  import { View } from 'react-native';
10
6
 
11
7
  export interface ExplorerProps {
@@ -25,18 +21,8 @@ export const Explorer: FC<ExplorerProps> = React.memo(function Explorer({
25
21
  }) {
26
22
  const containerRef = useRef<View>(null);
27
23
 
28
- // Track highlighted nodes, keep it in sync with props and enable keyboard navigation
29
- // const [highlighted, setHighlighted, highlightedRef] = useHighlighted({
30
- // containerRef,
31
- // isLoading,
32
- // isBrowsing,
33
- // dataset,
34
- // selected,
35
- // });
36
-
37
24
  return (
38
25
  <View ref={containerRef} id="storybook-explorer-tree">
39
- {/* {highlighted && <HighlightStyles {...highlighted} />} */}
40
26
  {dataset.entries.map(([refId, ref]) => (
41
27
  <Ref
42
28
  {...ref}
@@ -45,8 +31,6 @@ export const Explorer: FC<ExplorerProps> = React.memo(function Explorer({
45
31
  isBrowsing={isBrowsing}
46
32
  selectedStoryId={selected?.refId === ref.id ? selected.storyId : null}
47
33
  setSelection={setSelection}
48
- // highlightedRef={highlightedRef}
49
- // setHighlighted={setHighlighted}
50
34
  />
51
35
  ))}
52
36
  </View>
@@ -1,11 +1,6 @@
1
- import { Meta, StoryObj } from '@storybook/react';
2
-
1
+ import type { Meta, StoryObj } from '@storybook/react';
3
2
  import { Layout } from './Layout';
4
- // import { mockDataset } from './mockdata';
5
- // import { index } from './mockdata.large';
6
3
  import { mockDataset } from './mockdata';
7
- // import { GestureHandlerRootView } from 'react-native-gesture-handler';
8
- // import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
9
4
  import { Text } from 'react-native';
10
5
  import { GestureHandlerRootView } from 'react-native-gesture-handler';
11
6
  import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
package/src/Layout.tsx CHANGED
@@ -1,9 +1,9 @@
1
- import { SET_CURRENT_STORY } from '@storybook/core-events';
2
- import { addons } from '@storybook/manager-api';
1
+ import { SET_CURRENT_STORY } from '@storybook/core/core-events';
2
+ import { addons } from '@storybook/core/manager-api';
3
3
  import { styled, useTheme } from '@storybook/react-native-theming';
4
- import { type API_IndexHash, type Args, type StoryContext } from '@storybook/types';
5
- import { ReactNode, useRef } from 'react';
6
- import { Text, View } from 'react-native';
4
+ import { type API_IndexHash, type Args, type StoryContext } from '@storybook/core/types';
5
+ import { ReactNode, useRef, useState } from 'react';
6
+ import { Platform, Text, View } from 'react-native';
7
7
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
8
8
  import { IconButton } from './IconButton';
9
9
  import { Sidebar } from './Sidebar';
@@ -11,7 +11,7 @@ import { DEFAULT_REF_ID } from './constants';
11
11
  import { BottomBarToggleIcon } from './icon/BottomBarToggleIcon';
12
12
  import { MenuIcon } from './icon/MenuIcon';
13
13
 
14
- import { ReactRenderer } from '@storybook/react';
14
+ import type { ReactRenderer } from '@storybook/react';
15
15
  import { MobileMenuDrawer, MobileMenuDrawerRef } from './MobileMenuDrawer';
16
16
  import { MobileAddonsPanel, MobileAddonsPanelRef } from './MobileAddonsPanel';
17
17
 
@@ -26,13 +26,15 @@ export const Layout = ({
26
26
  }) => {
27
27
  const theme = useTheme();
28
28
  const mobileMenuDrawerRef = useRef<MobileMenuDrawerRef>(null);
29
+ const [menuOpen, setMenuOpen] = useState(false);
30
+ const [drawerOpen, setDrawerOpen] = useState(false);
29
31
  const addonPanelRef = useRef<MobileAddonsPanelRef>(null);
30
32
  const insets = useSafeAreaInsets();
31
33
 
32
34
  return (
33
35
  <View style={{ flex: 1, paddingTop: insets.top }}>
34
36
  <View style={{ flex: 1 }}>{children}</View>
35
- <MobileMenuDrawer ref={mobileMenuDrawerRef}>
37
+ <MobileMenuDrawer ref={mobileMenuDrawerRef} onStateChange={setDrawerOpen}>
36
38
  <Sidebar
37
39
  extra={[]}
38
40
  previewInitialized
@@ -49,25 +51,30 @@ export const Layout = ({
49
51
  refId={DEFAULT_REF_ID}
50
52
  />
51
53
  </MobileMenuDrawer>
52
- <MobileAddonsPanel ref={addonPanelRef} storyId={story?.id} />
53
- <Container style={{ marginBottom: insets.bottom }}>
54
- <Nav>
55
- <Button
56
- hitSlop={{ bottom: 10, left: 10, right: 10, top: 10 }}
57
- onPress={() => mobileMenuDrawerRef.current.setMobileMenuOpen(true)}
58
- >
59
- <MenuIcon color={theme.color.mediumdark} />
60
- <Text>
61
- {story?.title}/{story?.name}
62
- </Text>
63
- </Button>
54
+ <MobileAddonsPanel ref={addonPanelRef} storyId={story?.id} onStateChange={setMenuOpen} />
55
+ {(Platform.OS !== 'android' || (!menuOpen && !drawerOpen)) && (
56
+ <Container style={{ marginBottom: insets.bottom }}>
57
+ <Nav>
58
+ <Button
59
+ style={{ flexShrink: 1 }}
60
+ hitSlop={{ bottom: 10, left: 10, right: 10, top: 10 }}
61
+ onPress={() => {
62
+ mobileMenuDrawerRef.current.setMobileMenuOpen(true);
63
+ }}
64
+ >
65
+ <MenuIcon color={theme.color.mediumdark} />
66
+ <Text style={{ flexShrink: 1 }} numberOfLines={1}>
67
+ {story?.title}/{story?.name}
68
+ </Text>
69
+ </Button>
64
70
 
65
- <IconButton
66
- onPress={() => addonPanelRef.current.setAddonsPanelOpen(true)}
67
- Icon={BottomBarToggleIcon}
68
- />
69
- </Nav>
70
- </Container>
71
+ <IconButton
72
+ onPress={() => addonPanelRef.current.setAddonsPanelOpen(true)}
73
+ Icon={BottomBarToggleIcon}
74
+ />
75
+ </Nav>
76
+ </Container>
77
+ )}
71
78
  </View>
72
79
  );
73
80
  };
@@ -1,143 +1,159 @@
1
- import { BottomSheetModal, BottomSheetScrollView } from '@gorhom/bottom-sheet';
2
- import { addons } from '@storybook/manager-api';
1
+ import { BottomSheetModal } from '@gorhom/bottom-sheet';
2
+ import { addons } from '@storybook/core/manager-api';
3
3
  import { styled } from '@storybook/react-native-theming';
4
- import { Addon_TypesEnum } from '@storybook/types';
4
+ import { Addon_TypesEnum } from '@storybook/core/types';
5
5
  import { forwardRef, useImperativeHandle, useRef, useState } from 'react';
6
- import { Text, View } from 'react-native';
6
+ import { Text, View, useWindowDimensions } from 'react-native';
7
7
  import { ScrollView } from 'react-native-gesture-handler';
8
- import { useReducedMotion } from 'react-native-reanimated';
8
+ import Animated, {
9
+ useAnimatedKeyboard,
10
+ useAnimatedStyle,
11
+ useReducedMotion,
12
+ useSharedValue,
13
+ } from 'react-native-reanimated';
9
14
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
10
15
 
11
16
  import { IconButton } from './IconButton';
12
17
  import { CloseIcon } from './icon/CloseIcon';
13
18
 
14
19
  export interface MobileAddonsPanelRef {
15
- isAddonsPanelOpen: boolean;
16
20
  setAddonsPanelOpen: (isOpen: boolean) => void;
17
21
  }
18
22
 
19
- export const MobileAddonsPanel = forwardRef<MobileAddonsPanelRef, { storyId?: string }>(
20
- ({ storyId }, ref) => {
21
- const [isAddonsPanelOpen, setAddonsPanelOpen] = useState(false);
22
- const reducedMotion = useReducedMotion();
23
-
24
- const addonsPanelBottomSheetRef = useRef<BottomSheetModal>(null);
25
- const insets = useSafeAreaInsets();
26
-
27
- const panels = addons.getElements(Addon_TypesEnum.PANEL);
28
- const [addonSelected, setAddonSelected] = useState(Object.keys(panels)[0]);
29
-
30
- useImperativeHandle(ref, () => ({
31
- isAddonsPanelOpen,
32
- setAddonsPanelOpen: (open: boolean) => {
33
- if (open) {
34
- setAddonsPanelOpen(true);
35
- addonsPanelBottomSheetRef.current?.present();
36
- } else {
37
- setAddonsPanelOpen(false);
38
- addonsPanelBottomSheetRef.current?.dismiss();
39
- }
40
- },
41
- }));
42
-
43
- return (
44
- <BottomSheetModal
45
- ref={addonsPanelBottomSheetRef}
46
- index={1}
47
- animateOnMount={!reducedMotion}
48
- onDismiss={() => {
49
- setAddonsPanelOpen(false);
50
- }}
51
- snapPoints={['25%', '50%']}
52
- style={{
53
- paddingTop: 8,
54
- }}
55
- containerStyle={{}}
56
- backgroundStyle={{
57
- borderRadius: 0,
58
- borderTopColor: 'lightgrey',
59
- borderTopWidth: 1,
60
- }}
61
- keyboardBehavior="interactive"
62
- keyboardBlurBehavior="restore"
63
- enableDismissOnClose
64
- enableHandlePanningGesture={true}
65
- enableContentPanningGesture={true}
66
- // enablePanDownToClose={false}
67
- stackBehavior="replace"
68
- >
69
- <View style={{ flex: 1 }}>
70
- <View style={{ flexDirection: 'row' }}>
71
- <ScrollView
72
- horizontal
73
- showsHorizontalScrollIndicator={false}
74
- contentContainerStyle={{
75
- paddingHorizontal: 8,
76
- // alignItems: 'center',
77
- justifyContent: 'center',
78
- }}
79
- >
80
- {Object.values(panels).map(({ id, title }) => {
81
- const resolvedTitle = typeof title === 'function' ? title({}) : title;
82
-
83
- return (
84
- <Tab
85
- key={id}
86
- active={id === addonSelected}
87
- onPress={() => setAddonSelected(id)}
88
- text={resolvedTitle}
89
- />
90
- );
91
- })}
92
- </ScrollView>
93
- <IconButton
94
- style={{
95
- marginRight: 4,
96
- marginBottom: 4,
97
- alignItems: 'center',
98
- justifyContent: 'center',
99
- }}
100
- hitSlop={{ top: 10, right: 10, bottom: 10, left: 10 }}
101
- Icon={CloseIcon}
102
- onPress={() => {
103
- setAddonsPanelOpen(false);
104
- addonsPanelBottomSheetRef.current?.dismiss();
105
- }}
106
- />
107
- </View>
108
- <BottomSheetScrollView
109
- keyboardShouldPersistTaps="handled"
23
+ export const MobileAddonsPanel = forwardRef<
24
+ MobileAddonsPanelRef,
25
+ { storyId?: string; onStateChange: (open: boolean) => void }
26
+ >(({ storyId, onStateChange }, ref) => {
27
+ const reducedMotion = useReducedMotion();
28
+
29
+ const addonsPanelBottomSheetRef = useRef<BottomSheetModal>(null);
30
+ const insets = useSafeAreaInsets();
31
+
32
+ const panels = addons.getElements(Addon_TypesEnum.PANEL);
33
+ const [addonSelected, setAddonSelected] = useState(Object.keys(panels)[0]);
34
+ const animatedPosition = useSharedValue(0);
35
+
36
+ // bringing in animated keyboard disables android resizing
37
+ // TODO replicate functionality without this
38
+ useAnimatedKeyboard();
39
+
40
+ useImperativeHandle(ref, () => ({
41
+ setAddonsPanelOpen: (open: boolean) => {
42
+ if (open) {
43
+ onStateChange(true);
44
+ addonsPanelBottomSheetRef.current?.present();
45
+ } else {
46
+ onStateChange(false);
47
+ addonsPanelBottomSheetRef.current?.dismiss();
48
+ }
49
+ },
50
+ }));
51
+
52
+ const { height } = useWindowDimensions();
53
+
54
+ const adjustedBottomSheetSize = useAnimatedStyle(() => {
55
+ return {
56
+ maxHeight: height - animatedPosition.value - insets.bottom,
57
+ };
58
+ }, [animatedPosition.value, height, insets.bottom]);
59
+
60
+ return (
61
+ <BottomSheetModal
62
+ ref={addonsPanelBottomSheetRef}
63
+ index={1}
64
+ animateOnMount={!reducedMotion}
65
+ onDismiss={() => {
66
+ onStateChange(false);
67
+ }}
68
+ snapPoints={['25%', '50%', '75%']}
69
+ style={{
70
+ paddingTop: 8,
71
+ }}
72
+ animatedPosition={animatedPosition}
73
+ containerStyle={{}}
74
+ backgroundStyle={{
75
+ borderRadius: 0,
76
+ borderTopColor: 'lightgrey',
77
+ borderTopWidth: 1,
78
+ }}
79
+ keyboardBehavior="extend"
80
+ // keyboardBlurBehavior="restore"
81
+ enableDismissOnClose
82
+ enableHandlePanningGesture={true}
83
+ // enableContentPanningGesture={true}
84
+ stackBehavior="replace"
85
+ >
86
+ <Animated.View style={[{ flex: 1 }, adjustedBottomSheetSize]}>
87
+ <View style={{ flexDirection: 'row' }}>
88
+ <ScrollView
89
+ horizontal
90
+ showsHorizontalScrollIndicator={false}
110
91
  contentContainerStyle={{
111
- paddingBottom: insets.bottom + 16,
112
- marginTop: 10,
113
- paddingHorizontal: 16,
92
+ paddingHorizontal: 8,
93
+ justifyContent: 'center',
114
94
  }}
115
95
  >
116
- {(() => {
117
- if (!storyId) {
118
- return (
119
- <View style={{ alignItems: 'center', justifyContent: 'center' }}>
120
- <Text>No Story Selected</Text>
121
- </View>
122
- );
123
- }
124
-
125
- if (Object.keys(panels).length === 0) {
126
- return (
127
- <View style={{ alignItems: 'center', justifyContent: 'center' }}>
128
- <Text>No addons loaded.</Text>
129
- </View>
130
- );
131
- }
132
-
133
- return panels[addonSelected].render({ active: true });
134
- })()}
135
- </BottomSheetScrollView>
96
+ {Object.values(panels).map(({ id, title }) => {
97
+ const resolvedTitle = typeof title === 'function' ? title({}) : title;
98
+
99
+ return (
100
+ <Tab
101
+ key={id}
102
+ active={id === addonSelected}
103
+ onPress={() => setAddonSelected(id)}
104
+ text={resolvedTitle}
105
+ />
106
+ );
107
+ })}
108
+ </ScrollView>
109
+ <IconButton
110
+ style={{
111
+ marginRight: 4,
112
+ marginBottom: 4,
113
+ alignItems: 'center',
114
+ justifyContent: 'center',
115
+ }}
116
+ hitSlop={{ top: 10, right: 10, bottom: 10, left: 10 }}
117
+ Icon={CloseIcon}
118
+ onPress={() => {
119
+ onStateChange(false);
120
+ addonsPanelBottomSheetRef.current?.dismiss();
121
+ }}
122
+ />
136
123
  </View>
137
- </BottomSheetModal>
138
- );
139
- }
140
- );
124
+ <ScrollView
125
+ style={{ flex: 1 }}
126
+ keyboardShouldPersistTaps="handled"
127
+ contentContainerStyle={{
128
+ paddingBottom: insets.bottom + 16,
129
+ marginTop: 10,
130
+ paddingHorizontal: 16,
131
+ }}
132
+ >
133
+ {(() => {
134
+ if (!storyId) {
135
+ return (
136
+ <View style={{ alignItems: 'center', justifyContent: 'center' }}>
137
+ <Text>No Story Selected</Text>
138
+ </View>
139
+ );
140
+ }
141
+
142
+ if (Object.keys(panels).length === 0) {
143
+ return (
144
+ <View style={{ alignItems: 'center', justifyContent: 'center' }}>
145
+ <Text>No addons loaded.</Text>
146
+ </View>
147
+ );
148
+ }
149
+
150
+ return panels[addonSelected].render({ active: true });
151
+ })()}
152
+ </ScrollView>
153
+ </Animated.View>
154
+ </BottomSheetModal>
155
+ );
156
+ });
141
157
 
142
158
  const Tab = ({ active, onPress, text }: { active: boolean; onPress: () => void; text: string }) => {
143
159
  return (
@@ -150,7 +166,6 @@ const Tab = ({ active, onPress, text }: { active: boolean; onPress: () => void;
150
166
  const TabButton = styled.TouchableOpacity<{ active: boolean }>(({ theme, active }) => ({
151
167
  borderBottomWidth: active ? 2 : 0,
152
168
  borderBottomColor: active ? theme.barSelectedColor : undefined,
153
-
154
169
  overflow: 'hidden',
155
170
  paddingHorizontal: 15,
156
171
  justifyContent: 'center',
@@ -161,6 +176,6 @@ const TabText = styled.Text<{ active: boolean }>(({ theme, active }) => ({
161
176
  color: active ? theme.barSelectedColor : theme.color.mediumdark,
162
177
  textAlign: 'center',
163
178
  fontWeight: 'bold',
164
- fontSize: 13,
179
+ fontSize: 12,
165
180
  lineHeight: 12,
166
181
  }));