@storybook/react-native 6.5.0-rc.1 → 6.5.0-rc.3
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/dist/hooks.d.ts +5 -0
- package/dist/hooks.js +10 -3
- package/dist/index.d.ts +1 -1
- package/dist/preview/View.d.ts +9 -7
- package/dist/preview/View.js +3 -1
- package/dist/preview/components/OnDeviceUI/OnDeviceUI.js +61 -40
- package/dist/preview/components/OnDeviceUI/Panel.d.ts +7 -5
- package/dist/preview/components/OnDeviceUI/Panel.js +9 -4
- package/dist/preview/components/OnDeviceUI/absolute-positioned-keyboard-aware-view.d.ts +1 -1
- package/dist/preview/components/OnDeviceUI/addons/Addons.d.ts +3 -2
- package/dist/preview/components/OnDeviceUI/addons/Addons.js +7 -22
- package/dist/preview/components/OnDeviceUI/addons/List.js +6 -15
- package/dist/preview/components/OnDeviceUI/addons/Wrapper.js +10 -14
- package/dist/preview/components/OnDeviceUI/animation.d.ts +34 -11
- package/dist/preview/components/OnDeviceUI/animation.js +49 -18
- package/dist/preview/components/OnDeviceUI/navigation/Navigation.d.ts +3 -1
- package/dist/preview/components/OnDeviceUI/navigation/Navigation.js +5 -5
- package/dist/preview/components/OnDeviceUI/navigation/NavigationBar.d.ts +8 -0
- package/dist/preview/components/OnDeviceUI/navigation/NavigationBar.js +14 -0
- package/dist/preview/components/OnDeviceUI/navigation/VisibilityButton.js +10 -10
- package/dist/preview/components/Shared/layout.d.ts +12 -0
- package/dist/preview/components/Shared/layout.js +27 -0
- package/dist/preview/components/Shared/tabs.d.ts +20 -0
- package/dist/preview/components/Shared/tabs.js +35 -0
- package/dist/preview/components/Shared/theme.d.ts +183 -13
- package/dist/preview/components/Shared/theme.js +177 -13
- package/dist/preview/components/StoryListView/StoryListView.js +57 -65
- package/dist/types/types-6.0.d.ts +10 -10
- package/dist/types/types.d.ts +3 -3
- package/package.json +6 -6
- package/scripts/__snapshots__/loader.test.js.snap +35 -35
- package/dist/preview/components/OnDeviceUI/navigation/Bar.d.ts +0 -9
- package/dist/preview/components/OnDeviceUI/navigation/Bar.js +0 -17
- package/dist/preview/components/OnDeviceUI/navigation/Button.d.ts +0 -10
- package/dist/preview/components/OnDeviceUI/navigation/Button.js +0 -17
- package/dist/preview/components/Shared/text.d.ts +0 -3
- package/dist/preview/components/Shared/text.js +0 -18
package/dist/hooks.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { StoryContext } from '@storybook/csf';
|
|
2
|
+
import type { Theme } from './preview/components/Shared/theme';
|
|
2
3
|
import type { ReactNativeFramework } from './types/types-6.0';
|
|
3
4
|
/**
|
|
4
5
|
* Hook that returns a function to set the current story context.
|
|
@@ -20,3 +21,7 @@ export declare function useIsStorySelected(storyId: string): boolean;
|
|
|
20
21
|
* Hook that indicates if `title` is the currently selected story section.
|
|
21
22
|
*/
|
|
22
23
|
export declare function useIsStorySectionSelected(title: string): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Hook that gets the current theme values.
|
|
26
|
+
*/
|
|
27
|
+
export declare function useTheme(): Theme;
|
package/dist/hooks.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import { atom, useAtomValue, useSetAtom } from 'jotai';
|
|
3
|
+
import { useTheme as useEmotionTheme } from 'emotion-theming';
|
|
3
4
|
const storyContextAtom = atom(null);
|
|
4
5
|
/**
|
|
5
6
|
* Hook that returns a function to set the current story context.
|
|
@@ -18,18 +19,24 @@ export function useStoryContext() {
|
|
|
18
19
|
*/
|
|
19
20
|
export function useStoryContextParam(name, defaultValue) {
|
|
20
21
|
var _a;
|
|
21
|
-
const paramAtom = useMemo(() => atom(get => { var _a, _b; return (_b = (_a = get(storyContextAtom)) === null || _a === void 0 ? void 0 : _a.parameters) === null || _b === void 0 ? void 0 : _b[name]; }), [name]);
|
|
22
|
+
const paramAtom = useMemo(() => atom((get) => { var _a, _b; return (_b = (_a = get(storyContextAtom)) === null || _a === void 0 ? void 0 : _a.parameters) === null || _b === void 0 ? void 0 : _b[name]; }), [name]);
|
|
22
23
|
return (_a = useAtomValue(paramAtom)) !== null && _a !== void 0 ? _a : defaultValue;
|
|
23
24
|
}
|
|
24
25
|
/**
|
|
25
26
|
* Hook that indicates if `storyId` is the currently selected story.
|
|
26
27
|
*/
|
|
27
28
|
export function useIsStorySelected(storyId) {
|
|
28
|
-
return useAtomValue(useMemo(() => atom(get => { var _a; return ((_a = get(storyContextAtom)) === null || _a === void 0 ? void 0 : _a.id) === storyId; }), [storyId]));
|
|
29
|
+
return useAtomValue(useMemo(() => atom((get) => { var _a; return ((_a = get(storyContextAtom)) === null || _a === void 0 ? void 0 : _a.id) === storyId; }), [storyId]));
|
|
29
30
|
}
|
|
30
31
|
/**
|
|
31
32
|
* Hook that indicates if `title` is the currently selected story section.
|
|
32
33
|
*/
|
|
33
34
|
export function useIsStorySectionSelected(title) {
|
|
34
|
-
return useAtomValue(useMemo(() => atom(get => { var _a; return ((_a = get(storyContextAtom)) === null || _a === void 0 ? void 0 : _a.title) === title; }), [title]));
|
|
35
|
+
return useAtomValue(useMemo(() => atom((get) => { var _a; return ((_a = get(storyContextAtom)) === null || _a === void 0 ? void 0 : _a.title) === title; }), [title]));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Hook that gets the current theme values.
|
|
39
|
+
*/
|
|
40
|
+
export function useTheme() {
|
|
41
|
+
return useEmotionTheme();
|
|
35
42
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { ReactNode } from 'react';
|
|
|
6
6
|
import type { ReactNativeFramework } from './types/types-6.0';
|
|
7
7
|
declare const configure: (loadable: import("@storybook/core-client").Loadable, m: NodeModule) => void;
|
|
8
8
|
export { configure };
|
|
9
|
-
|
|
9
|
+
type C = ClientApi<ReactNativeFramework>;
|
|
10
10
|
export declare const setAddon: C['setAddon'];
|
|
11
11
|
export declare const addDecorator: C['addDecorator'];
|
|
12
12
|
export declare const addParameters: C['addParameters'];
|
package/dist/preview/View.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { StoryIndex, SelectionSpecifier } from '@storybook/store';
|
|
3
3
|
import { StoryContext } from '@storybook/csf';
|
|
4
|
-
import {
|
|
4
|
+
import { Theme } from './components/Shared/theme';
|
|
5
5
|
import type { ReactNativeFramework } from '../types/types-6.0';
|
|
6
6
|
import { PreviewWeb } from '@storybook/preview-web';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
type StoryKind = string;
|
|
8
|
+
type StoryName = string;
|
|
9
|
+
type InitialSelection = `${StoryKind}--${StoryName}` | {
|
|
10
10
|
/**
|
|
11
11
|
* Kind is the default export name or the storiesOf("name") name
|
|
12
12
|
*/
|
|
@@ -16,7 +16,10 @@ declare type InitialSelection = `${StoryKind}--${StoryName}` | {
|
|
|
16
16
|
*/
|
|
17
17
|
name: StoryName;
|
|
18
18
|
};
|
|
19
|
-
|
|
19
|
+
type DeepPartial<T> = T extends object ? {
|
|
20
|
+
[P in keyof T]?: DeepPartial<T[P]>;
|
|
21
|
+
} : T;
|
|
22
|
+
export type Params = {
|
|
20
23
|
onDeviceUI?: boolean;
|
|
21
24
|
enableWebsockets?: boolean;
|
|
22
25
|
query?: string;
|
|
@@ -29,8 +32,7 @@ export declare type Params = {
|
|
|
29
32
|
isUIHidden?: boolean;
|
|
30
33
|
shouldDisableKeyboardAvoidingView?: boolean;
|
|
31
34
|
keyboardAvoidingViewVerticalOffset?: number;
|
|
32
|
-
|
|
33
|
-
theme?: typeof theme;
|
|
35
|
+
theme: DeepPartial<Theme>;
|
|
34
36
|
};
|
|
35
37
|
export declare class View {
|
|
36
38
|
_storyIndex: StoryIndex;
|
package/dist/preview/View.js
CHANGED
|
@@ -20,6 +20,7 @@ import StoryView from './components/StoryView';
|
|
|
20
20
|
import createChannel from '@storybook/channel-websocket';
|
|
21
21
|
import getHost from './rn-host-detect';
|
|
22
22
|
import events from '@storybook/core-events';
|
|
23
|
+
import deepmerge from 'deepmerge';
|
|
23
24
|
const STORAGE_KEY = 'lastOpenedStory';
|
|
24
25
|
export class View {
|
|
25
26
|
constructor(preview) {
|
|
@@ -65,6 +66,7 @@ export class View {
|
|
|
65
66
|
});
|
|
66
67
|
};
|
|
67
68
|
this.getStorybookUI = (params = {}) => {
|
|
69
|
+
var _a;
|
|
68
70
|
const { shouldPersistSelection = true, onDeviceUI = true, enableWebsockets = false } = params;
|
|
69
71
|
const initialStory = this._getInitialStory(params);
|
|
70
72
|
if (enableWebsockets) {
|
|
@@ -87,7 +89,7 @@ export class View {
|
|
|
87
89
|
});
|
|
88
90
|
// eslint-disable-next-line consistent-this
|
|
89
91
|
const self = this;
|
|
90
|
-
const appliedTheme =
|
|
92
|
+
const appliedTheme = deepmerge(theme, (_a = params.theme) !== null && _a !== void 0 ? _a : {});
|
|
91
93
|
return () => {
|
|
92
94
|
const setContext = useSetStoryContext();
|
|
93
95
|
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import styled from '@emotion/native';
|
|
2
2
|
import React, { useState, useRef } from 'react';
|
|
3
|
-
import { Animated, Dimensions, Keyboard, KeyboardAvoidingView, Platform,
|
|
4
|
-
import { useStoryContextParam } from '../../../hooks';
|
|
3
|
+
import { Animated, Dimensions, Easing, Keyboard, KeyboardAvoidingView, Platform, TouchableOpacity, StatusBar, StyleSheet, View, } from 'react-native';
|
|
4
|
+
import { useStoryContextParam, useTheme } from '../../../hooks';
|
|
5
5
|
import StoryListView from '../StoryListView';
|
|
6
6
|
import StoryView from '../StoryView';
|
|
7
7
|
import AbsolutePositionedKeyboardAwareView from './absolute-positioned-keyboard-aware-view';
|
|
8
8
|
import Addons from './addons/Addons';
|
|
9
|
-
import { getAddonPanelPosition, getNavigatorPanelPosition,
|
|
9
|
+
import { getAddonPanelPosition, getNavigatorPanelPosition, getPreviewShadowStyle, getPreviewStyle, } from './animation';
|
|
10
10
|
import Navigation from './navigation';
|
|
11
11
|
import { PREVIEW, ADDONS } from './navigation/constants';
|
|
12
12
|
import Panel from './Panel';
|
|
13
13
|
import { useWindowDimensions } from 'react-native';
|
|
14
14
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
15
|
-
const ANIMATION_DURATION =
|
|
15
|
+
const ANIMATION_DURATION = 400;
|
|
16
16
|
const IS_IOS = Platform.OS === 'ios';
|
|
17
17
|
// @ts-ignore: Property 'Expo' does not exist on type 'Global'
|
|
18
18
|
const getExpoRoot = () => global.Expo || global.__expo || global.__exponent;
|
|
@@ -20,28 +20,22 @@ export const IS_EXPO = getExpoRoot() !== undefined;
|
|
|
20
20
|
const IS_ANDROID = Platform.OS === 'android';
|
|
21
21
|
const BREAKPOINT = 1024;
|
|
22
22
|
const flex = { flex: 1 };
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
const absolutePosition = {
|
|
33
|
-
position: 'absolute',
|
|
34
|
-
top: 0,
|
|
35
|
-
bottom: 0,
|
|
36
|
-
left: 0,
|
|
37
|
-
right: 0,
|
|
38
|
-
};
|
|
23
|
+
/**
|
|
24
|
+
* Story preview container.
|
|
25
|
+
*/
|
|
26
|
+
function Preview({ animatedValue, style, children }) {
|
|
27
|
+
const theme = useTheme();
|
|
28
|
+
const containerStyle = Object.assign({ backgroundColor: theme.preview.backgroundColor }, getPreviewShadowStyle(animatedValue));
|
|
29
|
+
return (React.createElement(Animated.View, { style: [flex, containerStyle] },
|
|
30
|
+
React.createElement(View, { style: [flex, style] }, children)));
|
|
31
|
+
}
|
|
39
32
|
const styles = StyleSheet.create({
|
|
40
33
|
expoAndroidContainer: { paddingTop: StatusBar.currentHeight },
|
|
41
34
|
});
|
|
35
|
+
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' } }))));
|
|
42
36
|
const OnDeviceUI = ({ storyIndex, isUIHidden, shouldDisableKeyboardAvoidingView, keyboardAvoidingViewVerticalOffset, tabOpen: initialTabOpen, }) => {
|
|
43
37
|
const [tabOpen, setTabOpen] = useState(initialTabOpen || PREVIEW);
|
|
44
|
-
const
|
|
38
|
+
const lastTabOpen = React.useRef(tabOpen);
|
|
45
39
|
const [previewDimensions, setPreviewDimensions] = useState(() => ({
|
|
46
40
|
width: Dimensions.get('window').width,
|
|
47
41
|
height: Dimensions.get('window').height,
|
|
@@ -54,14 +48,14 @@ const OnDeviceUI = ({ storyIndex, isUIHidden, shouldDisableKeyboardAvoidingView,
|
|
|
54
48
|
if (newTabOpen === tabOpen) {
|
|
55
49
|
return;
|
|
56
50
|
}
|
|
51
|
+
lastTabOpen.current = tabOpen;
|
|
57
52
|
Animated.timing(animatedValue.current, {
|
|
58
53
|
toValue: newTabOpen,
|
|
59
54
|
duration: ANIMATION_DURATION,
|
|
55
|
+
easing: Easing.inOut(Easing.cubic),
|
|
60
56
|
useNativeDriver: true,
|
|
61
57
|
}).start();
|
|
62
58
|
setTabOpen(newTabOpen);
|
|
63
|
-
const isSwipingBetweenNavigatorAndAddons = tabOpen + newTabOpen === PREVIEW;
|
|
64
|
-
setSlideBetweenAnimation(isSwipingBetweenNavigatorAndAddons);
|
|
65
59
|
// close the keyboard opened from a TextInput from story list or knobs
|
|
66
60
|
if (newTabOpen === PREVIEW) {
|
|
67
61
|
Keyboard.dismiss();
|
|
@@ -70,35 +64,62 @@ const OnDeviceUI = ({ storyIndex, isUIHidden, shouldDisableKeyboardAvoidingView,
|
|
|
70
64
|
const noSafeArea = useStoryContextParam('noSafeArea', false);
|
|
71
65
|
const previewWrapperStyles = [
|
|
72
66
|
flex,
|
|
73
|
-
|
|
67
|
+
getPreviewStyle({
|
|
74
68
|
animatedValue: animatedValue.current,
|
|
75
69
|
previewDimensions,
|
|
76
|
-
slideBetweenAnimation,
|
|
77
70
|
wide,
|
|
78
|
-
noSafeArea,
|
|
79
71
|
insets,
|
|
72
|
+
tabOpen,
|
|
73
|
+
lastTabOpen: lastTabOpen.current,
|
|
80
74
|
}),
|
|
81
75
|
];
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
const
|
|
76
|
+
// The initial value is just a guess until the layout calculation has been done.
|
|
77
|
+
const [navBarHeight, setNavBarHeight] = React.useState(insets.bottom + 40);
|
|
78
|
+
const measureNavigation = React.useCallback(({ nativeEvent }) => {
|
|
79
|
+
const inset = insets.bottom;
|
|
80
|
+
setNavBarHeight(isUIVisible ? nativeEvent.layout.height - inset : 0);
|
|
81
|
+
}, [isUIVisible, insets]);
|
|
82
|
+
// There are 4 cases for the additional UI margin:
|
|
83
|
+
// 1. Storybook UI is visible, and `noSafeArea` is false: Include top and
|
|
84
|
+
// bottom safe area insets, and also include the navigation bar height.
|
|
85
|
+
//
|
|
86
|
+
// 2. Storybook UI is not visible, and `noSafeArea` is false: Include top
|
|
87
|
+
// and bottom safe area insets.
|
|
88
|
+
//
|
|
89
|
+
// 3. Storybook UI is visible, and `noSafeArea` is true: Include only the
|
|
90
|
+
// bottom safe area inset and the navigation bar height.
|
|
91
|
+
//
|
|
92
|
+
// 4. Storybook UI is not visible, and `noSafeArea` is true: No margin.
|
|
93
|
+
const safeAreaMargins = {
|
|
94
|
+
paddingBottom: isUIVisible ? insets.bottom + navBarHeight : noSafeArea ? 0 : insets.bottom,
|
|
95
|
+
paddingTop: !noSafeArea ? insets.top : 0,
|
|
96
|
+
};
|
|
97
|
+
// The panels always apply the safe area, regardless of the story parameters.
|
|
98
|
+
const panelSafeAreaMargins = {
|
|
99
|
+
paddingBottom: insets.bottom + navBarHeight,
|
|
100
|
+
paddingTop: insets.top,
|
|
101
|
+
};
|
|
102
|
+
// Adjust the keyboard offset (possibly in a negative direction) to account
|
|
103
|
+
// for the safe area and navigation bar.
|
|
104
|
+
const keyboardVerticalOffset = -panelSafeAreaMargins.paddingBottom + (keyboardAvoidingViewVerticalOffset !== null && keyboardAvoidingViewVerticalOffset !== void 0 ? keyboardAvoidingViewVerticalOffset : 0);
|
|
85
105
|
return (React.createElement(React.Fragment, null,
|
|
86
|
-
React.createElement(
|
|
87
|
-
React.createElement(KeyboardAvoidingView, { enabled: !shouldDisableKeyboardAvoidingView || tabOpen !== PREVIEW, behavior: IS_IOS ? 'padding' : null, keyboardVerticalOffset:
|
|
106
|
+
React.createElement(Container, null,
|
|
107
|
+
React.createElement(KeyboardAvoidingView, { enabled: !shouldDisableKeyboardAvoidingView || tabOpen !== PREVIEW, behavior: IS_IOS ? 'padding' : null, keyboardVerticalOffset: keyboardVerticalOffset, style: flex },
|
|
88
108
|
React.createElement(AbsolutePositionedKeyboardAwareView, { onLayout: setPreviewDimensions, previewDimensions: previewDimensions },
|
|
89
109
|
React.createElement(Animated.View, { style: previewWrapperStyles },
|
|
90
|
-
React.createElement(
|
|
91
|
-
React.createElement(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
110
|
+
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),
|
|
113
|
+
React.createElement(Panel, { edge: "right", style: [
|
|
114
|
+
getNavigatorPanelPosition(animatedValue.current, previewDimensions.width, wide),
|
|
115
|
+
panelSafeAreaMargins,
|
|
116
|
+
] },
|
|
96
117
|
React.createElement(StoryListView, { storyIndex: storyIndex })),
|
|
97
|
-
React.createElement(Panel, { style: [
|
|
118
|
+
React.createElement(Panel, { edge: "left", style: [
|
|
98
119
|
getAddonPanelPosition(animatedValue.current, previewDimensions.width, wide),
|
|
99
|
-
|
|
120
|
+
panelSafeAreaMargins,
|
|
100
121
|
] },
|
|
101
122
|
React.createElement(Addons, { active: tabOpen === ADDONS })))),
|
|
102
|
-
React.createElement(Navigation, { tabOpen: tabOpen, onChangeTab: handleToggleTab, isUIVisible: isUIVisible, setIsUIVisible: setIsUIVisible }))));
|
|
123
|
+
React.createElement(Navigation, { onLayout: measureNavigation, tabOpen: tabOpen, onChangeTab: handleToggleTab, isUIVisible: isUIVisible, setIsUIVisible: setIsUIVisible }))));
|
|
103
124
|
};
|
|
104
125
|
export default React.memo(OnDeviceUI);
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import React
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Animated, StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
interface PanelProps {
|
|
4
|
+
edge: 'left' | 'right';
|
|
5
|
+
style: StyleProp<Animated.AnimatedProps<ViewStyle>>;
|
|
6
|
+
children: React.ReactNode;
|
|
5
7
|
}
|
|
6
|
-
declare const _default: React.MemoExoticComponent<({ children, style }:
|
|
8
|
+
declare const _default: React.MemoExoticComponent<({ edge, children, style }: PanelProps) => JSX.Element>;
|
|
7
9
|
export default _default;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { StyleSheet, Animated } from 'react-native';
|
|
3
3
|
import styled from '@emotion/native';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const Container = styled(Animated.View)(({ theme, edge }) => ({
|
|
5
|
+
backgroundColor: theme.panel.backgroundColor,
|
|
6
|
+
borderStartWidth: edge === 'left' ? theme.panel.borderWidth : undefined,
|
|
7
|
+
borderEndWidth: edge === 'right' ? theme.panel.borderWidth : undefined,
|
|
8
|
+
borderColor: theme.panel.borderColor,
|
|
7
9
|
}));
|
|
8
|
-
const Panel = ({ children, style }) =>
|
|
10
|
+
const Panel = ({ edge, children, style }) => {
|
|
11
|
+
const containerStyle = StyleSheet.flatten([StyleSheet.absoluteFillObject, style]);
|
|
12
|
+
return (React.createElement(Container, { edge: edge, style: containerStyle }, children));
|
|
13
|
+
};
|
|
9
14
|
export default React.memo(Panel);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
2
|
+
interface AddonsProps {
|
|
3
3
|
active: boolean;
|
|
4
|
-
}
|
|
4
|
+
}
|
|
5
|
+
declare const _default: React.MemoExoticComponent<({ active }: AddonsProps) => JSX.Element>;
|
|
5
6
|
export default _default;
|
|
@@ -1,33 +1,18 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import styled from '@emotion/native';
|
|
2
|
+
import { Text } from 'react-native';
|
|
4
3
|
import { addons } from '@storybook/addons';
|
|
5
4
|
import AddonsList from './List';
|
|
6
5
|
import AddonWrapper from './Wrapper';
|
|
7
|
-
import {
|
|
8
|
-
const NoAddonContainer = styled.View({
|
|
9
|
-
flex: 1,
|
|
10
|
-
alignItems: 'center',
|
|
11
|
-
justifyContent: 'center',
|
|
12
|
-
});
|
|
13
|
-
const Container = styled.View(({ theme }) => ({
|
|
14
|
-
flex: 1,
|
|
15
|
-
backgroundColor: theme.backgroundColor,
|
|
16
|
-
}));
|
|
6
|
+
import { Box } from '../../Shared/layout';
|
|
17
7
|
const Addons = ({ active }) => {
|
|
18
8
|
const panels = addons.getElements('panel');
|
|
19
9
|
const [addonSelected, setAddonSelected] = useState(Object.keys(panels)[0] || null);
|
|
20
10
|
if (Object.keys(panels).length === 0) {
|
|
21
|
-
return (React.createElement(
|
|
22
|
-
React.createElement(
|
|
23
|
-
React.createElement(Label, null, "No addons loaded."))));
|
|
11
|
+
return (React.createElement(Box, { alignItems: "center", justifyContent: "center" },
|
|
12
|
+
React.createElement(Text, null, "No addons loaded.")));
|
|
24
13
|
}
|
|
25
|
-
return (React.createElement(
|
|
26
|
-
React.createElement(
|
|
27
|
-
|
|
28
|
-
React.createElement(AddonWrapper, { addonSelected: active ? addonSelected : null, panels: panels }))));
|
|
14
|
+
return (React.createElement(Box, { flex: true },
|
|
15
|
+
React.createElement(AddonsList, { onPressAddon: setAddonSelected, panels: panels, addonSelected: active ? addonSelected : null }),
|
|
16
|
+
React.createElement(AddonWrapper, { addonSelected: active ? addonSelected : null, panels: panels })));
|
|
29
17
|
};
|
|
30
|
-
const styles = StyleSheet.create({
|
|
31
|
-
container: { flex: 1 },
|
|
32
|
-
});
|
|
33
18
|
export default React.memo(Addons);
|
|
@@ -1,20 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import styled from '@emotion/native';
|
|
4
|
-
import Button from '../navigation/Button';
|
|
5
|
-
const Container = styled.View(({ theme }) => ({
|
|
6
|
-
flexDirection: 'row',
|
|
7
|
-
borderBottomWidth: 1,
|
|
8
|
-
borderBottomColor: theme.borderColor || '#e6e6e6',
|
|
9
|
-
}));
|
|
2
|
+
import { TabBar, TabButton } from '../../Shared/tabs';
|
|
10
3
|
const AddonList = ({ panels, addonSelected, onPressAddon }) => {
|
|
11
4
|
const addonKeys = Object.keys(panels);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return renderTab(id, resolvedTitle);
|
|
18
|
-
}))));
|
|
5
|
+
return (React.createElement(TabBar, { scrollable: true }, addonKeys.map((id) => {
|
|
6
|
+
const { title } = panels[id];
|
|
7
|
+
const resolvedTitle = typeof title === 'function' ? title() : title;
|
|
8
|
+
return (React.createElement(TabButton, { active: id === addonSelected, key: id, id: id, onPress: () => onPressAddon(id) }, resolvedTitle.toUpperCase()));
|
|
9
|
+
})));
|
|
19
10
|
};
|
|
20
11
|
export default React.memo(AddonList);
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
},
|
|
10
|
-
flex: {
|
|
11
|
-
flex: 1,
|
|
12
|
-
},
|
|
13
|
-
});
|
|
2
|
+
import { ScrollView } from 'react-native';
|
|
3
|
+
import styled from '@emotion/native';
|
|
4
|
+
import { useTheme } from '../../../../hooks';
|
|
5
|
+
const Container = styled.View(({ selected }) => ({
|
|
6
|
+
display: selected ? 'flex' : 'none',
|
|
7
|
+
flex: 1,
|
|
8
|
+
}));
|
|
14
9
|
const Wrapper = ({ panels, addonSelected }) => {
|
|
10
|
+
const theme = useTheme();
|
|
15
11
|
const addonKeys = Object.keys(panels);
|
|
16
12
|
const content = addonKeys.map((id) => {
|
|
17
13
|
const selected = addonSelected === id;
|
|
18
|
-
return (React.createElement(
|
|
19
|
-
React.createElement(ScrollView,
|
|
14
|
+
return (React.createElement(Container, { key: id, selected: selected },
|
|
15
|
+
React.createElement(ScrollView, { contentContainerStyle: { padding: theme.panel.paddingHorizontal } }, panels[id].render({ active: selected, key: id }))));
|
|
20
16
|
});
|
|
21
17
|
return React.createElement(React.Fragment, null, content);
|
|
22
18
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { Animated } from 'react-native';
|
|
2
|
-
import { EdgeInsets } from 'react-native-safe-area-context';
|
|
1
|
+
import { Animated, Insets } from 'react-native';
|
|
3
2
|
import { PreviewDimens } from './absolute-positioned-keyboard-aware-view';
|
|
4
3
|
export declare const getNavigatorPanelPosition: (animatedValue: Animated.Value, previewWidth: number, wide: boolean) => {
|
|
5
4
|
transform: {
|
|
@@ -13,26 +12,50 @@ export declare const getAddonPanelPosition: (animatedValue: Animated.Value, prev
|
|
|
13
12
|
}[];
|
|
14
13
|
width: number;
|
|
15
14
|
}[];
|
|
16
|
-
|
|
15
|
+
type PreviewPositionArgs = {
|
|
17
16
|
animatedValue: Animated.Value;
|
|
18
17
|
previewDimensions: PreviewDimens;
|
|
19
|
-
slideBetweenAnimation: boolean;
|
|
20
18
|
wide: boolean;
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
insets: Insets;
|
|
20
|
+
tabOpen: number;
|
|
21
|
+
lastTabOpen: number;
|
|
23
22
|
};
|
|
24
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Build the animated style for the preview container view.
|
|
25
|
+
*
|
|
26
|
+
* When the navigator or addons panel is focused, the preview container is
|
|
27
|
+
* scaled down and translated to the left (or right) of the panel.
|
|
28
|
+
*/
|
|
29
|
+
export declare const getPreviewStyle: ({ animatedValue, previewDimensions: { width: previewWidth, height: previewHeight }, wide, insets, tabOpen, lastTabOpen, }: PreviewPositionArgs) => {
|
|
25
30
|
transform: ({
|
|
26
31
|
translateX: Animated.AnimatedInterpolation<string | number>;
|
|
27
32
|
translateY?: undefined;
|
|
33
|
+
scale?: undefined;
|
|
28
34
|
} | {
|
|
29
35
|
translateY: Animated.AnimatedInterpolation<string | number>;
|
|
30
36
|
translateX?: undefined;
|
|
37
|
+
scale?: undefined;
|
|
38
|
+
} | {
|
|
39
|
+
scale: Animated.AnimatedInterpolation<string | number>;
|
|
40
|
+
translateX?: undefined;
|
|
41
|
+
translateY?: undefined;
|
|
31
42
|
})[];
|
|
32
43
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Build the animated shadow style for the preview.
|
|
46
|
+
*
|
|
47
|
+
* When the navigator or addons panel are visible the scaled preview will have
|
|
48
|
+
* a shadow, and when going to the preview tab the shadow will be invisible.
|
|
49
|
+
*/
|
|
50
|
+
export declare const getPreviewShadowStyle: (animatedValue: Animated.Value) => {
|
|
51
|
+
elevation: number;
|
|
52
|
+
shadowColor: string;
|
|
53
|
+
shadowOpacity: Animated.AnimatedInterpolation<string | number>;
|
|
54
|
+
shadowRadius: number;
|
|
55
|
+
shadowOffset: {
|
|
56
|
+
width: number;
|
|
57
|
+
height: number;
|
|
58
|
+
};
|
|
59
|
+
overflow: "visible";
|
|
37
60
|
};
|
|
38
61
|
export {};
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { I18nManager } from 'react-native';
|
|
2
2
|
import { NAVIGATOR, PREVIEW, ADDONS } from './navigation/constants';
|
|
3
|
+
// Factor that will flip the animation orientation in RTL locales.
|
|
3
4
|
const RTL_SCALE = I18nManager.isRTL ? -1 : 1;
|
|
5
|
+
// Percentage to scale the preview area by when opening a panel.
|
|
4
6
|
const PREVIEW_SCALE = 0.3;
|
|
5
|
-
|
|
7
|
+
// Percentage to scale the preview area by when opening a panel, on wide screens.
|
|
8
|
+
const PREVIEW_SCALE_WIDE = 0.7;
|
|
9
|
+
// Percentage to shrink the visible preview by, without affecting the panel size.
|
|
10
|
+
const PREVIEW_SCALE_SHRINK = 0.9;
|
|
6
11
|
const SCALE_OFFSET = 0.025;
|
|
7
|
-
const TRANSLATE_X_OFFSET = 6;
|
|
8
12
|
const TRANSLATE_Y_OFFSET = 12;
|
|
9
13
|
const panelWidth = (width, wide) => {
|
|
10
|
-
const scale = wide ?
|
|
14
|
+
const scale = wide ? PREVIEW_SCALE_WIDE : PREVIEW_SCALE;
|
|
11
15
|
return width * (1 - scale - SCALE_OFFSET);
|
|
12
16
|
};
|
|
13
17
|
export const getNavigatorPanelPosition = (animatedValue, previewWidth, wide) => {
|
|
@@ -32,7 +36,10 @@ export const getAddonPanelPosition = (animatedValue, previewWidth, wide) => {
|
|
|
32
36
|
{
|
|
33
37
|
translateX: animatedValue.interpolate({
|
|
34
38
|
inputRange: [PREVIEW, ADDONS],
|
|
35
|
-
outputRange: [
|
|
39
|
+
outputRange: [
|
|
40
|
+
previewWidth * RTL_SCALE,
|
|
41
|
+
(previewWidth - panelWidth(previewWidth, wide)) * RTL_SCALE,
|
|
42
|
+
],
|
|
36
43
|
}),
|
|
37
44
|
},
|
|
38
45
|
],
|
|
@@ -40,11 +47,25 @@ export const getAddonPanelPosition = (animatedValue, previewWidth, wide) => {
|
|
|
40
47
|
},
|
|
41
48
|
];
|
|
42
49
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Build the animated style for the preview container view.
|
|
52
|
+
*
|
|
53
|
+
* When the navigator or addons panel is focused, the preview container is
|
|
54
|
+
* scaled down and translated to the left (or right) of the panel.
|
|
55
|
+
*/
|
|
56
|
+
export const getPreviewStyle = ({ animatedValue, previewDimensions: { width: previewWidth, height: previewHeight }, wide, insets, tabOpen, lastTabOpen, }) => {
|
|
57
|
+
const scale = (wide ? PREVIEW_SCALE_WIDE : PREVIEW_SCALE) * PREVIEW_SCALE_SHRINK;
|
|
58
|
+
const scaledPreviewWidth = previewWidth * scale;
|
|
59
|
+
const scaledPreviewHeight = previewHeight * scale;
|
|
60
|
+
// Horizontally center the scaled preview in the available space beside the panel.
|
|
61
|
+
const nonPanelWidth = previewWidth - panelWidth(previewWidth, wide);
|
|
62
|
+
const translateXOffset = (nonPanelWidth - scaledPreviewWidth) / 2;
|
|
63
|
+
const translateX = (previewWidth / 2 - (previewWidth * scale) / 2 - translateXOffset) * RTL_SCALE;
|
|
64
|
+
// Translate the preview to the top edge of the screen, move it down by the
|
|
65
|
+
// safe area inset, then by the preview Y offset.
|
|
66
|
+
const translateY = -(previewHeight / 2 - scaledPreviewHeight / 2) + insets.top + TRANSLATE_Y_OFFSET;
|
|
67
|
+
// Is navigation moving from one panel to another, skipping preview?
|
|
68
|
+
const skipPreview = lastTabOpen !== PREVIEW && tabOpen !== PREVIEW;
|
|
48
69
|
return {
|
|
49
70
|
transform: [
|
|
50
71
|
{
|
|
@@ -56,22 +77,32 @@ export const getPreviewPosition = ({ animatedValue, previewDimensions: { width:
|
|
|
56
77
|
{
|
|
57
78
|
translateY: animatedValue.interpolate({
|
|
58
79
|
inputRange: [NAVIGATOR, PREVIEW, ADDONS],
|
|
59
|
-
outputRange: [translateY,
|
|
80
|
+
outputRange: [translateY, skipPreview ? translateY : 0, translateY],
|
|
60
81
|
}),
|
|
61
82
|
},
|
|
62
|
-
],
|
|
63
|
-
};
|
|
64
|
-
};
|
|
65
|
-
export const getPreviewScale = (animatedValue, slideBetweenAnimation, wide) => {
|
|
66
|
-
const scale = wide ? PREVIEW_WIDE_SCREEN : PREVIEW_SCALE;
|
|
67
|
-
return {
|
|
68
|
-
transform: [
|
|
69
83
|
{
|
|
70
84
|
scale: animatedValue.interpolate({
|
|
71
85
|
inputRange: [NAVIGATOR, PREVIEW, ADDONS],
|
|
72
|
-
outputRange: [scale,
|
|
86
|
+
outputRange: [scale, skipPreview ? scale : 1, scale],
|
|
73
87
|
}),
|
|
74
88
|
},
|
|
75
89
|
],
|
|
76
90
|
};
|
|
77
91
|
};
|
|
92
|
+
/**
|
|
93
|
+
* Build the animated shadow style for the preview.
|
|
94
|
+
*
|
|
95
|
+
* When the navigator or addons panel are visible the scaled preview will have
|
|
96
|
+
* a shadow, and when going to the preview tab the shadow will be invisible.
|
|
97
|
+
*/
|
|
98
|
+
export const getPreviewShadowStyle = (animatedValue) => ({
|
|
99
|
+
elevation: 8,
|
|
100
|
+
shadowColor: '#000',
|
|
101
|
+
shadowOpacity: animatedValue.interpolate({
|
|
102
|
+
inputRange: [NAVIGATOR, PREVIEW, ADDONS],
|
|
103
|
+
outputRange: [0.25, 0, 0.25],
|
|
104
|
+
}),
|
|
105
|
+
shadowRadius: 8,
|
|
106
|
+
shadowOffset: { width: 0, height: 0 },
|
|
107
|
+
overflow: 'visible',
|
|
108
|
+
});
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React, { Dispatch, SetStateAction } from 'react';
|
|
2
|
+
import { ViewProps } from 'react-native';
|
|
2
3
|
interface Props {
|
|
3
4
|
tabOpen: number;
|
|
4
5
|
onChangeTab: (index: number) => void;
|
|
5
6
|
isUIVisible: boolean;
|
|
6
7
|
setIsUIVisible: Dispatch<SetStateAction<boolean>>;
|
|
8
|
+
onLayout: ViewProps['onLayout'];
|
|
7
9
|
}
|
|
8
|
-
declare const _default: React.MemoExoticComponent<({ tabOpen, onChangeTab, isUIVisible, setIsUIVisible }: Props) => JSX.Element>;
|
|
10
|
+
declare const _default: React.MemoExoticComponent<({ tabOpen, onChangeTab, isUIVisible, setIsUIVisible, onLayout }: Props) => JSX.Element>;
|
|
9
11
|
export default _default;
|