@storybook/react-native 6.5.0-rc.2 → 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/preview/View.d.ts +5 -3
- package/dist/preview/View.js +3 -1
- package/dist/preview/components/OnDeviceUI/OnDeviceUI.js +16 -11
- package/dist/preview/components/OnDeviceUI/Panel.d.ts +7 -5
- package/dist/preview/components/OnDeviceUI/Panel.js +9 -4
- 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/navigation/Navigation.js +3 -3
- 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 +55 -47
- package/package.json +3 -2
- 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/preview/View.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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
7
|
type StoryKind = string;
|
|
@@ -16,6 +16,9 @@ type InitialSelection = `${StoryKind}--${StoryName}` | {
|
|
|
16
16
|
*/
|
|
17
17
|
name: StoryName;
|
|
18
18
|
};
|
|
19
|
+
type DeepPartial<T> = T extends object ? {
|
|
20
|
+
[P in keyof T]?: DeepPartial<T[P]>;
|
|
21
|
+
} : T;
|
|
19
22
|
export type Params = {
|
|
20
23
|
onDeviceUI?: boolean;
|
|
21
24
|
enableWebsockets?: boolean;
|
|
@@ -29,8 +32,7 @@ export 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,8 +1,7 @@
|
|
|
1
1
|
import styled from '@emotion/native';
|
|
2
|
-
import { useTheme } from 'emotion-theming';
|
|
3
2
|
import React, { useState, useRef } from 'react';
|
|
4
3
|
import { Animated, Dimensions, Easing, Keyboard, KeyboardAvoidingView, Platform, TouchableOpacity, StatusBar, StyleSheet, View, } from 'react-native';
|
|
5
|
-
import { useStoryContextParam } from '../../../hooks';
|
|
4
|
+
import { useStoryContextParam, useTheme } from '../../../hooks';
|
|
6
5
|
import StoryListView from '../StoryListView';
|
|
7
6
|
import StoryView from '../StoryView';
|
|
8
7
|
import AbsolutePositionedKeyboardAwareView from './absolute-positioned-keyboard-aware-view';
|
|
@@ -26,14 +25,14 @@ const flex = { flex: 1 };
|
|
|
26
25
|
*/
|
|
27
26
|
function Preview({ animatedValue, style, children }) {
|
|
28
27
|
const theme = useTheme();
|
|
29
|
-
const containerStyle = Object.assign({ backgroundColor: theme.backgroundColor }, getPreviewShadowStyle(animatedValue));
|
|
28
|
+
const containerStyle = Object.assign({ backgroundColor: theme.preview.backgroundColor }, getPreviewShadowStyle(animatedValue));
|
|
30
29
|
return (React.createElement(Animated.View, { style: [flex, containerStyle] },
|
|
31
30
|
React.createElement(View, { style: [flex, style] }, children)));
|
|
32
31
|
}
|
|
33
32
|
const styles = StyleSheet.create({
|
|
34
33
|
expoAndroidContainer: { paddingTop: StatusBar.currentHeight },
|
|
35
34
|
});
|
|
36
|
-
const Container = styled.View(({ theme }) => (Object.assign({ flex: 1, backgroundColor: theme.
|
|
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' } }))));
|
|
37
36
|
const OnDeviceUI = ({ storyIndex, isUIHidden, shouldDisableKeyboardAvoidingView, keyboardAvoidingViewVerticalOffset, tabOpen: initialTabOpen, }) => {
|
|
38
37
|
const [tabOpen, setTabOpen] = useState(initialTabOpen || PREVIEW);
|
|
39
38
|
const lastTabOpen = React.useRef(tabOpen);
|
|
@@ -44,7 +43,6 @@ const OnDeviceUI = ({ storyIndex, isUIHidden, shouldDisableKeyboardAvoidingView,
|
|
|
44
43
|
const animatedValue = useRef(new Animated.Value(tabOpen));
|
|
45
44
|
const wide = useWindowDimensions().width >= BREAKPOINT;
|
|
46
45
|
const insets = useSafeAreaInsets();
|
|
47
|
-
const theme = useTheme();
|
|
48
46
|
const [isUIVisible, setIsUIVisible] = useState(isUIHidden !== undefined ? !isUIHidden : true);
|
|
49
47
|
const handleToggleTab = React.useCallback((newTabOpen) => {
|
|
50
48
|
if (newTabOpen === tabOpen) {
|
|
@@ -96,23 +94,30 @@ const OnDeviceUI = ({ storyIndex, isUIHidden, shouldDisableKeyboardAvoidingView,
|
|
|
96
94
|
paddingBottom: isUIVisible ? insets.bottom + navBarHeight : noSafeArea ? 0 : insets.bottom,
|
|
97
95
|
paddingTop: !noSafeArea ? insets.top : 0,
|
|
98
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);
|
|
99
105
|
return (React.createElement(React.Fragment, null,
|
|
100
106
|
React.createElement(Container, null,
|
|
101
|
-
React.createElement(KeyboardAvoidingView, { enabled: !shouldDisableKeyboardAvoidingView || tabOpen !== PREVIEW, behavior: IS_IOS ? 'padding' : null, keyboardVerticalOffset:
|
|
107
|
+
React.createElement(KeyboardAvoidingView, { enabled: !shouldDisableKeyboardAvoidingView || tabOpen !== PREVIEW, behavior: IS_IOS ? 'padding' : null, keyboardVerticalOffset: keyboardVerticalOffset, style: flex },
|
|
102
108
|
React.createElement(AbsolutePositionedKeyboardAwareView, { onLayout: setPreviewDimensions, previewDimensions: previewDimensions },
|
|
103
109
|
React.createElement(Animated.View, { style: previewWrapperStyles },
|
|
104
110
|
React.createElement(Preview, { style: safeAreaMargins, animatedValue: animatedValue.current },
|
|
105
111
|
React.createElement(StoryView, null)),
|
|
106
112
|
tabOpen !== PREVIEW ? (React.createElement(TouchableOpacity, { style: StyleSheet.absoluteFillObject, onPress: () => handleToggleTab(PREVIEW) })) : null),
|
|
107
|
-
React.createElement(Panel, { style: [
|
|
113
|
+
React.createElement(Panel, { edge: "right", style: [
|
|
108
114
|
getNavigatorPanelPosition(animatedValue.current, previewDimensions.width, wide),
|
|
109
|
-
|
|
110
|
-
{ backgroundColor: theme.storyListBackgroundColor },
|
|
115
|
+
panelSafeAreaMargins,
|
|
111
116
|
] },
|
|
112
117
|
React.createElement(StoryListView, { storyIndex: storyIndex })),
|
|
113
|
-
React.createElement(Panel, { style: [
|
|
118
|
+
React.createElement(Panel, { edge: "left", style: [
|
|
114
119
|
getAddonPanelPosition(animatedValue.current, previewDimensions.width, wide),
|
|
115
|
-
|
|
120
|
+
panelSafeAreaMargins,
|
|
116
121
|
] },
|
|
117
122
|
React.createElement(Addons, { active: tabOpen === ADDONS })))),
|
|
118
123
|
React.createElement(Navigation, { onLayout: measureNavigation, tabOpen: tabOpen, onChangeTab: handleToggleTab, isUIVisible: isUIVisible, setIsUIVisible: setIsUIVisible }))));
|
|
@@ -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
|
};
|
|
@@ -2,7 +2,7 @@ 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
|
|
5
|
+
import { NavigationBar } from './NavigationBar';
|
|
6
6
|
import VisibilityButton from './VisibilityButton';
|
|
7
7
|
const SWIPE_CONFIG = {
|
|
8
8
|
velocityThreshold: 0.2,
|
|
@@ -31,7 +31,7 @@ const Navigation = ({ tabOpen, onChangeTab, isUIVisible, setIsUIVisible, onLayou
|
|
|
31
31
|
};
|
|
32
32
|
return (React.createElement(View, { style: navStyle, onLayout: onLayout },
|
|
33
33
|
isUIVisible && (React.createElement(GestureRecognizer, { onSwipeLeft: handleSwipeLeft, onSwipeRight: handleSwipeRight, config: SWIPE_CONFIG },
|
|
34
|
-
React.createElement(
|
|
35
|
-
React.createElement(VisibilityButton, { onPress: handleToggleUI, style: {
|
|
34
|
+
React.createElement(NavigationBar, { index: tabOpen, onPress: onChangeTab, style: { paddingBottom: insets.bottom } }))),
|
|
35
|
+
React.createElement(VisibilityButton, { onPress: handleToggleUI, style: { paddingBottom: insets.bottom } })));
|
|
36
36
|
};
|
|
37
37
|
export default React.memo(Navigation);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
export interface NavigationBarProps {
|
|
4
|
+
index: number;
|
|
5
|
+
onPress: (id: number) => void;
|
|
6
|
+
style?: StyleProp<ViewStyle>;
|
|
7
|
+
}
|
|
8
|
+
export declare const NavigationBar: React.MemoExoticComponent<({ index, onPress, style }: NavigationBarProps) => JSX.Element>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from '@emotion/native';
|
|
3
|
+
import { NAVIGATOR, PREVIEW, ADDONS } from './constants';
|
|
4
|
+
import { TabBar, TabButton } from '../../Shared/tabs';
|
|
5
|
+
const NavigationTabBar = styled(TabBar)(({ theme }) => ({
|
|
6
|
+
paddingHorizontal: theme.tokens.spacing2,
|
|
7
|
+
backgroundColor: theme.navigation.backgroundColor,
|
|
8
|
+
borderColor: theme.navigation.borderColor,
|
|
9
|
+
borderTopWidth: theme.navigation.borderWidth,
|
|
10
|
+
}));
|
|
11
|
+
export const NavigationBar = React.memo(({ index, onPress, style }) => (React.createElement(NavigationTabBar, { style: style },
|
|
12
|
+
React.createElement(TabButton, { onPress: onPress, testID: "BottomMenu.Navigator", id: NAVIGATOR, active: index === NAVIGATOR }, "NAVIGATOR"),
|
|
13
|
+
React.createElement(TabButton, { onPress: onPress, testID: "BottomMenu.Preview", id: PREVIEW, active: index === PREVIEW }, "PREVIEW"),
|
|
14
|
+
React.createElement(TabButton, { onPress: onPress, testID: "BottomMenu.Addons", id: ADDONS, active: index === ADDONS }, "ADDONS"))));
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import styled from '@emotion/native';
|
|
3
3
|
import { View } from 'react-native';
|
|
4
|
-
const Touchable = styled.TouchableOpacity({
|
|
4
|
+
const Touchable = styled.TouchableOpacity(({ theme }) => ({
|
|
5
5
|
backgroundColor: 'transparent',
|
|
6
6
|
position: 'absolute',
|
|
7
|
-
right:
|
|
8
|
-
bottom:
|
|
7
|
+
right: theme.tokens.spacing2,
|
|
8
|
+
bottom: theme.tokens.spacing4,
|
|
9
9
|
zIndex: 100,
|
|
10
|
-
});
|
|
10
|
+
}));
|
|
11
11
|
const HIDE_ICON_SIZE = 14;
|
|
12
12
|
const HIDE_ICON_BORDER_WIDTH = 1;
|
|
13
13
|
const Inner = styled.View(({ theme }) => ({
|
|
@@ -16,22 +16,22 @@ const Inner = styled.View(({ theme }) => ({
|
|
|
16
16
|
left: 0,
|
|
17
17
|
width: HIDE_ICON_SIZE,
|
|
18
18
|
height: HIDE_ICON_SIZE,
|
|
19
|
-
borderRadius:
|
|
19
|
+
borderRadius: theme.navigation.visibilityBorderRadius,
|
|
20
20
|
overflow: 'hidden',
|
|
21
|
-
borderColor: theme.
|
|
21
|
+
borderColor: theme.navigation.visibilityInnerBorderColor,
|
|
22
22
|
borderWidth: HIDE_ICON_BORDER_WIDTH * 2,
|
|
23
23
|
}));
|
|
24
|
-
const Outer = styled.View({
|
|
24
|
+
const Outer = styled.View(({ theme }) => ({
|
|
25
25
|
position: 'absolute',
|
|
26
26
|
top: 0,
|
|
27
27
|
left: 0,
|
|
28
28
|
width: HIDE_ICON_SIZE,
|
|
29
29
|
height: HIDE_ICON_SIZE,
|
|
30
|
-
borderRadius:
|
|
30
|
+
borderRadius: theme.navigation.visibilityBorderRadius,
|
|
31
31
|
overflow: 'hidden',
|
|
32
|
-
borderColor:
|
|
32
|
+
borderColor: theme.navigation.visibilityOuterBorderColor,
|
|
33
33
|
borderWidth: HIDE_ICON_BORDER_WIDTH,
|
|
34
|
-
});
|
|
34
|
+
}));
|
|
35
35
|
const hideIconStyles = {
|
|
36
36
|
width: HIDE_ICON_SIZE,
|
|
37
37
|
height: HIDE_ICON_SIZE,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A general (flex)box layout component that accepts props for flexbox layout
|
|
3
|
+
* styles, such as `flex`, `alignItems`, `marginVertical`, etc.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* <Box flex flexDirection='row' alignItems='center' marginVertical={42}>
|
|
8
|
+
* <MyContent />
|
|
9
|
+
* </Box>
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export declare const Box: any;
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
};
|
|
12
|
+
import styled from '@emotion/native';
|
|
13
|
+
/**
|
|
14
|
+
* A general (flex)box layout component that accepts props for flexbox layout
|
|
15
|
+
* styles, such as `flex`, `alignItems`, `marginVertical`, etc.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <Box flex flexDirection='row' alignItems='center' marginVertical={42}>
|
|
20
|
+
* <MyContent />
|
|
21
|
+
* </Box>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export const Box = styled.View((_a) => {
|
|
25
|
+
var { flex } = _a, layoutProps = __rest(_a, ["flex"]);
|
|
26
|
+
return (Object.assign({ flex: flex === true ? 1 : flex === false ? 0 : flex }, layoutProps));
|
|
27
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
interface TabButtonProps {
|
|
4
|
+
id: number | string;
|
|
5
|
+
active: boolean;
|
|
6
|
+
onPress: (id: number | string) => void;
|
|
7
|
+
testID?: string;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
export declare const TabButton: React.MemoExoticComponent<({ onPress, id, active, children, testID }: TabButtonProps) => JSX.Element>;
|
|
11
|
+
interface TabBarProps {
|
|
12
|
+
/**
|
|
13
|
+
* Should the tab bar contents scroll horizontally?
|
|
14
|
+
*/
|
|
15
|
+
scrollable?: boolean;
|
|
16
|
+
style?: StyleProp<ViewStyle>;
|
|
17
|
+
children: React.ReactNode;
|
|
18
|
+
}
|
|
19
|
+
export declare const TabBar: React.MemoExoticComponent<({ scrollable, style, children }: TabBarProps) => JSX.Element>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ScrollView, View } from 'react-native';
|
|
3
|
+
import styled from '@emotion/native';
|
|
4
|
+
import { useTheme } from '../../../hooks';
|
|
5
|
+
const TabButtonText = styled.Text(({ theme, active }) => ({
|
|
6
|
+
color: active ? theme.tabs.activeTextColor : theme.tabs.inactiveTextColor,
|
|
7
|
+
paddingVertical: theme.tabs.paddingVertical,
|
|
8
|
+
paddingHorizontal: theme.tabs.paddingHorizontal,
|
|
9
|
+
fontSize: theme.tabs.fontSize,
|
|
10
|
+
fontWeight: theme.tabs.fontWeight,
|
|
11
|
+
}));
|
|
12
|
+
const hitSlop = { top: 8, left: 0, right: 0, bottom: 20 };
|
|
13
|
+
const TabButtonTouchable = styled.TouchableOpacity(({ theme, active }) => ({
|
|
14
|
+
borderWidth: theme.tabs.borderWidth,
|
|
15
|
+
borderColor: active ? theme.tabs.activeBorderColor : theme.tabs.inactiveBorderColor,
|
|
16
|
+
borderRadius: theme.tabs.borderRadius,
|
|
17
|
+
backgroundColor: active ? theme.tabs.activeBackgroundColor : theme.tabs.inactiveBackgroundColor,
|
|
18
|
+
}));
|
|
19
|
+
export const TabButton = React.memo(({ onPress, id, active, children, testID }) => {
|
|
20
|
+
return (React.createElement(TabButtonTouchable, { active: active, testID: testID, onPress: () => onPress(id), activeOpacity: 0.8, hitSlop: hitSlop },
|
|
21
|
+
React.createElement(TabButtonText, { active: active }, children)));
|
|
22
|
+
});
|
|
23
|
+
export const TabBar = React.memo(({ scrollable = false, style, children }) => {
|
|
24
|
+
const theme = useTheme();
|
|
25
|
+
if (scrollable) {
|
|
26
|
+
children = (React.createElement(ScrollView, { showsHorizontalScrollIndicator: false, horizontal: true, contentContainerStyle: { paddingHorizontal: theme.tokens.spacing2 } }, children));
|
|
27
|
+
}
|
|
28
|
+
return (React.createElement(View, { style: style },
|
|
29
|
+
React.createElement(TabBarContainer, null, children)));
|
|
30
|
+
});
|
|
31
|
+
const TabBarContainer = styled.View({
|
|
32
|
+
flexDirection: 'row',
|
|
33
|
+
paddingVertical: 6,
|
|
34
|
+
justifyItems: 'center',
|
|
35
|
+
});
|
|
@@ -1,15 +1,185 @@
|
|
|
1
|
-
|
|
1
|
+
import { ShadowStyleIOS, ViewStyle, TextStyle } from 'react-native';
|
|
2
|
+
type ShadowStyle = ShadowStyleIOS | Pick<ViewStyle, 'elevation'>;
|
|
3
|
+
type FontWeight = TextStyle['fontWeight'];
|
|
4
|
+
/**
|
|
5
|
+
* Primitive tokens used to build the theme.
|
|
6
|
+
*
|
|
7
|
+
* Ideally components should not use these values directly, and should prefer
|
|
8
|
+
* to use more semantic values from the theme.
|
|
9
|
+
*/
|
|
10
|
+
interface ThemeTokens {
|
|
11
|
+
spacing1: number;
|
|
12
|
+
spacing2: number;
|
|
13
|
+
spacing3: number;
|
|
14
|
+
spacing4: number;
|
|
15
|
+
spacing5: number;
|
|
16
|
+
spacing6: number;
|
|
17
|
+
fontSize: {
|
|
18
|
+
small: number;
|
|
19
|
+
smaller: number;
|
|
20
|
+
normal: number;
|
|
21
|
+
};
|
|
22
|
+
color: {
|
|
23
|
+
black: string;
|
|
24
|
+
white: string;
|
|
25
|
+
grey200: string;
|
|
26
|
+
grey700: string;
|
|
27
|
+
red500: string;
|
|
28
|
+
blue100: string;
|
|
29
|
+
blue200: string;
|
|
30
|
+
blue250: string;
|
|
31
|
+
blue300: string;
|
|
32
|
+
blue400: string;
|
|
33
|
+
blue600: string;
|
|
34
|
+
green500: string;
|
|
35
|
+
};
|
|
36
|
+
borderRadius: {
|
|
37
|
+
small: number;
|
|
38
|
+
medium: number;
|
|
39
|
+
large: number;
|
|
40
|
+
round: number;
|
|
41
|
+
};
|
|
42
|
+
borderWidthNormal: number;
|
|
43
|
+
/** Elevation shadows */
|
|
44
|
+
elevation: {
|
|
45
|
+
floating: ShadowStyle;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
interface ThemeButton {
|
|
49
|
+
textColor: string;
|
|
2
50
|
backgroundColor: string;
|
|
3
|
-
storyListBackgroundColor: string;
|
|
4
|
-
listItemTextColor: string;
|
|
5
|
-
listItemActiveColor: string;
|
|
6
|
-
listItemActiveTextColor: string;
|
|
7
|
-
sectionActiveColor: string;
|
|
8
|
-
headerTextColor: string;
|
|
9
|
-
labelColor: string;
|
|
10
51
|
borderColor: string;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
52
|
+
borderWidth: number;
|
|
53
|
+
borderRadius: number;
|
|
54
|
+
}
|
|
55
|
+
export interface Theme {
|
|
56
|
+
tokens: ThemeTokens;
|
|
57
|
+
backgroundColor: string;
|
|
58
|
+
text: {
|
|
59
|
+
primaryColor: string;
|
|
60
|
+
secondaryColor: string;
|
|
61
|
+
linkColor: string;
|
|
62
|
+
};
|
|
63
|
+
preview: {
|
|
64
|
+
containerBackgroundColor: string;
|
|
65
|
+
backgroundColor: string;
|
|
66
|
+
};
|
|
67
|
+
/** Navigation bar and related area */
|
|
68
|
+
navigation: {
|
|
69
|
+
backgroundColor: string;
|
|
70
|
+
borderColor: string;
|
|
71
|
+
borderWidth: number;
|
|
72
|
+
visibilityBorderRadius: number;
|
|
73
|
+
visibilityInnerBorderColor: string;
|
|
74
|
+
visibilityOuterBorderColor: string;
|
|
75
|
+
};
|
|
76
|
+
/** Side panels (Story list, addons) */
|
|
77
|
+
panel: {
|
|
78
|
+
backgroundColor: string;
|
|
79
|
+
borderWidth: number;
|
|
80
|
+
borderColor: string;
|
|
81
|
+
paddingVertical: number;
|
|
82
|
+
paddingHorizontal: number;
|
|
83
|
+
};
|
|
84
|
+
/** Story list and list items */
|
|
85
|
+
storyList: {
|
|
86
|
+
fontSize: number;
|
|
87
|
+
headerPaddingHorizontal: number;
|
|
88
|
+
headerPaddingVertical: number;
|
|
89
|
+
headerTextColor: string;
|
|
90
|
+
headerFontWeight: FontWeight;
|
|
91
|
+
storyPaddingHorizontal: number;
|
|
92
|
+
storyPaddingVertical: number;
|
|
93
|
+
storyIndent: number;
|
|
94
|
+
storyTextColor: string;
|
|
95
|
+
storyFontWeight: FontWeight;
|
|
96
|
+
storySelectedBackgroundColor: string;
|
|
97
|
+
storySelectedTextColor: string;
|
|
98
|
+
storySelectedFontWeight: FontWeight;
|
|
99
|
+
sectionSpacing: number;
|
|
100
|
+
sectionActiveBackgroundColor: string;
|
|
101
|
+
sectionBorderRadius: number;
|
|
102
|
+
search: {
|
|
103
|
+
fontSize: number;
|
|
104
|
+
textColor: string;
|
|
105
|
+
placeholderTextColor: string;
|
|
106
|
+
borderRadius: number;
|
|
107
|
+
borderColor: string;
|
|
108
|
+
borderWidth: number;
|
|
109
|
+
backgroundColor: string;
|
|
110
|
+
paddingVertical: number;
|
|
111
|
+
paddingHorizontal: number;
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
/** Buttons */
|
|
115
|
+
button: {
|
|
116
|
+
fontSize: number;
|
|
117
|
+
fontWeight: FontWeight;
|
|
118
|
+
paddingVertical: number;
|
|
119
|
+
paddingHorizontal: number;
|
|
120
|
+
primary: ThemeButton;
|
|
121
|
+
secondary: ThemeButton;
|
|
122
|
+
};
|
|
123
|
+
/** Tabs (navigation and addons) */
|
|
124
|
+
tabs: {
|
|
125
|
+
fontSize: number;
|
|
126
|
+
fontWeight: FontWeight;
|
|
127
|
+
paddingVertical: number;
|
|
128
|
+
paddingHorizontal: number;
|
|
129
|
+
borderWidth: number;
|
|
130
|
+
borderRadius: number;
|
|
131
|
+
activeBorderColor: string;
|
|
132
|
+
activeBackgroundColor: string;
|
|
133
|
+
activeTextColor: string;
|
|
134
|
+
inactiveBorderColor: string;
|
|
135
|
+
inactiveBackgroundColor: string;
|
|
136
|
+
inactiveTextColor: string;
|
|
137
|
+
};
|
|
138
|
+
/** Inputs (text, radio, slider, etc.) */
|
|
139
|
+
inputs: {
|
|
140
|
+
errorTextColor: string;
|
|
141
|
+
labelFontSize: number;
|
|
142
|
+
labelTextColor: string;
|
|
143
|
+
text: {
|
|
144
|
+
fontSize: number;
|
|
145
|
+
textColor: string;
|
|
146
|
+
borderWidth: number;
|
|
147
|
+
borderColor: string;
|
|
148
|
+
backgroundColor: string;
|
|
149
|
+
borderRadius: number;
|
|
150
|
+
paddingVertical: number;
|
|
151
|
+
paddingHorizontal: number;
|
|
152
|
+
};
|
|
153
|
+
radio: {
|
|
154
|
+
fontSize: number;
|
|
155
|
+
height: number;
|
|
156
|
+
borderWidth: number;
|
|
157
|
+
borderColor: string;
|
|
158
|
+
backgroundColor: string;
|
|
159
|
+
paddingVertical: number;
|
|
160
|
+
paddingHorizontal: number;
|
|
161
|
+
activeBackgroundColor: string;
|
|
162
|
+
itemSpacing: number;
|
|
163
|
+
labelSpacing: number;
|
|
164
|
+
};
|
|
165
|
+
swatch: {
|
|
166
|
+
fontSize: number;
|
|
167
|
+
height: number;
|
|
168
|
+
borderWidth: number;
|
|
169
|
+
borderColor: string;
|
|
170
|
+
backgroundColor: string;
|
|
171
|
+
outerBorderRadius: number;
|
|
172
|
+
innerBorderRadius: number;
|
|
173
|
+
paddingVertical: number;
|
|
174
|
+
paddingHorizontal: number;
|
|
175
|
+
nameTextWeight: FontWeight;
|
|
176
|
+
};
|
|
177
|
+
slider: {
|
|
178
|
+
fontSize: number;
|
|
179
|
+
labelTextColor: string;
|
|
180
|
+
valueTextColor: string;
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
export declare const theme: Theme;
|
|
185
|
+
export {};
|
|
@@ -1,15 +1,179 @@
|
|
|
1
|
+
const tokens = {
|
|
2
|
+
spacing1: 4,
|
|
3
|
+
spacing2: 8,
|
|
4
|
+
spacing3: 12,
|
|
5
|
+
spacing4: 16,
|
|
6
|
+
spacing5: 20,
|
|
7
|
+
spacing6: 24,
|
|
8
|
+
fontSize: {
|
|
9
|
+
small: 12,
|
|
10
|
+
smaller: 14,
|
|
11
|
+
normal: 16,
|
|
12
|
+
},
|
|
13
|
+
color: {
|
|
14
|
+
black: '#001a23',
|
|
15
|
+
white: '#ffffff',
|
|
16
|
+
grey200: '#dee2e3',
|
|
17
|
+
grey700: '#859499',
|
|
18
|
+
red500: '#ff4400',
|
|
19
|
+
blue100: '#f6f9fc',
|
|
20
|
+
blue200: '#e0eaf5',
|
|
21
|
+
blue250: '#dcebf9',
|
|
22
|
+
blue300: '#b2cbe6',
|
|
23
|
+
blue400: '#a3c1e0',
|
|
24
|
+
blue600: '#1ea7fd',
|
|
25
|
+
green500: '#66bf3c',
|
|
26
|
+
},
|
|
27
|
+
borderRadius: {
|
|
28
|
+
small: 4,
|
|
29
|
+
medium: 6,
|
|
30
|
+
large: 8,
|
|
31
|
+
round: 100,
|
|
32
|
+
},
|
|
33
|
+
borderWidthNormal: 1,
|
|
34
|
+
elevation: {
|
|
35
|
+
floating: {
|
|
36
|
+
shadowColor: '#000000',
|
|
37
|
+
shadowOpacity: 0.2,
|
|
38
|
+
shadowOffset: { width: 0, height: 0 },
|
|
39
|
+
shadowRadius: 16,
|
|
40
|
+
elevation: 10,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
const text = {
|
|
45
|
+
primaryColor: tokens.color.black,
|
|
46
|
+
secondaryColor: tokens.color.grey700,
|
|
47
|
+
linkColor: '#0000ff',
|
|
48
|
+
};
|
|
1
49
|
export const theme = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
50
|
+
tokens,
|
|
51
|
+
text,
|
|
52
|
+
backgroundColor: tokens.color.white,
|
|
53
|
+
preview: {
|
|
54
|
+
containerBackgroundColor: tokens.color.white,
|
|
55
|
+
backgroundColor: tokens.color.white,
|
|
56
|
+
},
|
|
57
|
+
navigation: {
|
|
58
|
+
backgroundColor: tokens.color.white,
|
|
59
|
+
borderColor: tokens.color.grey200,
|
|
60
|
+
borderWidth: tokens.borderWidthNormal,
|
|
61
|
+
visibilityBorderRadius: tokens.borderRadius.small,
|
|
62
|
+
visibilityInnerBorderColor: tokens.color.grey700,
|
|
63
|
+
visibilityOuterBorderColor: tokens.color.white,
|
|
64
|
+
},
|
|
65
|
+
panel: {
|
|
66
|
+
backgroundColor: tokens.color.blue100,
|
|
67
|
+
borderWidth: tokens.borderWidthNormal,
|
|
68
|
+
borderColor: tokens.color.blue200,
|
|
69
|
+
paddingVertical: 0,
|
|
70
|
+
paddingHorizontal: tokens.spacing2,
|
|
71
|
+
},
|
|
72
|
+
storyList: {
|
|
73
|
+
fontSize: tokens.fontSize.normal,
|
|
74
|
+
headerPaddingHorizontal: tokens.spacing2,
|
|
75
|
+
headerPaddingVertical: tokens.spacing2,
|
|
76
|
+
headerTextColor: text.primaryColor,
|
|
77
|
+
headerFontWeight: '500',
|
|
78
|
+
storyPaddingHorizontal: tokens.spacing2,
|
|
79
|
+
storyPaddingVertical: tokens.spacing1 * 1.5,
|
|
80
|
+
storyIndent: tokens.spacing6,
|
|
81
|
+
storyTextColor: text.primaryColor,
|
|
82
|
+
storyFontWeight: '400',
|
|
83
|
+
storySelectedBackgroundColor: tokens.color.blue600,
|
|
84
|
+
storySelectedTextColor: tokens.color.white,
|
|
85
|
+
storySelectedFontWeight: '700',
|
|
86
|
+
sectionSpacing: tokens.spacing2,
|
|
87
|
+
sectionActiveBackgroundColor: tokens.color.blue200,
|
|
88
|
+
sectionBorderRadius: tokens.borderRadius.medium,
|
|
89
|
+
search: {
|
|
90
|
+
fontSize: tokens.fontSize.normal,
|
|
91
|
+
textColor: text.primaryColor,
|
|
92
|
+
placeholderTextColor: text.secondaryColor,
|
|
93
|
+
borderRadius: tokens.borderRadius.round,
|
|
94
|
+
borderColor: tokens.color.blue400,
|
|
95
|
+
borderWidth: tokens.borderWidthNormal,
|
|
96
|
+
backgroundColor: tokens.color.white,
|
|
97
|
+
paddingVertical: tokens.spacing2,
|
|
98
|
+
paddingHorizontal: tokens.spacing3,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
button: {
|
|
102
|
+
fontSize: tokens.fontSize.smaller,
|
|
103
|
+
fontWeight: '600',
|
|
104
|
+
paddingVertical: tokens.spacing2,
|
|
105
|
+
paddingHorizontal: tokens.spacing5,
|
|
106
|
+
primary: {
|
|
107
|
+
textColor: text.primaryColor,
|
|
108
|
+
backgroundColor: tokens.color.blue250,
|
|
109
|
+
borderColor: tokens.color.blue300,
|
|
110
|
+
borderWidth: tokens.borderWidthNormal,
|
|
111
|
+
borderRadius: tokens.borderRadius.medium,
|
|
112
|
+
},
|
|
113
|
+
secondary: {
|
|
114
|
+
textColor: text.primaryColor,
|
|
115
|
+
backgroundColor: 'transparent',
|
|
116
|
+
borderColor: tokens.color.blue300,
|
|
117
|
+
borderWidth: tokens.borderWidthNormal,
|
|
118
|
+
borderRadius: tokens.borderRadius.medium,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
tabs: {
|
|
122
|
+
fontSize: tokens.fontSize.small,
|
|
123
|
+
fontWeight: '500',
|
|
124
|
+
paddingVertical: tokens.spacing2,
|
|
125
|
+
paddingHorizontal: tokens.spacing2 * 1.25,
|
|
126
|
+
borderWidth: tokens.borderWidthNormal,
|
|
127
|
+
borderRadius: tokens.borderRadius.round,
|
|
128
|
+
activeBorderColor: tokens.color.blue300,
|
|
129
|
+
activeBackgroundColor: tokens.color.blue250,
|
|
130
|
+
activeTextColor: text.primaryColor,
|
|
131
|
+
inactiveBorderColor: 'transparent',
|
|
132
|
+
inactiveBackgroundColor: 'transparent',
|
|
133
|
+
inactiveTextColor: text.secondaryColor,
|
|
134
|
+
},
|
|
135
|
+
inputs: {
|
|
136
|
+
errorTextColor: tokens.color.red500,
|
|
137
|
+
labelFontSize: tokens.fontSize.smaller,
|
|
138
|
+
labelTextColor: text.primaryColor,
|
|
139
|
+
text: {
|
|
140
|
+
fontSize: tokens.fontSize.smaller,
|
|
141
|
+
textColor: text.primaryColor,
|
|
142
|
+
borderWidth: tokens.borderWidthNormal,
|
|
143
|
+
borderColor: tokens.color.blue400,
|
|
144
|
+
backgroundColor: tokens.color.white,
|
|
145
|
+
borderRadius: tokens.borderRadius.medium,
|
|
146
|
+
paddingVertical: tokens.spacing1 * 1.5,
|
|
147
|
+
paddingHorizontal: tokens.spacing1 * 1.5,
|
|
148
|
+
},
|
|
149
|
+
radio: {
|
|
150
|
+
fontSize: tokens.fontSize.smaller,
|
|
151
|
+
height: 20,
|
|
152
|
+
borderWidth: tokens.borderWidthNormal,
|
|
153
|
+
borderColor: tokens.color.blue400,
|
|
154
|
+
backgroundColor: tokens.color.white,
|
|
155
|
+
paddingVertical: 3,
|
|
156
|
+
paddingHorizontal: 3,
|
|
157
|
+
activeBackgroundColor: tokens.color.green500,
|
|
158
|
+
itemSpacing: tokens.spacing1,
|
|
159
|
+
labelSpacing: tokens.spacing2,
|
|
160
|
+
},
|
|
161
|
+
swatch: {
|
|
162
|
+
fontSize: tokens.fontSize.smaller,
|
|
163
|
+
height: 40,
|
|
164
|
+
borderWidth: tokens.borderWidthNormal,
|
|
165
|
+
borderColor: tokens.color.blue400,
|
|
166
|
+
backgroundColor: tokens.color.white,
|
|
167
|
+
outerBorderRadius: tokens.borderRadius.medium,
|
|
168
|
+
innerBorderRadius: tokens.borderRadius.small,
|
|
169
|
+
paddingVertical: tokens.spacing1,
|
|
170
|
+
paddingHorizontal: tokens.spacing1,
|
|
171
|
+
nameTextWeight: '600',
|
|
172
|
+
},
|
|
173
|
+
slider: {
|
|
174
|
+
fontSize: tokens.fontSize.smaller,
|
|
175
|
+
labelTextColor: text.secondaryColor,
|
|
176
|
+
valueTextColor: text.primaryColor,
|
|
177
|
+
},
|
|
178
|
+
},
|
|
15
179
|
};
|
|
@@ -3,78 +3,79 @@ 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 { useIsStorySectionSelected, useIsStorySelected } from '../../../hooks';
|
|
7
6
|
import { GridIcon, SearchIcon, StoryIcon } from '../Shared/icons';
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
import { Box } from '../Shared/layout';
|
|
8
|
+
import { useIsStorySelected, useIsStorySectionSelected, useTheme } from '../../../hooks';
|
|
9
|
+
const SectionHeaderText = styled.Text(({ theme }) => ({
|
|
10
|
+
fontSize: theme.storyList.fontSize,
|
|
11
|
+
color: theme.storyList.headerTextColor,
|
|
12
|
+
fontWeight: theme.storyList.headerFontWeight,
|
|
11
13
|
}));
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
const StoryNameText = styled.Text(({ selected, theme }) => ({
|
|
15
|
+
fontSize: theme.storyList.fontSize,
|
|
16
|
+
fontWeight: selected ? theme.storyList.storySelectedFontWeight : theme.storyList.storyFontWeight,
|
|
17
|
+
color: selected ? theme.storyList.storySelectedTextColor : theme.storyList.storyTextColor,
|
|
18
|
+
}));
|
|
19
|
+
const SEARCH_ICON_SIZE = 24;
|
|
20
|
+
const SearchInput = styled.TextInput(Object.assign({ padding: 0 }, StyleSheet.absoluteFillObject), ({ theme }) => ({
|
|
21
|
+
fontSize: theme.storyList.search.fontSize,
|
|
22
|
+
paddingStart: theme.storyList.search.paddingHorizontal + SEARCH_ICON_SIZE,
|
|
23
|
+
color: theme.storyList.search.textColor,
|
|
24
|
+
}));
|
|
25
|
+
const SearchContainer = styled.View(({ theme }) => ({
|
|
19
26
|
flexDirection: 'row',
|
|
20
27
|
alignItems: 'center',
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
margin: theme.panel.paddingHorizontal,
|
|
29
|
+
paddingVertical: theme.storyList.search.paddingVertical,
|
|
30
|
+
paddingStart: theme.storyList.search.paddingHorizontal,
|
|
31
|
+
borderColor: theme.storyList.search.borderColor,
|
|
32
|
+
borderWidth: theme.storyList.search.borderWidth,
|
|
33
|
+
borderRadius: theme.storyList.search.borderRadius,
|
|
34
|
+
backgroundColor: theme.storyList.search.backgroundColor,
|
|
24
35
|
}));
|
|
25
36
|
const SearchBar = (props) => {
|
|
37
|
+
const theme = useTheme();
|
|
26
38
|
return (React.createElement(SearchContainer, null,
|
|
27
39
|
React.createElement(SearchIcon, null),
|
|
28
|
-
React.createElement(SearchInput, Object.assign({}, props, { autoCapitalize: "none", autoComplete: "off", autoCorrect: false, spellCheck: false, clearButtonMode: "while-editing", disableFullscreenUI: true, placeholderTextColor:
|
|
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" }))));
|
|
29
41
|
};
|
|
30
42
|
const HeaderContainer = styled.TouchableOpacity({
|
|
31
|
-
marginTop: 8,
|
|
32
|
-
marginHorizontal: 6,
|
|
33
|
-
padding: 6,
|
|
34
|
-
paddingHorizontal: 8,
|
|
35
|
-
borderTopLeftRadius: 6,
|
|
36
|
-
borderTopRightRadius: 6,
|
|
37
43
|
flexDirection: 'row',
|
|
38
44
|
alignItems: 'center',
|
|
39
45
|
}, ({ selected, theme }) => ({
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
backgroundColor: theme.storyListBackgroundColor,
|
|
46
|
+
marginTop: theme.storyList.sectionSpacing,
|
|
47
|
+
paddingHorizontal: theme.storyList.headerPaddingHorizontal,
|
|
48
|
+
paddingVertical: theme.storyList.headerPaddingVertical,
|
|
49
|
+
backgroundColor: selected ? theme.storyList.sectionActiveBackgroundColor : undefined,
|
|
50
|
+
borderTopLeftRadius: theme.storyList.sectionBorderRadius,
|
|
51
|
+
borderTopRightRadius: theme.storyList.sectionBorderRadius,
|
|
47
52
|
}));
|
|
48
53
|
const SectionHeader = React.memo(({ title, onPress }) => {
|
|
49
54
|
const selected = useIsStorySectionSelected(title);
|
|
50
55
|
return (React.createElement(HeaderContainer, { key: title, selected: selected, onPress: onPress, activeOpacity: 0.8 },
|
|
51
56
|
React.createElement(GridIcon, null),
|
|
52
|
-
React.createElement(
|
|
57
|
+
React.createElement(SectionHeaderText, { selected: selected }, title)));
|
|
53
58
|
});
|
|
54
59
|
const ItemTouchable = styled.TouchableOpacity({
|
|
55
|
-
marginHorizontal: 6,
|
|
56
|
-
padding: 6,
|
|
57
|
-
paddingLeft: 24,
|
|
58
60
|
flexDirection: 'row',
|
|
59
61
|
alignItems: 'center',
|
|
60
|
-
}, ({ selected, sectionSelected, isLastItem, theme }) => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
});
|
|
62
|
+
}, ({ selected, sectionSelected, isLastItem, theme }) => ({
|
|
63
|
+
padding: theme.storyList.storyPaddingHorizontal,
|
|
64
|
+
paddingStart: theme.storyList.storyIndent,
|
|
65
|
+
backgroundColor: selected
|
|
66
|
+
? theme.storyList.storySelectedBackgroundColor
|
|
67
|
+
: sectionSelected
|
|
68
|
+
? theme.storyList.sectionActiveBackgroundColor
|
|
69
|
+
: undefined,
|
|
70
|
+
borderBottomLeftRadius: isLastItem ? theme.storyList.sectionBorderRadius : undefined,
|
|
71
|
+
borderBottomRightRadius: isLastItem ? theme.storyList.sectionBorderRadius : undefined,
|
|
72
|
+
}));
|
|
72
73
|
const ListItem = React.memo(({ storyId, kind, title, isLastItem, onPress }) => {
|
|
73
74
|
const selected = useIsStorySelected(storyId);
|
|
74
75
|
const sectionSelected = useIsStorySectionSelected(kind);
|
|
75
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 },
|
|
76
77
|
React.createElement(StoryIcon, { selected: selected }),
|
|
77
|
-
React.createElement(
|
|
78
|
+
React.createElement(StoryNameText, { selected: selected }, title)));
|
|
78
79
|
}, (prevProps, nextProps) => prevProps.storyId === nextProps.storyId);
|
|
79
80
|
const getStories = (storyIndex) => {
|
|
80
81
|
if (!storyIndex) {
|
|
@@ -100,6 +101,7 @@ function keyExtractor(item, index) {
|
|
|
100
101
|
const StoryListView = ({ storyIndex }) => {
|
|
101
102
|
const originalData = useMemo(() => getStories(storyIndex), [storyIndex]);
|
|
102
103
|
const [data, setData] = useState(originalData);
|
|
104
|
+
const theme = useTheme();
|
|
103
105
|
const handleChangeSearchText = (text) => {
|
|
104
106
|
const query = text.trim();
|
|
105
107
|
if (!query) {
|
|
@@ -127,8 +129,14 @@ const StoryListView = ({ storyIndex }) => {
|
|
|
127
129
|
return (React.createElement(ListItem, { storyId: item.id, title: item.name, kind: item.title, isLastItem: index === section.data.length - 1, onPress: () => changeStory(item.id) }));
|
|
128
130
|
}, []);
|
|
129
131
|
const renderSectionHeader = React.useCallback(({ section: { title, data } }) => (React.createElement(SectionHeader, { title: title, onPress: () => changeStory(data[0].id) })), []);
|
|
130
|
-
return (React.createElement(
|
|
132
|
+
return (React.createElement(Box, { flex: true },
|
|
131
133
|
React.createElement(SearchBar, { testID: "Storybook.ListView.SearchBar", onChangeText: handleChangeSearchText, placeholder: "Find by name" }),
|
|
132
|
-
React.createElement(SectionList, { style: styles.sectionList, contentContainerStyle:
|
|
134
|
+
React.createElement(SectionList, { style: styles.sectionList, contentContainerStyle: [
|
|
135
|
+
styles.sectionListContentContainer,
|
|
136
|
+
{
|
|
137
|
+
paddingVertical: theme.panel.paddingVertical,
|
|
138
|
+
paddingHorizontal: theme.panel.paddingHorizontal,
|
|
139
|
+
},
|
|
140
|
+
], testID: "Storybook.ListView", renderItem: renderItem, renderSectionHeader: renderSectionHeader, keyExtractor: keyExtractor, sections: data, stickySectionHeadersEnabled: false })));
|
|
133
141
|
};
|
|
134
142
|
export default React.memo(StoryListView);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storybook/react-native",
|
|
3
|
-
"version": "6.5.0-rc.
|
|
3
|
+
"version": "6.5.0-rc.3",
|
|
4
4
|
"description": "A better way to develop React Native Components for your app",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"@storybook/preview-web": "^6.5.14",
|
|
62
62
|
"chokidar": "^3.5.1",
|
|
63
63
|
"commander": "^8.2.0",
|
|
64
|
+
"deepmerge": "^4.3.0",
|
|
64
65
|
"emotion-theming": "^10.0.19",
|
|
65
66
|
"glob": "^7.1.7",
|
|
66
67
|
"jotai": "^2.0.2",
|
|
@@ -86,5 +87,5 @@
|
|
|
86
87
|
"publishConfig": {
|
|
87
88
|
"access": "public"
|
|
88
89
|
},
|
|
89
|
-
"gitHead": "
|
|
90
|
+
"gitHead": "f2a1a9100ba5ddf37fa59b58518751a525eb4788"
|
|
90
91
|
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { ViewStyle } from 'react-native';
|
|
3
|
-
export interface Props {
|
|
4
|
-
index: number;
|
|
5
|
-
onPress: (id: number) => void;
|
|
6
|
-
style: ViewStyle;
|
|
7
|
-
}
|
|
8
|
-
declare const _default: React.MemoExoticComponent<({ index, onPress, style }: Props) => JSX.Element>;
|
|
9
|
-
export default _default;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import styled from '@emotion/native';
|
|
3
|
-
import Button from './Button';
|
|
4
|
-
import { NAVIGATOR, PREVIEW, ADDONS } from './constants';
|
|
5
|
-
const Container = styled.View(({ theme }) => ({
|
|
6
|
-
flexDirection: 'row',
|
|
7
|
-
paddingHorizontal: 8,
|
|
8
|
-
backgroundColor: theme.backgroundColor || 'white',
|
|
9
|
-
borderTopWidth: 1,
|
|
10
|
-
borderBottomWidth: 1,
|
|
11
|
-
borderColor: theme.borderColor || '#e6e6e6',
|
|
12
|
-
}));
|
|
13
|
-
const Bar = ({ index, onPress, style }) => (React.createElement(Container, { style: style },
|
|
14
|
-
React.createElement(Button, { onPress: onPress, testID: "BottomMenu.Navigator", id: NAVIGATOR, active: index === NAVIGATOR }, "NAVIGATOR"),
|
|
15
|
-
React.createElement(Button, { onPress: onPress, testID: "BottomMenu.Preview", id: PREVIEW, active: index === PREVIEW }, "PREVIEW"),
|
|
16
|
-
React.createElement(Button, { onPress: onPress, testID: "BottomMenu.Addons", id: ADDONS, active: index === ADDONS }, "ADDONS")));
|
|
17
|
-
export default React.memo(Bar);
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import React, { ReactNode } from 'react';
|
|
2
|
-
interface Props {
|
|
3
|
-
id: number | string;
|
|
4
|
-
active: boolean;
|
|
5
|
-
onPress: (id: number | string) => void;
|
|
6
|
-
testID?: string;
|
|
7
|
-
children: ReactNode;
|
|
8
|
-
}
|
|
9
|
-
declare const _default: React.MemoExoticComponent<({ onPress, id, active, children, testID }: Props) => JSX.Element>;
|
|
10
|
-
export default _default;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { TouchableOpacity } from 'react-native';
|
|
3
|
-
import styled from '@emotion/native';
|
|
4
|
-
const ActiveBorder = styled.View(({ active, theme }) => ({
|
|
5
|
-
backgroundColor: active ? theme.borderColor || '#e6e6e6' : 'transparent',
|
|
6
|
-
height: 3,
|
|
7
|
-
}));
|
|
8
|
-
const ButtonText = styled.Text(({ theme, active }) => ({
|
|
9
|
-
color: active ? theme.buttonActiveTextColor || '#444444' : theme.buttonTextColor || '#999999',
|
|
10
|
-
paddingHorizontal: 8,
|
|
11
|
-
paddingVertical: 10,
|
|
12
|
-
fontSize: 11,
|
|
13
|
-
}));
|
|
14
|
-
const Button = ({ onPress, id, active, children, testID }) => (React.createElement(TouchableOpacity, { testID: testID, onPress: () => onPress(id), activeOpacity: 0.8 },
|
|
15
|
-
React.createElement(ButtonText, { active: active }, children),
|
|
16
|
-
React.createElement(ActiveBorder, { active: active })));
|
|
17
|
-
export default React.memo(Button);
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import styled from '@emotion/native';
|
|
2
|
-
export const Header = styled.Text(({ theme }) => ({
|
|
3
|
-
fontSize: 16,
|
|
4
|
-
color: theme.headerTextColor || 'black',
|
|
5
|
-
fontWeight: '500',
|
|
6
|
-
}));
|
|
7
|
-
export const Name = styled.Text({
|
|
8
|
-
fontSize: 16,
|
|
9
|
-
}, ({ selected, theme }) => {
|
|
10
|
-
var _a, _b;
|
|
11
|
-
return selected
|
|
12
|
-
? { fontWeight: '700', color: (_a = theme.listItemActiveTextColor) !== null && _a !== void 0 ? _a : 'white' }
|
|
13
|
-
: { color: (_b = theme.listItemTextColor) !== null && _b !== void 0 ? _b : 'black' };
|
|
14
|
-
});
|
|
15
|
-
export const Label = styled.Text(({ theme }) => ({
|
|
16
|
-
fontSize: 18,
|
|
17
|
-
color: theme.labelColor || 'black',
|
|
18
|
-
}));
|