@storybook/react-native 6.5.0-rc.3 → 6.5.0-rc.5

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 (26) hide show
  1. package/dist/constants.d.ts +5 -0
  2. package/dist/constants.js +5 -0
  3. package/dist/hooks.d.ts +39 -0
  4. package/dist/hooks.js +70 -2
  5. package/dist/preview/View.d.ts +1 -0
  6. package/dist/preview/View.js +7 -2
  7. package/dist/preview/components/OnDeviceUI/OnDeviceUI.d.ts +1 -2
  8. package/dist/preview/components/OnDeviceUI/OnDeviceUI.js +16 -9
  9. package/dist/preview/components/OnDeviceUI/Panel.d.ts +1 -1
  10. package/dist/preview/components/OnDeviceUI/Panel.js +5 -1
  11. package/dist/preview/components/OnDeviceUI/addons/Addons.js +3 -2
  12. package/dist/preview/components/OnDeviceUI/addons/AddonsSkeleton.d.ts +14 -0
  13. package/dist/preview/components/OnDeviceUI/addons/AddonsSkeleton.js +59 -0
  14. package/dist/preview/components/OnDeviceUI/addons/Wrapper.js +4 -1
  15. package/dist/preview/components/OnDeviceUI/navigation/Navigation.d.ts +3 -5
  16. package/dist/preview/components/OnDeviceUI/navigation/Navigation.js +22 -8
  17. package/dist/preview/components/OnDeviceUI/navigation/NavigationButton.d.ts +3 -0
  18. package/dist/preview/components/OnDeviceUI/navigation/NavigationButton.js +20 -0
  19. package/dist/preview/components/Shared/icons.d.ts +35 -6
  20. package/dist/preview/components/Shared/icons.js +44 -31
  21. package/dist/preview/components/StoryListView/StoryListView.js +4 -4
  22. package/dist/preview/components/StoryView/StoryView.js +19 -13
  23. package/package.json +2 -2
  24. package/readme.md +2 -0
  25. package/dist/preview/components/OnDeviceUI/navigation/VisibilityButton.d.ts +0 -8
  26. package/dist/preview/components/OnDeviceUI/navigation/VisibilityButton.js +0 -45
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Duration for a typical transition animation, such as rescaling the preview
3
+ * UI.
4
+ */
5
+ export declare const ANIMATION_DURATION_TRANSITION = 400;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Duration for a typical transition animation, such as rescaling the preview
3
+ * UI.
4
+ */
5
+ export const ANIMATION_DURATION_TRANSITION = 400;
package/dist/hooks.d.ts CHANGED
@@ -21,7 +21,46 @@ export declare function useIsStorySelected(storyId: string): boolean;
21
21
  * Hook that indicates if `title` is the currently selected story section.
22
22
  */
23
23
  export declare function useIsStorySectionSelected(title: string): boolean;
24
+ /**
25
+ * Hook that causes a re-render when the currently selected story is changed.
26
+ */
27
+ export declare function useUpdateOnStoryChanged(): void;
24
28
  /**
25
29
  * Hook that gets the current theme values.
26
30
  */
27
31
  export declare function useTheme(): Theme;
32
+ /**
33
+ * A boolean atom creator for an atom that can only be toggled between
34
+ * true/false.
35
+ *
36
+ * @see {@link https://jotai.org/docs/recipes/atom-creators#atomwithtoggle}
37
+ */
38
+ export declare function atomWithToggle(initialValue?: boolean): import("jotai").WritableAtom<boolean, [nextValue?: boolean], void> & {
39
+ init: boolean;
40
+ };
41
+ /**
42
+ * Hook that retrieves the current state, and a setter, for the `isUIVisible`
43
+ * atom.
44
+ */
45
+ export declare function useIsUIVisible(): [boolean, (nextValue?: boolean) => void];
46
+ /**
47
+ * Hook that retrieves the current state, and a setter, for the
48
+ * `isSplitPanelVisibleAtom` atom.
49
+ */
50
+ export declare function useIsSplitPanelVisible(): [boolean, (nextValue?: boolean) => void];
51
+ interface SyncExternalUIParams {
52
+ isUIVisible?: boolean;
53
+ isSplitPanelVisible?: boolean;
54
+ }
55
+ /**
56
+ * Sync the UI atom states with external values, such as from Story parameters.
57
+ */
58
+ export declare function syncExternalUI({ isUIVisible, isSplitPanelVisible }: SyncExternalUIParams): void;
59
+ /**
60
+ * Hook that manages the state for the currently selected addon.
61
+ *
62
+ * This value persists across stories, so that the same addon will be selected
63
+ * when switching stories.
64
+ */
65
+ export declare function useSelectedAddon(initialValue?: string): [string, (args_0: string | ((prev: string) => string)) => void];
66
+ export {};
package/dist/hooks.js CHANGED
@@ -1,5 +1,5 @@
1
- import { useMemo } from 'react';
2
- import { atom, useAtomValue, useSetAtom } from 'jotai';
1
+ import React, { useMemo } from 'react';
2
+ import { atom, useAtom, useAtomValue, useSetAtom, getDefaultStore } from 'jotai';
3
3
  import { useTheme as useEmotionTheme } from 'emotion-theming';
4
4
  const storyContextAtom = atom(null);
5
5
  /**
@@ -34,9 +34,77 @@ export function useIsStorySelected(storyId) {
34
34
  export function useIsStorySectionSelected(title) {
35
35
  return useAtomValue(useMemo(() => atom((get) => { var _a; return ((_a = get(storyContextAtom)) === null || _a === void 0 ? void 0 : _a.title) === title; }), [title]));
36
36
  }
37
+ /**
38
+ * Hook that causes a re-render when the currently selected story is changed.
39
+ */
40
+ export function useUpdateOnStoryChanged() {
41
+ useAtomValue(useMemo(() => atom((get) => { var _a; return (_a = get(storyContextAtom)) === null || _a === void 0 ? void 0 : _a.id; }), []));
42
+ }
37
43
  /**
38
44
  * Hook that gets the current theme values.
39
45
  */
40
46
  export function useTheme() {
41
47
  return useEmotionTheme();
42
48
  }
49
+ /**
50
+ * A boolean atom creator for an atom that can only be toggled between
51
+ * true/false.
52
+ *
53
+ * @see {@link https://jotai.org/docs/recipes/atom-creators#atomwithtoggle}
54
+ */
55
+ export function atomWithToggle(initialValue) {
56
+ const anAtom = atom(initialValue, (get, set, nextValue) => {
57
+ const update = nextValue !== null && nextValue !== void 0 ? nextValue : !get(anAtom);
58
+ set(anAtom, update);
59
+ });
60
+ return anAtom;
61
+ }
62
+ const isUIVisibleAtom = atomWithToggle(true);
63
+ /**
64
+ * Hook that retrieves the current state, and a setter, for the `isUIVisible`
65
+ * atom.
66
+ */
67
+ export function useIsUIVisible() {
68
+ return useAtom(isUIVisibleAtom);
69
+ }
70
+ const isSplitPanelVisibleAtom = atomWithToggle(false);
71
+ /**
72
+ * Hook that retrieves the current state, and a setter, for the
73
+ * `isSplitPanelVisibleAtom` atom.
74
+ */
75
+ export function useIsSplitPanelVisible() {
76
+ return useAtom(isSplitPanelVisibleAtom);
77
+ }
78
+ /**
79
+ * Sync the UI atom states with external values, such as from Story parameters.
80
+ */
81
+ export function syncExternalUI({ isUIVisible, isSplitPanelVisible }) {
82
+ const jotaiStore = getDefaultStore();
83
+ if (isUIVisible !== undefined) {
84
+ jotaiStore.set(isUIVisibleAtom, isUIVisible);
85
+ }
86
+ if (isSplitPanelVisible !== undefined) {
87
+ jotaiStore.set(isSplitPanelVisibleAtom, isSplitPanelVisible);
88
+ }
89
+ }
90
+ const selectedAddonAtom = atom(undefined);
91
+ /**
92
+ * Hook that manages the state for the currently selected addon.
93
+ *
94
+ * This value persists across stories, so that the same addon will be selected
95
+ * when switching stories.
96
+ */
97
+ export function useSelectedAddon(initialValue) {
98
+ const result = useAtom(selectedAddonAtom);
99
+ const set = result[1];
100
+ React.useEffect(() => {
101
+ const jotaiStore = getDefaultStore();
102
+ // Only apply the initial value once, and only if the atom doesn't have a
103
+ // value yet.
104
+ if (jotaiStore.get(selectedAddonAtom) === undefined) {
105
+ set(initialValue);
106
+ }
107
+ // eslint-disable-next-line react-hooks/exhaustive-deps
108
+ }, []);
109
+ return result;
110
+ }
@@ -30,6 +30,7 @@ export type Params = {
30
30
  shouldPersistSelection?: boolean;
31
31
  tabOpen?: number;
32
32
  isUIHidden?: boolean;
33
+ isSplitPanelVisible?: boolean;
33
34
  shouldDisableKeyboardAvoidingView?: boolean;
34
35
  keyboardAvoidingViewVerticalOffset?: number;
35
36
  theme: DeepPartial<Theme>;
@@ -13,7 +13,7 @@ import { toId } from '@storybook/csf';
13
13
  import { addons } from '@storybook/addons';
14
14
  import { ThemeProvider } from 'emotion-theming';
15
15
  import { SafeAreaProvider } from 'react-native-safe-area-context';
16
- import { useSetStoryContext } from '../hooks';
16
+ import { useSetStoryContext, syncExternalUI } from '../hooks';
17
17
  import OnDeviceUI from './components/OnDeviceUI';
18
18
  import { theme } from './components/Shared/theme';
19
19
  import StoryView from './components/StoryView';
@@ -90,6 +90,11 @@ export class View {
90
90
  // eslint-disable-next-line consistent-this
91
91
  const self = this;
92
92
  const appliedTheme = deepmerge(theme, (_a = params.theme) !== null && _a !== void 0 ? _a : {});
93
+ // Sync the Storybook parameters (external) with app UI state (internal), to initialise them.
94
+ syncExternalUI({
95
+ isUIVisible: params.isUIHidden !== undefined ? !params.isUIHidden : undefined,
96
+ isSplitPanelVisible: params.isSplitPanelVisible,
97
+ });
93
98
  return () => {
94
99
  const setContext = useSetStoryContext();
95
100
  const [, forceUpdate] = useReducer((x) => x + 1, 0);
@@ -112,7 +117,7 @@ export class View {
112
117
  if (onDeviceUI) {
113
118
  return (React.createElement(SafeAreaProvider, null,
114
119
  React.createElement(ThemeProvider, { theme: appliedTheme },
115
- React.createElement(OnDeviceUI, { storyIndex: self._storyIndex, isUIHidden: params.isUIHidden, tabOpen: params.tabOpen, shouldDisableKeyboardAvoidingView: params.shouldDisableKeyboardAvoidingView, keyboardAvoidingViewVerticalOffset: params.keyboardAvoidingViewVerticalOffset }))));
120
+ React.createElement(OnDeviceUI, { storyIndex: self._storyIndex, tabOpen: params.tabOpen, shouldDisableKeyboardAvoidingView: params.shouldDisableKeyboardAvoidingView, keyboardAvoidingViewVerticalOffset: params.keyboardAvoidingViewVerticalOffset }))));
116
121
  }
117
122
  else {
118
123
  return React.createElement(StoryView, null);
@@ -5,9 +5,8 @@ interface OnDeviceUIProps {
5
5
  storyIndex: StoryIndex;
6
6
  url?: string;
7
7
  tabOpen?: number;
8
- isUIHidden?: boolean;
9
8
  shouldDisableKeyboardAvoidingView?: boolean;
10
9
  keyboardAvoidingViewVerticalOffset?: number;
11
10
  }
12
- declare const _default: React.MemoExoticComponent<({ storyIndex, isUIHidden, shouldDisableKeyboardAvoidingView, keyboardAvoidingViewVerticalOffset, tabOpen: initialTabOpen, }: OnDeviceUIProps) => JSX.Element>;
11
+ declare const _default: React.MemoExoticComponent<({ storyIndex, shouldDisableKeyboardAvoidingView, keyboardAvoidingViewVerticalOffset, tabOpen: initialTabOpen, }: OnDeviceUIProps) => JSX.Element>;
13
12
  export default _default;
@@ -1,18 +1,19 @@
1
1
  import styled from '@emotion/native';
2
2
  import React, { useState, useRef } from 'react';
3
3
  import { Animated, Dimensions, Easing, Keyboard, KeyboardAvoidingView, Platform, TouchableOpacity, StatusBar, StyleSheet, View, } from 'react-native';
4
- import { useStoryContextParam, useTheme } from '../../../hooks';
4
+ import { useIsSplitPanelVisible, useIsUIVisible, useStoryContextParam, useTheme, } from '../../../hooks';
5
+ import { ANIMATION_DURATION_TRANSITION } from '../../../constants';
5
6
  import StoryListView from '../StoryListView';
6
7
  import StoryView from '../StoryView';
7
8
  import AbsolutePositionedKeyboardAwareView from './absolute-positioned-keyboard-aware-view';
8
9
  import Addons from './addons/Addons';
10
+ import { AddonsSkeleton } from './addons/AddonsSkeleton';
9
11
  import { getAddonPanelPosition, getNavigatorPanelPosition, getPreviewShadowStyle, getPreviewStyle, } from './animation';
10
12
  import Navigation from './navigation';
11
13
  import { PREVIEW, ADDONS } from './navigation/constants';
12
14
  import Panel from './Panel';
13
15
  import { useWindowDimensions } from 'react-native';
14
16
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
15
- const ANIMATION_DURATION = 400;
16
17
  const IS_IOS = Platform.OS === 'ios';
17
18
  // @ts-ignore: Property 'Expo' does not exist on type 'Global'
18
19
  const getExpoRoot = () => global.Expo || global.__expo || global.__exponent;
@@ -33,7 +34,7 @@ const styles = StyleSheet.create({
33
34
  expoAndroidContainer: { paddingTop: StatusBar.currentHeight },
34
35
  });
35
36
  const Container = styled.View(({ theme }) => (Object.assign(Object.assign({ flex: 1, backgroundColor: theme.preview.containerBackgroundColor }, (IS_ANDROID && IS_EXPO ? styles.expoAndroidContainer : undefined)), Platform.select({ web: { overflow: 'hidden' } }))));
36
- const OnDeviceUI = ({ storyIndex, isUIHidden, shouldDisableKeyboardAvoidingView, keyboardAvoidingViewVerticalOffset, tabOpen: initialTabOpen, }) => {
37
+ const OnDeviceUI = ({ storyIndex, shouldDisableKeyboardAvoidingView, keyboardAvoidingViewVerticalOffset, tabOpen: initialTabOpen, }) => {
37
38
  const [tabOpen, setTabOpen] = useState(initialTabOpen || PREVIEW);
38
39
  const lastTabOpen = React.useRef(tabOpen);
39
40
  const [previewDimensions, setPreviewDimensions] = useState(() => ({
@@ -43,7 +44,6 @@ const OnDeviceUI = ({ storyIndex, isUIHidden, shouldDisableKeyboardAvoidingView,
43
44
  const animatedValue = useRef(new Animated.Value(tabOpen));
44
45
  const wide = useWindowDimensions().width >= BREAKPOINT;
45
46
  const insets = useSafeAreaInsets();
46
- const [isUIVisible, setIsUIVisible] = useState(isUIHidden !== undefined ? !isUIHidden : true);
47
47
  const handleToggleTab = React.useCallback((newTabOpen) => {
48
48
  if (newTabOpen === tabOpen) {
49
49
  return;
@@ -51,7 +51,7 @@ const OnDeviceUI = ({ storyIndex, isUIHidden, shouldDisableKeyboardAvoidingView,
51
51
  lastTabOpen.current = tabOpen;
52
52
  Animated.timing(animatedValue.current, {
53
53
  toValue: newTabOpen,
54
- duration: ANIMATION_DURATION,
54
+ duration: ANIMATION_DURATION_TRANSITION,
55
55
  easing: Easing.inOut(Easing.cubic),
56
56
  useNativeDriver: true,
57
57
  }).start();
@@ -73,6 +73,7 @@ const OnDeviceUI = ({ storyIndex, isUIHidden, shouldDisableKeyboardAvoidingView,
73
73
  lastTabOpen: lastTabOpen.current,
74
74
  }),
75
75
  ];
76
+ const [isUIVisible] = useIsUIVisible();
76
77
  // The initial value is just a guess until the layout calculation has been done.
77
78
  const [navBarHeight, setNavBarHeight] = React.useState(insets.bottom + 40);
78
79
  const measureNavigation = React.useCallback(({ nativeEvent }) => {
@@ -93,6 +94,7 @@ const OnDeviceUI = ({ storyIndex, isUIHidden, shouldDisableKeyboardAvoidingView,
93
94
  const safeAreaMargins = {
94
95
  paddingBottom: isUIVisible ? insets.bottom + navBarHeight : noSafeArea ? 0 : insets.bottom,
95
96
  paddingTop: !noSafeArea ? insets.top : 0,
97
+ overflow: 'hidden',
96
98
  };
97
99
  // The panels always apply the safe area, regardless of the story parameters.
98
100
  const panelSafeAreaMargins = {
@@ -102,14 +104,19 @@ const OnDeviceUI = ({ storyIndex, isUIHidden, shouldDisableKeyboardAvoidingView,
102
104
  // Adjust the keyboard offset (possibly in a negative direction) to account
103
105
  // for the safe area and navigation bar.
104
106
  const keyboardVerticalOffset = -panelSafeAreaMargins.paddingBottom + (keyboardAvoidingViewVerticalOffset !== null && keyboardAvoidingViewVerticalOffset !== void 0 ? keyboardAvoidingViewVerticalOffset : 0);
107
+ const [isSplitPanelVisible] = useIsSplitPanelVisible();
108
+ const isPreviewInactive = tabOpen !== PREVIEW;
105
109
  return (React.createElement(React.Fragment, null,
106
110
  React.createElement(Container, null,
107
- React.createElement(KeyboardAvoidingView, { enabled: !shouldDisableKeyboardAvoidingView || tabOpen !== PREVIEW, behavior: IS_IOS ? 'padding' : null, keyboardVerticalOffset: keyboardVerticalOffset, style: flex },
111
+ React.createElement(KeyboardAvoidingView, { enabled: !shouldDisableKeyboardAvoidingView || isPreviewInactive, behavior: IS_IOS ? 'padding' : null, keyboardVerticalOffset: keyboardVerticalOffset, style: flex },
108
112
  React.createElement(AbsolutePositionedKeyboardAwareView, { onLayout: setPreviewDimensions, previewDimensions: previewDimensions },
109
113
  React.createElement(Animated.View, { style: previewWrapperStyles },
110
114
  React.createElement(Preview, { style: safeAreaMargins, animatedValue: animatedValue.current },
111
- React.createElement(StoryView, null)),
112
- tabOpen !== PREVIEW ? (React.createElement(TouchableOpacity, { style: StyleSheet.absoluteFillObject, onPress: () => handleToggleTab(PREVIEW) })) : null),
115
+ React.createElement(StoryView, null),
116
+ isSplitPanelVisible ? (React.createElement(Panel, { edge: "top", style: { flex: 1 } },
117
+ React.createElement(Addons, { active: true }),
118
+ React.createElement(AddonsSkeleton, { visible: isPreviewInactive }))) : null),
119
+ isPreviewInactive ? (React.createElement(TouchableOpacity, { style: StyleSheet.absoluteFillObject, onPress: () => handleToggleTab(PREVIEW) })) : null),
113
120
  React.createElement(Panel, { edge: "right", style: [
114
121
  getNavigatorPanelPosition(animatedValue.current, previewDimensions.width, wide),
115
122
  panelSafeAreaMargins,
@@ -120,6 +127,6 @@ const OnDeviceUI = ({ storyIndex, isUIHidden, shouldDisableKeyboardAvoidingView,
120
127
  panelSafeAreaMargins,
121
128
  ] },
122
129
  React.createElement(Addons, { active: tabOpen === ADDONS })))),
123
- React.createElement(Navigation, { onLayout: measureNavigation, tabOpen: tabOpen, onChangeTab: handleToggleTab, isUIVisible: isUIVisible, setIsUIVisible: setIsUIVisible }))));
130
+ React.createElement(Navigation, { onLayout: measureNavigation, tabOpen: tabOpen, onChangeTab: handleToggleTab }))));
124
131
  };
125
132
  export default React.memo(OnDeviceUI);
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { Animated, StyleProp, ViewStyle } from 'react-native';
3
3
  interface PanelProps {
4
- edge: 'left' | 'right';
4
+ edge: 'left' | 'right' | 'top';
5
5
  style: StyleProp<Animated.AnimatedProps<ViewStyle>>;
6
6
  children: React.ReactNode;
7
7
  }
@@ -3,12 +3,16 @@ import { StyleSheet, Animated } from 'react-native';
3
3
  import styled from '@emotion/native';
4
4
  const Container = styled(Animated.View)(({ theme, edge }) => ({
5
5
  backgroundColor: theme.panel.backgroundColor,
6
+ borderTopWidth: edge === 'top' ? theme.panel.borderWidth : undefined,
6
7
  borderStartWidth: edge === 'left' ? theme.panel.borderWidth : undefined,
7
8
  borderEndWidth: edge === 'right' ? theme.panel.borderWidth : undefined,
8
9
  borderColor: theme.panel.borderColor,
9
10
  }));
10
11
  const Panel = ({ edge, children, style }) => {
11
- const containerStyle = StyleSheet.flatten([StyleSheet.absoluteFillObject, style]);
12
+ const containerStyle = StyleSheet.flatten([
13
+ edge === 'top' ? undefined : StyleSheet.absoluteFillObject,
14
+ style,
15
+ ]);
12
16
  return (React.createElement(Container, { edge: edge, style: containerStyle }, children));
13
17
  };
14
18
  export default React.memo(Panel);
@@ -1,12 +1,13 @@
1
- import React, { useState } from 'react';
1
+ import React from 'react';
2
2
  import { Text } from 'react-native';
3
3
  import { addons } from '@storybook/addons';
4
+ import { useSelectedAddon } from '../../../../hooks';
4
5
  import AddonsList from './List';
5
6
  import AddonWrapper from './Wrapper';
6
7
  import { Box } from '../../Shared/layout';
7
8
  const Addons = ({ active }) => {
8
9
  const panels = addons.getElements('panel');
9
- const [addonSelected, setAddonSelected] = useState(Object.keys(panels)[0] || null);
10
+ const [addonSelected, setAddonSelected] = useSelectedAddon(Object.keys(panels)[0]);
10
11
  if (Object.keys(panels).length === 0) {
11
12
  return (React.createElement(Box, { alignItems: "center", justifyContent: "center" },
12
13
  React.createElement(Text, null, "No addons loaded.")));
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ /**
3
+ * Component that mimics the addons panel.
4
+ *
5
+ * The main reason this exists is that the scaled addons view feels more
6
+ * cluttered than a more abstract skeleton view, which allows users to focus
7
+ * on the story content rather than become distracted by the addons UI in an
8
+ * already small view.
9
+ */
10
+ export declare const AddonsSkeleton: React.NamedExoticComponent<AddonsSkeletonProps>;
11
+ interface AddonsSkeletonProps {
12
+ visible: boolean;
13
+ }
14
+ export {};
@@ -0,0 +1,59 @@
1
+ import React from 'react';
2
+ import styled from '@emotion/native';
3
+ import { Animated, Easing, StyleSheet, View } from 'react-native';
4
+ import { ANIMATION_DURATION_TRANSITION } from '../../../../constants';
5
+ /**
6
+ * Component that mimics the addons panel.
7
+ *
8
+ * The main reason this exists is that the scaled addons view feels more
9
+ * cluttered than a more abstract skeleton view, which allows users to focus
10
+ * on the story content rather than become distracted by the addons UI in an
11
+ * already small view.
12
+ */
13
+ export const AddonsSkeleton = React.memo(function AddonsSkeleton({ visible }) {
14
+ const progress = React.useRef(new Animated.Value(visible ? 1 : 0));
15
+ React.useEffect(() => {
16
+ Animated.timing(progress.current, {
17
+ toValue: visible ? 1 : 0,
18
+ duration: ANIMATION_DURATION_TRANSITION,
19
+ useNativeDriver: true,
20
+ easing: Easing.inOut(Easing.cubic),
21
+ }).start();
22
+ }, [visible]);
23
+ return (React.createElement(AddonsSkeletonContainer, { pointerEvents: "none", opacity: progress.current },
24
+ React.createElement(TabsSkeleton, null),
25
+ React.createElement(AddonsContentSkeleton, null)));
26
+ });
27
+ const TabSkeleton = styled.View(({ theme, active }) => ({
28
+ opacity: active ? 1 : 0.5,
29
+ backgroundColor: active ? theme.tabs.activeBackgroundColor : theme.tokens.color.grey200,
30
+ borderRadius: theme.tokens.borderRadius.round,
31
+ width: active ? 100 : 70,
32
+ height: 30,
33
+ marginRight: 12,
34
+ }));
35
+ const BoxSkeleton = styled.View(({ theme, width, height }) => ({
36
+ backgroundColor: theme.tokens.color.blue200,
37
+ borderRadius: theme.tokens.borderRadius.large,
38
+ height,
39
+ width,
40
+ }));
41
+ function AddonsFieldSkeleton({ long = false }) {
42
+ return (React.createElement(View, { style: { marginBottom: 32 } },
43
+ React.createElement(BoxSkeleton, { width: 75, height: 10, marginBottom: 12 }),
44
+ React.createElement(BoxSkeleton, { width: long ? 200 : 120, height: 15 })));
45
+ }
46
+ function AddonsContentSkeleton() {
47
+ return (React.createElement(React.Fragment, null,
48
+ React.createElement(AddonsFieldSkeleton, { long: true }),
49
+ React.createElement(AddonsFieldSkeleton, { long: true }),
50
+ React.createElement(AddonsFieldSkeleton, null),
51
+ React.createElement(AddonsFieldSkeleton, null)));
52
+ }
53
+ function TabsSkeleton() {
54
+ return (React.createElement(View, { style: { flexDirection: 'row', marginBottom: 16 } },
55
+ React.createElement(TabSkeleton, null),
56
+ React.createElement(TabSkeleton, { active: true }),
57
+ React.createElement(TabSkeleton, null)));
58
+ }
59
+ const AddonsSkeletonContainer = styled(Animated.View)(({ theme }) => (Object.assign(Object.assign({}, StyleSheet.absoluteFillObject), { flex: 1, backgroundColor: theme.panel.backgroundColor, borderTopWidth: theme.panel.borderWidth, borderColor: theme.panel.borderColor, padding: theme.panel.paddingHorizontal, overflow: 'hidden' })));
@@ -1,12 +1,15 @@
1
1
  import React from 'react';
2
2
  import { ScrollView } from 'react-native';
3
3
  import styled from '@emotion/native';
4
- import { useTheme } from '../../../../hooks';
4
+ import { useTheme, useUpdateOnStoryChanged } from '../../../../hooks';
5
5
  const Container = styled.View(({ selected }) => ({
6
6
  display: selected ? 'flex' : 'none',
7
7
  flex: 1,
8
8
  }));
9
9
  const Wrapper = ({ panels, addonSelected }) => {
10
+ // Force a re-render when the current story changes, to ensure that the addon
11
+ // panels state is not desynced.
12
+ useUpdateOnStoryChanged();
10
13
  const theme = useTheme();
11
14
  const addonKeys = Object.keys(panels);
12
15
  const content = addonKeys.map((id) => {
@@ -1,11 +1,9 @@
1
- import React, { Dispatch, SetStateAction } from 'react';
1
+ import React from 'react';
2
2
  import { ViewProps } from 'react-native';
3
- interface Props {
3
+ interface NavigationProps {
4
4
  tabOpen: number;
5
5
  onChangeTab: (index: number) => void;
6
- isUIVisible: boolean;
7
- setIsUIVisible: Dispatch<SetStateAction<boolean>>;
8
6
  onLayout: ViewProps['onLayout'];
9
7
  }
10
- declare const _default: React.MemoExoticComponent<({ tabOpen, onChangeTab, isUIVisible, setIsUIVisible, onLayout }: Props) => JSX.Element>;
8
+ declare const _default: React.MemoExoticComponent<({ tabOpen, onChangeTab, onLayout }: NavigationProps) => JSX.Element>;
11
9
  export default _default;
@@ -2,8 +2,9 @@ import React from 'react';
2
2
  import { View } from 'react-native';
3
3
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
4
4
  import GestureRecognizer from 'react-native-swipe-gestures';
5
+ import { useIsUIVisible } from '../../../../hooks';
5
6
  import { NavigationBar } from './NavigationBar';
6
- import VisibilityButton from './VisibilityButton';
7
+ import { VisibilityButton, AddonsSplitButton } from './NavigationButton';
7
8
  const SWIPE_CONFIG = {
8
9
  velocityThreshold: 0.2,
9
10
  directionalOffsetThreshold: 80,
@@ -14,11 +15,8 @@ const navStyle = {
14
15
  right: 0,
15
16
  bottom: 0,
16
17
  };
17
- const Navigation = ({ tabOpen, onChangeTab, isUIVisible, setIsUIVisible, onLayout }) => {
18
+ const Navigation = ({ tabOpen, onChangeTab, onLayout }) => {
18
19
  const insets = useSafeAreaInsets();
19
- const handleToggleUI = () => {
20
- setIsUIVisible((oldIsUIVisible) => !oldIsUIVisible);
21
- };
22
20
  const handleSwipeLeft = () => {
23
21
  if (tabOpen < 1) {
24
22
  onChangeTab(tabOpen + 1);
@@ -29,9 +27,25 @@ const Navigation = ({ tabOpen, onChangeTab, isUIVisible, setIsUIVisible, onLayou
29
27
  onChangeTab(tabOpen - 1);
30
28
  }
31
29
  };
30
+ const [isUIVisible] = useIsUIVisible();
32
31
  return (React.createElement(View, { style: navStyle, onLayout: onLayout },
33
- isUIVisible && (React.createElement(GestureRecognizer, { onSwipeLeft: handleSwipeLeft, onSwipeRight: handleSwipeRight, config: SWIPE_CONFIG },
34
- React.createElement(NavigationBar, { index: tabOpen, onPress: onChangeTab, style: { paddingBottom: insets.bottom } }))),
35
- React.createElement(VisibilityButton, { onPress: handleToggleUI, style: { paddingBottom: insets.bottom } })));
32
+ React.createElement(View, null, isUIVisible && (React.createElement(GestureRecognizer, { onSwipeLeft: handleSwipeLeft, onSwipeRight: handleSwipeRight, config: SWIPE_CONFIG },
33
+ React.createElement(NavigationBar, { index: tabOpen, onPress: onChangeTab, style: { paddingBottom: insets.bottom } })))),
34
+ React.createElement(NavigationShortcuts, null,
35
+ React.createElement(VisibilityButton, null),
36
+ React.createElement(AddonsSplitButton, null))));
36
37
  };
37
38
  export default React.memo(Navigation);
39
+ function NavigationShortcuts({ children }) {
40
+ const insets = useSafeAreaInsets();
41
+ return (React.createElement(View, { style: {
42
+ zIndex: 100,
43
+ alignSelf: 'center',
44
+ justifyContent: 'center',
45
+ alignItems: 'center',
46
+ flexDirection: 'row-reverse',
47
+ position: 'absolute',
48
+ bottom: insets.bottom + 14,
49
+ right: 8,
50
+ } }, children));
51
+ }
@@ -0,0 +1,3 @@
1
+ /// <reference types="react" />
2
+ export declare function VisibilityButton(): JSX.Element;
3
+ export declare function AddonsSplitButton(): JSX.Element;
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { TouchableWithoutFeedback } from 'react-native';
3
+ import { useIsSplitPanelVisible, useIsUIVisible } from '../../../../hooks';
4
+ import { Icon } from '../../Shared/icons';
5
+ import { Box } from '../../Shared/layout';
6
+ const hitSlop = { top: 5, left: 5, right: 5, bottom: 5 };
7
+ function NavigationButton({ iconName, inverseIconName, active, toggle }) {
8
+ return (React.createElement(TouchableWithoutFeedback, { onPress: toggle, hitSlop: hitSlop },
9
+ React.createElement(Box, { marginHorizontal: 8 },
10
+ React.createElement(Icon, { flex: 1, background: true, name: inverseIconName, opacity: 0.8, pointerEvents: "none" },
11
+ React.createElement(Icon, { name: iconName, opacity: active ? 0.6 : 0.25 })))));
12
+ }
13
+ export function VisibilityButton() {
14
+ const [active, toggle] = useIsUIVisible();
15
+ return (React.createElement(NavigationButton, { iconName: "layout-bottom", inverseIconName: "layout-bottom-inverse", active: active, toggle: () => toggle() }));
16
+ }
17
+ export function AddonsSplitButton() {
18
+ const [active, toggle] = useIsSplitPanelVisible();
19
+ return (React.createElement(NavigationButton, { iconName: "layout-split", inverseIconName: "layout-split-inverse", active: active, toggle: () => toggle() }));
20
+ }
@@ -1,6 +1,35 @@
1
- /// <reference types="react" />
2
- export declare const GridIcon: () => JSX.Element;
3
- export declare const StoryIcon: ({ selected }: {
4
- selected: boolean;
5
- }) => JSX.Element;
6
- export declare const SearchIcon: () => JSX.Element;
1
+ import React from 'react';
2
+ declare const iconSources: {
3
+ grid: {
4
+ uri: string;
5
+ };
6
+ 'story-white': {
7
+ uri: string;
8
+ };
9
+ 'story-blue': {
10
+ uri: string;
11
+ };
12
+ search: {
13
+ uri: string;
14
+ };
15
+ 'layout-bottom': {
16
+ uri: string;
17
+ };
18
+ 'layout-bottom-inverse': {
19
+ uri: string;
20
+ };
21
+ 'layout-split': {
22
+ uri: string;
23
+ };
24
+ 'layout-split-inverse': {
25
+ uri: string;
26
+ };
27
+ };
28
+ export type IconName = keyof typeof iconSources;
29
+ declare const StyledImage: any;
30
+ interface IconProps extends React.ComponentProps<typeof StyledImage> {
31
+ name: IconName;
32
+ background?: boolean;
33
+ }
34
+ export declare function Icon({ name, background, ...props }: IconProps): JSX.Element;
35
+ export {};
@@ -1,34 +1,47 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
1
12
  import React from 'react';
2
- import { Image, StyleSheet } from 'react-native';
13
+ import { Image, ImageBackground } from 'react-native';
14
+ import styled from '@emotion/native';
3
15
  const iconSources = {
4
- grid: { uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAMAAABHPGVmAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAALdQTFRFAAAAIJ//Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqj9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Haf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqb9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqj9Hqf9Hmz01QAAAD10Uk5TAAAEGjNefYqLXDUFAjSY0e79/9KZAghmyMkBQbz6vUIBBnD1cQcOo6Sp76qBePTHVOoC+RQTX4mCDZsBMmX5zDcAAAHcSURBVHic7ZrrcoIwEIVX8W5VpAiUaq33GyJqrfby/s9VacsCDojUJNNx9vzCTLLfZJdkHM4ChJTJSrl8oVj6s4qFckWqZiBed7V6Q24qV6op36uVVgxC042rAQh60LUIhPnY7rBCuOo8dZ9PGb3+gCXC1WA4CjPGE9YIV9NxiNHmwVCUWYAy4rIPV1PMmNnnxVCUoVf9LvOa+xp0f89HqCDzhbWMle3NsuPnWIt5MF7757zogfOxcqLOEGrtzVufm6U5Kz9iR3eHNgYObNWXc4svhQDs1C0GNTbHgZp/l6hnt5ECApqKQZu1471b93O1S1h6OQR2fsbqGcg28JeTtDIFBBwM28iCJON7lZSsVBAN3zFZCpRkkbgwDQQWWJQcvOK2LLYQCwPnoYDPS7aQJQYuwJ4/ZA8l/pASQQhCEIIQhCAEIQhBghB7nSj839xMnmtHQriJIAS5IYiQw3g7dxdBCEIQghCEIAQhyH+ECPmMLsQQKOMzY2vjgIHLYkwaIXaTEOMM+FuAIMbMhJYIW1aIwczaKj9EWuVCTH++7Quml8Qev0aMnl+qtxkfxnu4cYXLXiYhhpg2H4AP1g1L7a55ygDGrVdGZOuVKwFNZN/KSp/XtsPVpOpJ0C+2naE/XA0rpwAAAABJRU5ErkJggg==' },
5
- storyWhite: { uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAAIRlWElmTU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAAGSgAwAEAAAAAQAAAGQAAAAA3IGzQgAAAAlwSFlzAAALEwAACxMBAJqcGAAAAVlpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDYuMC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KGV7hBwAACdlJREFUeAHtnW2oZVUZx73jjFNhGYMiKUkhCAYGDdj4FhKRYkIiIZkhhgRKBPahGL9I9CEsISxB7OVDbypaiUoDRmqKOjKNVmhgQiISilYqNJGN83b7/fZZz2nd4znnnnvPPmev46wHnrPWXq/P+v/Xs9ba+9y7zxFHVKkIVAQqAhWBikBFoCJQEWgfgaW2mlxeXt5AW0eih9pqcwHaET/1wNLS0nIx9kLGkWhr5BYzsAkNaXP8U4OoMcyOg9pO/AKCj6Pv9hKdun3aKFFibK9i3A7G/3uNdFJO6ylTAYYBGzDgEOFm7LkTvUjDDkP5Djh8tY1xT0tI4x0QcjvGfA49gMYeEmvqYB8xuyKMcQxe5+kRN7S9KGuoDPbRS/1/ubg2jLp5Wp4e+dG2eXn7kR5pR6WGtkPKDWCxiXB/SltzEI2uuWJ0TPgRKv8RlQiNdWM/nETwN6H/RE+GjH+DSX8ZXysQnozWK1F3a2pAMiJtvW0uYr2NyejjCE9N8XVPymkADNd1dlTpITA1FtMQEiQEMXF9OIdTYxHuNg2Ik+xDGuqGH+L1JPWifJdh2CpWM7e5DUJWAysGNLU7r9bRHPJjLDPrataExAD2MILfonEd4SQDy8tGPMLV6k9aLm8n6hi6pOvZ70DPT2Hkc9m+zJoQj8KeOF7kOHhJ++bPp0WOsS5VL6HvQxeakFhzfdblDVSc2X3UEnlEixSB38hEepNwC9rGAWjVgc7aQ8IAB7efwcHLsk9G424+8osMsTXsyg8kkTaTcC6sJ8tL94hhAIfNEQ4r02raPAlp1fC3a2OVkMKYrYRUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqodUQgpDoDBzqocURsi8Xhwws2GnN0RE+76Fct2v14tGugwXlhCIaF6jBwH7cgBJb7x+Ud4WkdtufCEJAfT+iyaJX8g4tqF6xkMQ8Rihr2z1PSVzeyWGfbYhC0cIQB+lVxAeDwB3oWfnQJB+N9eXU+Y/UTbPLz2+UJt6AEz4foDdjUqGnqEnqL5l6GL0ccock4iL17iSXL4sDCEAvDkBfDKwPoGehO5FfVOdnq76bi7TPozups6xqY4vel4IWQhCEhlvEn4IVPUMlyvfY+Wb3gbFNEk5BX2COidAinUXgpTiCQFI9wwB9YXNu1BfJubJahzAkiJhH0Al5YOpjeKXr6IJcVYDpBu4p6jHUV/yLxmTACthlj0Bdfk6JbU1jkiKdivFEgKA4RnnAtFO1Fk/KRmBqsTpKceiesppyVOKJaVIQhIZesb5APkw6ma9VjKo0ojgS8p70F20eXrJpBRHCIB50ycZ/vTFb1DFo+24ZSqOvE3hIR+SIqHvQnfS9jmJlHFtDmlm9knFEAJIviGzuQMn/CxDvycNX7DHvYTZ/DjyStwoEXzzbetR+vhEIr4oUoogRDIAyUcd+4lfQfwOVAmwe1crP31lqLNeMn6BPoYKtsvTKDHfOsoD9HVhaaR0TkgiwweFknEVQP1EtJDVyIhl7E7q6lHnoX9AXZ68Dxkl4Snm76DPzyRSJKtz6ZQQwLB/fzbpAPGvEP9+QsRHIM78YaJnSJbA/pi6l1KXYOm/XJ+B7kTjPoToUBF821B+Rf3PU98Jscm2esndfHZGCAO3b4E8SPxa4jeivmBZMjxVDZPIF9BbqHtlKuT+0zzdJe0c0h5AYyNPRd4SSHiQciv1v0hdva7TX5zrghDG3v/OQjK+AQjXo4Lt7B9HhmUE8kbA+xKhj9ld7g6helmz7BD/JFk7UL3IPcV2h0lOyo+o/2XboWCQMqresLa6TcN4BysgV6OKgPiLbbkIuPIXtCGfsAEt1f22mYh1o2yTMPBhnmUUyWt+oo74W8gjLW//l1ZAfPQyaFuTkT5y27+W2t9AXtMW4XvRV1LZQTvzds9KdRtsjM9NMG6thDRrM/WCmJvSAF27BweZsprAvMj/ugPk2iVqpHeT199/iP8cVby3ycFrErOPfFJcl/qJMR5DubcVIc8woP6sIf7DBMQkIAWI2xNIztyRZFhGoUzfewb6C3KTCSsC88ITv9lrqWlrC+kvp5KD9cM+sxfGQ/6aDe62NLDVyNBzYrDXJJA3krYqGVlfuaeER9rvIKjJpCbISbkp9auH/CMVGqwbNppdPCEx255OA7sjDWqSNT0Vbe5NnPFrIiMjJfeUSfcsQZY45buom/xLXiAzJaQ/g2IALYcxmzczkHtp+9Oopxg3y1HnffPDri9w6vkpdb0+SHzNpx7qCKB2eLOynbj3K+5FntjUsJFoX7RNIs3XO09Eo+9RdlOkQ2FgseGNO2VRrL/sGM+XIa8HJWal6Zc6PMJWbtZox72n8RbCa1FFT9CmUWJ+eLll8uUp6uRpUy9Zw2ZH2zTHjIq777ge7MebsjiyXsxsdnmT9FZ+q5z2nO2C6A3kt4j7ZEBbGu8jHCbmi5G2K6Ns7+W28DkPQsLM/loeCVmYk/EpALtHMgj3obFUZMXXF6UtSXEJk5TvEb86taRtAXpK6geSMM72fsE2IvMkZJS9+8jQMwTeR+L3BRmjKkyTnggOUn5AW1ek9gTdidGpdE2IjzVclgTiY4D1O8hovkefJSoZKXrhz+jLp8WKE6NTUrokRM/wAeAb6FkAszORMe77DIq2I4kUN3RJ8fsUT4BKp6R0RYhk6Bl70DMB5MkEzFzIoM9GJAX1ZCcpvybxvJTVGSldEBLL1GsMfhtAPJ08Q5I6kUSKS+X9GHAuGvdKc50gDn7ehDhAl6lX0I8CwLOJjLkPnP5XCLY0f91I+AgZZ6PeQGrrXCdKG4RMeiyNPeNvDNI/xXm+i2WKvkdKIsXlazeFzkRdUl1a50ZKG4RMcrOkBziw51A948VExtwGSr8TCbbFnvIUFbahLq3aPokXT4IFTY2WaQiJzl9NzespkZb3uJcLXf8Z1D3j713vGblxw+KJFPeUZ8k/HX0ZdQyOZVBizO47kqdMumr0SrfxCajN3Suh36a9jip7e0Hz6c3XG+n6T4RHN5b2Hoe0YcLM28BmSfB5y4noC6jimPLnV/5jkLIrlfXLsyBp5jau6ICOPR5q8GVoiMbmBnt/8c5Urim/opHCL7Dd5coxHof6RVtIPk4fQG5N5eJJdTcjw5Bm2SO8AP0zGvIvIjegjYGEC0dGIIrtQcrRxG9G96CKRDyI+g9CkjY1Gf8Dj4t7s6eI4kwAAAAASUVORK5CYII=' },
6
- storyBlue: { uri: 'data:image/png;base64,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' },
7
- search: { uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAABgWlDQ1BzUkdCIElFQzYxOTY2LTIuMQAAKJF1kd8rg1EYxz/baGLy88KFsjSuTEyJG2USamnNlOFme+2H2ubtfbe03Cq3K0rc+HXBX8Ctcq0UkZJbrokb1ut5t9Uke07PeT7ne87zdM5zwBpMKim9ZgBS6YwWmPI6F0KLTvsLVlqw00VrWNHVcb/fR1X7vMdixlu3Wav6uX+tYSWqK2CpEx5TVC0jPC3sW8+oJu8ItyuJ8IrwmXCfJhcUvjP1SIlfTY6X+NtkLRiYAGuzsDP+iyO/WEloKWF5Oa5UMquU72O+xBFNz89J7BbvRCfAFF6czDDJBMMMMirzMG489MuKKvkDxfxZ1iRXkVklh8YqcRJk6BM1K9WjEmOiR2UkyZn9/9tXPTbkKVV3eKH22TDee8C+DYW8YXwdGUbhGGxPcJmu5K8dwsiH6PmK5jqApk04v6pokV242IKORzWshYuSTdwai8HbKTSGoO0G6pdKPSvvc/IAwQ35qmvY24deOd+0/AMfDGfFB6WkFAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAqNJREFUWIXF2DmIFFEQBuBvVh3FC0EFdT3QaDORTUTFI9hQPAIRQxE0ETXwQBADD7zAaBEDTQxlIxExE4VFjTzYxQNFERUTr/VCvII3Q78Ze2Z6drvZH5ppmqq//q73XnXVlGRHFzZgJeZiDibjLd7gMa7gOr62wdsWStiEAfzNeH3HhYroXNGNu02CPsN9vMOfBjZHUc5DzBb8qAtwG7uxMMW+jB704kOd3w3MGImYg3WEQ9jehn8nrtVxPMeC4YjZ4v+3S8tIFmzD54jrnnAIMqNbWPcqwRFhU48EC/A04uzLyllSu4Fv5CCmimX4HXGvzeK0Se2eGe4yNcLpiH8QY1s5xHVmR85iYEJFSDXG5mbGXZHhnQLEVLEqinO5meGByHBPgYJKeF2J80XIWirimrGoQEFwLoq1PM2gQyhkhMr8vGBBg9H9nFaC3hYshtAVVNGZZtAhqZ5DhcupjTE+zaBDkplZhcthdnQ/kGbQIUnjTDm1Ck0QL9ODRoKeVO5LQjdYJFZXfj/hVSOjjZKj2FugmGn4WYlzq5nhJMlX/oMGxzEHHJa8+MlWxhcj46sFiFkiyc57TG/lME9tL7Q1RzFlYQNXuXdmdTweOX3C/JwEHYt4BzAuq2MZNyPnx1g6AiFlYer4FXH2tEsyEy8igl84of36tFgYkepHo2F1EwvxsI7oIVZk8J2KQ5INnHbta0bQqG+egktYV/f8lTAuDwoVfkj4HHRijVD44mw+ErLShbPR8/041UxYI7HrK6RZx+jq9RG71G7gvdrIVDOMFea1PuFPhEYiPqMfZ4S9mIaWotoddSYK81unUNHHC0f5AV5WgrTCXrXLNazlyxu5LV+eSBU1ZhQF9eObpFj2CIdh1BFn6s1oZqiKfqHtWYLz/wAUO/neFu9thgAAAABJRU5ErkJggg==' },
8
- };
9
- export const GridIcon = () => {
10
- return (React.createElement(Image, { source: iconSources.grid, style: styles.sectionIcon }));
11
- };
12
- export const StoryIcon = ({ selected }) => {
13
- return (React.createElement(Image, { source: selected ? iconSources.storyWhite : iconSources.storyBlue, style: styles.storyIcon }));
14
- };
15
- export const SearchIcon = () => {
16
- return (React.createElement(Image, { source: iconSources.search, style: styles.searchIcon }));
16
+ grid: {
17
+ uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAMAAABHPGVmAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAALdQTFRFAAAAIJ//Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqj9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Haf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqb9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqf9Hqj9Hqf9Hmz01QAAAD10Uk5TAAAEGjNefYqLXDUFAjSY0e79/9KZAghmyMkBQbz6vUIBBnD1cQcOo6Sp76qBePTHVOoC+RQTX4mCDZsBMmX5zDcAAAHcSURBVHic7ZrrcoIwEIVX8W5VpAiUaq33GyJqrfby/s9VacsCDojUJNNx9vzCTLLfZJdkHM4ChJTJSrl8oVj6s4qFckWqZiBed7V6Q24qV6op36uVVgxC042rAQh60LUIhPnY7rBCuOo8dZ9PGb3+gCXC1WA4CjPGE9YIV9NxiNHmwVCUWYAy4rIPV1PMmNnnxVCUoVf9LvOa+xp0f89HqCDzhbWMle3NsuPnWIt5MF7757zogfOxcqLOEGrtzVufm6U5Kz9iR3eHNgYObNWXc4svhQDs1C0GNTbHgZp/l6hnt5ECApqKQZu1471b93O1S1h6OQR2fsbqGcg28JeTtDIFBBwM28iCJON7lZSsVBAN3zFZCpRkkbgwDQQWWJQcvOK2LLYQCwPnoYDPS7aQJQYuwJ4/ZA8l/pASQQhCEIIQhCAEIQhBghB7nSj839xMnmtHQriJIAS5IYiQw3g7dxdBCEIQghCEIAQhyH+ECPmMLsQQKOMzY2vjgIHLYkwaIXaTEOMM+FuAIMbMhJYIW1aIwczaKj9EWuVCTH++7Quml8Qev0aMnl+qtxkfxnu4cYXLXiYhhpg2H4AP1g1L7a55ygDGrVdGZOuVKwFNZN/KSp/XtsPVpOpJ0C+2naE/XA0rpwAAAABJRU5ErkJggg==',
18
+ },
19
+ 'story-white': {
20
+ uri: 'data:image/png;base64,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',
21
+ },
22
+ 'story-blue': {
23
+ uri: 'data:image/png;base64,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',
24
+ },
25
+ search: {
26
+ uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAABgWlDQ1BzUkdCIElFQzYxOTY2LTIuMQAAKJF1kd8rg1EYxz/baGLy88KFsjSuTEyJG2USamnNlOFme+2H2ubtfbe03Cq3K0rc+HXBX8Ctcq0UkZJbrokb1ut5t9Uke07PeT7ne87zdM5zwBpMKim9ZgBS6YwWmPI6F0KLTvsLVlqw00VrWNHVcb/fR1X7vMdixlu3Wav6uX+tYSWqK2CpEx5TVC0jPC3sW8+oJu8ItyuJ8IrwmXCfJhcUvjP1SIlfTY6X+NtkLRiYAGuzsDP+iyO/WEloKWF5Oa5UMquU72O+xBFNz89J7BbvRCfAFF6czDDJBMMMMirzMG489MuKKvkDxfxZ1iRXkVklh8YqcRJk6BM1K9WjEmOiR2UkyZn9/9tXPTbkKVV3eKH22TDee8C+DYW8YXwdGUbhGGxPcJmu5K8dwsiH6PmK5jqApk04v6pokV242IKORzWshYuSTdwai8HbKTSGoO0G6pdKPSvvc/IAwQ35qmvY24deOd+0/AMfDGfFB6WkFAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAqNJREFUWIXF2DmIFFEQBuBvVh3FC0EFdT3QaDORTUTFI9hQPAIRQxE0ETXwQBADD7zAaBEDTQxlIxExE4VFjTzYxQNFERUTr/VCvII3Q78Ze2Z6drvZH5ppmqq//q73XnXVlGRHFzZgJeZiDibjLd7gMa7gOr62wdsWStiEAfzNeH3HhYroXNGNu02CPsN9vMOfBjZHUc5DzBb8qAtwG7uxMMW+jB704kOd3w3MGImYg3WEQ9jehn8nrtVxPMeC4YjZ4v+3S8tIFmzD54jrnnAIMqNbWPcqwRFhU48EC/A04uzLyllSu4Fv5CCmimX4HXGvzeK0Se2eGe4yNcLpiH8QY1s5xHVmR85iYEJFSDXG5mbGXZHhnQLEVLEqinO5meGByHBPgYJKeF2J80XIWirimrGoQEFwLoq1PM2gQyhkhMr8vGBBg9H9nFaC3hYshtAVVNGZZtAhqZ5DhcupjTE+zaBDkplZhcthdnQ/kGbQIUnjTDm1Ck0QL9ODRoKeVO5LQjdYJFZXfj/hVSOjjZKj2FugmGn4WYlzq5nhJMlX/oMGxzEHHJa8+MlWxhcj46sFiFkiyc57TG/lME9tL7Q1RzFlYQNXuXdmdTweOX3C/JwEHYt4BzAuq2MZNyPnx1g6AiFlYer4FXH2tEsyEy8igl84of36tFgYkepHo2F1EwvxsI7oIVZk8J2KQ5INnHbta0bQqG+egktYV/f8lTAuDwoVfkj4HHRijVD44mw+ErLShbPR8/041UxYI7HrK6RZx+jq9RG71G7gvdrIVDOMFea1PuFPhEYiPqMfZ4S9mIaWotoddSYK81unUNHHC0f5AV5WgrTCXrXLNazlyxu5LV+eSBU1ZhQF9eObpFj2CIdh1BFn6s1oZqiKfqHtWYLz/wAUO/neFu9thgAAAABJRU5ErkJggg==',
27
+ },
28
+ 'layout-bottom': {
29
+ uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAEzSURBVHgB7ZjhDYIwEIWfTsAIjOAIHYGNZANwAnQi3KBu4AhYIiYNNHCF0ivxvuR+mHjNe70eyR0gCIKQOspEZUKb6CJFa6IxkWMD2SC8Y45q0OItvgXALd6uiJeJFG7eVQkSuSNZ49sL3qVcSQF3zylKcoOp+FjCbTJMTdSUxPHbL8CHwvQyFxmXjZMMC3pOjqSO8J+YzOo54+CIAW7EADdigBsxwI0Y4EYMcLPWwNXEG+HGxf4s8si4xNI8sOe8XK7Q450Q8uZdlfDS85c9cMN+PBAAypvrtwOhm7jcoGc2gWOlYuPdA6/R7wv4GK90niDQPw/bsUY6i62GkqgwLVt/UKwFVzZo0A4dOfWQGvt969cGaa1o38Kh1+s/EylUosbGHsxN3BG3InoQriAIgpA0H3BBw2jFymiHAAAAAElFTkSuQmCC',
30
+ },
31
+ 'layout-bottom-inverse': {
32
+ uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAFgSURBVHgB7ZjhcYMwDIXlTEA2YISOYDbIRmSD0AloJ+kIyQbuBskGqnz4R+OYw7jYchN9dzouwYIn2+LgAQiCIFQNImqKE4XBcpwpRooWUqHkxgnnxmpo5nSqOfF0+KJ4gzq4UHRKqZt/YjeT0EM94i1WSx81kma/DSyjwakXGigA3eeA4Z7TMcljQHwR4Z6OJlDEEJN49pIOwIRb9bvJ9MeoQBLeDSCACbfy19//+XqqLsCypGcH/xwpgBspgBspgBspgBspgBspgJukAugFsae44nbYa51gC/wrB87ndCqOa/Ws/h6ws0WHXJ+YN7rdHlboeckeeId8fMJfWdpzbsyA2zfxMUXPYg8Q+5AjVoqUHvj2frM5dPho6VxikgZv1QzWY2yNMYk6sEcNFjK4nHCNYWuxjb3IgPURtBWf0153AzvI+8yPxWrokp+EONntH/ho+ubE4LSNNQiCIFTND53ElQfvR3pDAAAAAElFTkSuQmCC',
33
+ },
34
+ 'layout-split': {
35
+ uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADxSURBVHgB7ZjhCcIwFITP4gAdISM4QkZwI0doNqgb1Q3iJjU/Imga5YVK3yveB/ejkJa7vgTCAYQQYh2fNCTFpHkjTUljksMK+mx8VtaQvTSbnwBom3+dSFMIC3++NgkRDlA3+0m+NNtVAlxgl7NkkaW9XyqWZg+VADNs8+a5w85hAG0YQBsG0IYBtNl9gKNgzQG6fL3acAtpwwDaMIA2DKDNXwRorvW2pBbgXjyfoEfZA90gIGDZxWhMoceyER8lL3rUCyVRK/YD+uwhVnw46UcC7LVyAQ3svl5/hrAwiYCVZ9AlXbHtRGI27kEIIaZ5ALildIGPrcQbAAAAAElFTkSuQmCC',
36
+ },
37
+ 'layout-split-inverse': {
38
+ uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAEaSURBVHgB7ZntDYJADIaLcQBGYARHgAl0I0YQJ8BNdAPZ4BzBDWov8ENrkYuJtJA+SUO4D/K+1yaQAuA4jmMaRCwpjhQB5+NG0VIU8Cu0OR+EaxM15GM6szHxdLlQ7MAGHUWVZdmDT2xGNtRgR3wkaqmliY8MYF93AWwSs3B9HZAyUINdDnxAMmCpdDh7PiCVEIJhqITeNG9g4bgBbdyANm5AGzegzeINbKcW8Ff33Ex92ngJaeMGtHED2rgBbdZv4FtbzwKSgTu7V2uz0OHxPlCXsqlhzdWgkQXsm8uBaWlTNpZChzgIp/EXBuElyu38IvUhDdqjkbSus70+LKwoTqBP1CCKT4KyUVCcsf/tMxcB+zIuwXEcxzRPpfGo9y3IYogAAAAASUVORK5CYII=',
39
+ },
17
40
  };
18
- const styles = StyleSheet.create({
19
- sectionIcon: {
20
- width: 10,
21
- height: 10,
22
- marginRight: 6,
23
- },
24
- storyIcon: {
25
- width: 12,
26
- height: 12,
27
- marginRight: 6,
28
- },
29
- searchIcon: {
30
- opacity: 0.5,
31
- width: 16,
32
- height: 16,
33
- },
34
- });
41
+ const StyledImage = styled(Image)();
42
+ const StyledImageBackground = styled(ImageBackground)();
43
+ export function Icon(_a) {
44
+ var { name, background = false } = _a, props = __rest(_a, ["name", "background"]);
45
+ const IconComponent = background ? StyledImageBackground : StyledImage;
46
+ return React.createElement(IconComponent, Object.assign({ source: iconSources[name], width: 16, height: 16 }, props));
47
+ }
@@ -3,7 +3,7 @@ import { addons } from '@storybook/addons';
3
3
  import Events from '@storybook/core-events';
4
4
  import React, { useMemo, useState } from 'react';
5
5
  import { SectionList, StyleSheet } from 'react-native';
6
- import { GridIcon, SearchIcon, StoryIcon } from '../Shared/icons';
6
+ import { Icon } from '../Shared/icons';
7
7
  import { Box } from '../Shared/layout';
8
8
  import { useIsStorySelected, useIsStorySectionSelected, useTheme } from '../../../hooks';
9
9
  const SectionHeaderText = styled.Text(({ theme }) => ({
@@ -36,7 +36,7 @@ const SearchContainer = styled.View(({ theme }) => ({
36
36
  const SearchBar = (props) => {
37
37
  const theme = useTheme();
38
38
  return (React.createElement(SearchContainer, null,
39
- React.createElement(SearchIcon, null),
39
+ React.createElement(Icon, { name: "search", opacity: 0.5 }),
40
40
  React.createElement(SearchInput, Object.assign({}, props, { autoCapitalize: "none", autoComplete: "off", autoCorrect: false, spellCheck: false, clearButtonMode: "while-editing", disableFullscreenUI: true, placeholderTextColor: theme.storyList.search.placeholderTextColor, returnKeyType: "search" }))));
41
41
  };
42
42
  const HeaderContainer = styled.TouchableOpacity({
@@ -53,7 +53,7 @@ const HeaderContainer = styled.TouchableOpacity({
53
53
  const SectionHeader = React.memo(({ title, onPress }) => {
54
54
  const selected = useIsStorySectionSelected(title);
55
55
  return (React.createElement(HeaderContainer, { key: title, selected: selected, onPress: onPress, activeOpacity: 0.8 },
56
- React.createElement(GridIcon, null),
56
+ React.createElement(Icon, { name: "grid", width: 10, height: 10, marginRight: 6 }),
57
57
  React.createElement(SectionHeaderText, { selected: selected }, title)));
58
58
  });
59
59
  const ItemTouchable = styled.TouchableOpacity({
@@ -74,7 +74,7 @@ const ListItem = React.memo(({ storyId, kind, title, isLastItem, onPress }) => {
74
74
  const selected = useIsStorySelected(storyId);
75
75
  const sectionSelected = useIsStorySectionSelected(kind);
76
76
  return (React.createElement(ItemTouchable, { key: title, onPress: onPress, activeOpacity: 0.8, testID: `Storybook.ListItem.${kind}.${title}`, accessibilityLabel: `Storybook.ListItem.${title}`, selected: selected, sectionSelected: sectionSelected, isLastItem: isLastItem },
77
- React.createElement(StoryIcon, { selected: selected }),
77
+ React.createElement(Icon, { name: selected ? 'story-white' : 'story-blue', marginRight: 6 }),
78
78
  React.createElement(StoryNameText, { selected: selected }, title)));
79
79
  }, (prevProps, nextProps) => prevProps.storyId === nextProps.storyId);
80
80
  const getStories = (storyIndex) => {
@@ -1,24 +1,30 @@
1
1
  import React from 'react';
2
- import { Text, View, StyleSheet } from 'react-native';
2
+ import { Text, Keyboard } from 'react-native';
3
3
  import { useStoryContext } from '../../../hooks';
4
- const styles = StyleSheet.create({
5
- container: { flex: 1 },
6
- helpContainer: {
7
- flex: 1,
8
- paddingHorizontal: 16,
9
- paddingVertical: 16,
10
- alignItems: 'center',
11
- justifyContent: 'center',
12
- },
13
- });
4
+ import { Box } from '../Shared/layout';
5
+ /**
6
+ * This is a handler for `onStartShouldSetResponder`, which dismisses the
7
+ * keyboard as a side effect but then responds with `false` to the responder
8
+ * system, so as not to start actually handling the touch.
9
+ *
10
+ * The objective here is to dismiss the keyboard when the story view is tapped,
11
+ * but in a way that won't interfere with presses or swipes. Using a
12
+ * `Touchable...` component as a wrapper will start to handle the touch, which
13
+ * will swallow swipe gestures that should have gone on to a child view, such
14
+ * as `ScrollView`.
15
+ */
16
+ function dismissOnStartResponder() {
17
+ Keyboard.dismiss();
18
+ return false;
19
+ }
14
20
  const StoryView = () => {
15
21
  const context = useStoryContext();
16
22
  const id = context === null || context === void 0 ? void 0 : context.id;
17
23
  if (context && context.unboundStoryFn) {
18
24
  const { unboundStoryFn: StoryComponent } = context;
19
- return (React.createElement(View, { key: id, testID: id, style: styles.container }, StoryComponent && React.createElement(StoryComponent, Object.assign({}, context))));
25
+ return (React.createElement(Box, { flex: true, key: id, testID: id, onStartShouldSetResponder: dismissOnStartResponder }, StoryComponent && React.createElement(StoryComponent, Object.assign({}, context))));
20
26
  }
21
- return (React.createElement(View, { style: styles.helpContainer },
27
+ return (React.createElement(Box, { flex: true, padding: 16, alignItems: "center", justifyContent: "center" },
22
28
  React.createElement(Text, null, "Please open navigator and select a story to preview.")));
23
29
  };
24
30
  export default React.memo(StoryView);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storybook/react-native",
3
- "version": "6.5.0-rc.3",
3
+ "version": "6.5.0-rc.5",
4
4
  "description": "A better way to develop React Native Components for your app",
5
5
  "keywords": [
6
6
  "react",
@@ -87,5 +87,5 @@
87
87
  "publishConfig": {
88
88
  "access": "public"
89
89
  },
90
- "gitHead": "f2a1a9100ba5ddf37fa59b58518751a525eb4788"
90
+ "gitHead": "5bc5a1f17cbca33fba28f8cce57d7d49ee80c689"
91
91
  }
package/readme.md CHANGED
@@ -227,6 +227,8 @@ You can pass these parameters to getStorybookUI call in your storybook entry poi
227
227
  -- additional query string to pass to websockets
228
228
  isUIHidden: Boolean (false)
229
229
  -- should the ui be closed initially.
230
+ isSplitPanelVisible: Boolean (false)
231
+ -- should the split panel (addons on the bottom) be visible initially.
230
232
  tabOpen: Number (0)
231
233
  -- which tab should be open. -1 Navigator, 0 Preview, 1 Addons
232
234
  initialSelection: Object (null)
@@ -1,8 +0,0 @@
1
- import React from 'react';
2
- import { StyleProp, ViewStyle } from 'react-native';
3
- interface Props {
4
- onPress: () => void;
5
- style?: StyleProp<ViewStyle>;
6
- }
7
- declare const _default: React.MemoExoticComponent<({ onPress, style }: Props) => JSX.Element>;
8
- export default _default;
@@ -1,45 +0,0 @@
1
- import React from 'react';
2
- import styled from '@emotion/native';
3
- import { View } from 'react-native';
4
- const Touchable = styled.TouchableOpacity(({ theme }) => ({
5
- backgroundColor: 'transparent',
6
- position: 'absolute',
7
- right: theme.tokens.spacing2,
8
- bottom: theme.tokens.spacing4,
9
- zIndex: 100,
10
- }));
11
- const HIDE_ICON_SIZE = 14;
12
- const HIDE_ICON_BORDER_WIDTH = 1;
13
- const Inner = styled.View(({ theme }) => ({
14
- position: 'absolute',
15
- top: 0,
16
- left: 0,
17
- width: HIDE_ICON_SIZE,
18
- height: HIDE_ICON_SIZE,
19
- borderRadius: theme.navigation.visibilityBorderRadius,
20
- overflow: 'hidden',
21
- borderColor: theme.navigation.visibilityInnerBorderColor,
22
- borderWidth: HIDE_ICON_BORDER_WIDTH * 2,
23
- }));
24
- const Outer = styled.View(({ theme }) => ({
25
- position: 'absolute',
26
- top: 0,
27
- left: 0,
28
- width: HIDE_ICON_SIZE,
29
- height: HIDE_ICON_SIZE,
30
- borderRadius: theme.navigation.visibilityBorderRadius,
31
- overflow: 'hidden',
32
- borderColor: theme.navigation.visibilityOuterBorderColor,
33
- borderWidth: HIDE_ICON_BORDER_WIDTH,
34
- }));
35
- const hideIconStyles = {
36
- width: HIDE_ICON_SIZE,
37
- height: HIDE_ICON_SIZE,
38
- marginRight: 4,
39
- };
40
- const HideIcon = () => (React.createElement(View, { style: hideIconStyles },
41
- React.createElement(Inner, null),
42
- React.createElement(Outer, null)));
43
- const VisibilityButton = ({ onPress, style }) => (React.createElement(Touchable, { onPress: onPress, style: style, testID: "Storybook.OnDeviceUI.toggleUI", accessibilityLabel: "Storybook.OnDeviceUI.toggleUI", hitSlop: { top: 5, left: 5, bottom: 5, right: 5 } },
44
- React.createElement(HideIcon, null)));
45
- export default React.memo(VisibilityButton);