mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-04 11:11:06 +08:00
220 lines
5.9 KiB
JavaScript
220 lines
5.9 KiB
JavaScript
import React, { PureComponent } from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import { Keyboard, KeyboardAvoidingView, Platform, Animated, TouchableOpacity } from 'react-native';
|
|
import Events from '@storybook/core-events';
|
|
|
|
import StoryListView from '../StoryListView';
|
|
import StoryView from '../StoryView';
|
|
import Addons from './addons';
|
|
import Panel from './panel';
|
|
import Navigation from './navigation';
|
|
import AbsolutePositionedKeyboardAwareView from './absolute-positioned-keyboard-aware-view';
|
|
|
|
import { PREVIEW } from './navigation/consts';
|
|
|
|
import {
|
|
getPreviewPosition,
|
|
getPreviewScale,
|
|
getAddonPanelPosition,
|
|
getNavigatorPanelPosition,
|
|
} from './animation';
|
|
|
|
import style from './style';
|
|
|
|
const ANIMATION_DURATION = 300;
|
|
const IS_IOS = Platform.OS === 'ios';
|
|
|
|
export default class OnDeviceUI extends PureComponent {
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
const tabOpen = props.tabOpen || PREVIEW;
|
|
|
|
this.state = {
|
|
tabOpen,
|
|
slideBetweenAnimation: false,
|
|
selection: {},
|
|
storyFn: null,
|
|
previewWidth: 0,
|
|
previewHeight: 0,
|
|
};
|
|
|
|
this.animatedValue = new Animated.Value(tabOpen);
|
|
this.forceRender = this.forceUpdate.bind(this);
|
|
}
|
|
|
|
async componentWillMount() {
|
|
const { events, getInitialStory } = this.props;
|
|
|
|
if (getInitialStory) {
|
|
const story = await getInitialStory();
|
|
|
|
this.setState({
|
|
selection: story || {},
|
|
storyFn: story ? story.storyFn : null,
|
|
});
|
|
}
|
|
|
|
events.on(Events.SELECT_STORY, this.handleStoryChange);
|
|
events.on(Events.FORCE_RE_RENDER, this.forceRender);
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
const { events } = this.props;
|
|
events.removeListener(Events.SELECT_STORY, this.handleStoryChange);
|
|
events.removeListener(Events.FORCE_RE_RENDER, this.forceRender);
|
|
}
|
|
|
|
onLayout = ({ previewWidth, previewHeight }) => {
|
|
this.setState({ previewWidth, previewHeight });
|
|
};
|
|
|
|
handleOpenPreview = () => {
|
|
this.handleToggleTab(PREVIEW);
|
|
};
|
|
|
|
handleStoryChange = selection => {
|
|
const { selection: prevSelection } = this.state;
|
|
if (selection.kind === prevSelection.kind && selection.story === prevSelection.story) {
|
|
this.handleToggleTab(PREVIEW);
|
|
}
|
|
|
|
this.setState({
|
|
selection: {
|
|
kind: selection.kind,
|
|
story: selection.story,
|
|
},
|
|
storyFn: selection.storyFn,
|
|
});
|
|
};
|
|
|
|
handleToggleTab = newTabOpen => {
|
|
const { tabOpen } = this.state;
|
|
|
|
if (newTabOpen === tabOpen) {
|
|
return;
|
|
}
|
|
|
|
Animated.timing(this.animatedValue, {
|
|
toValue: newTabOpen,
|
|
duration: ANIMATION_DURATION,
|
|
useNativeDriver: true,
|
|
}).start();
|
|
|
|
this.setState({
|
|
tabOpen: newTabOpen,
|
|
// True if swiping between navigator and addons
|
|
slideBetweenAnimation: tabOpen + newTabOpen === PREVIEW,
|
|
});
|
|
|
|
// close the keyboard opened from a TextInput from story list or knobs
|
|
if (newTabOpen === PREVIEW) {
|
|
Keyboard.dismiss();
|
|
}
|
|
};
|
|
|
|
render() {
|
|
const {
|
|
stories,
|
|
events,
|
|
url,
|
|
isUIHidden,
|
|
shouldDisableKeyboardAvoidingView,
|
|
keyboardAvoidingViewVerticalOffset,
|
|
} = this.props;
|
|
const {
|
|
tabOpen,
|
|
slideBetweenAnimation,
|
|
selection,
|
|
storyFn,
|
|
previewWidth,
|
|
previewHeight,
|
|
} = this.state;
|
|
|
|
const previewWrapperStyles = [
|
|
style.flex,
|
|
getPreviewPosition(this.animatedValue, previewWidth, previewHeight, slideBetweenAnimation),
|
|
];
|
|
|
|
const previewStyles = [
|
|
style.flex,
|
|
tabOpen !== 0 && style.previewMinimized,
|
|
getPreviewScale(this.animatedValue, slideBetweenAnimation),
|
|
];
|
|
|
|
return (
|
|
<KeyboardAvoidingView
|
|
enabled={!shouldDisableKeyboardAvoidingView || tabOpen !== PREVIEW}
|
|
behavior={IS_IOS ? 'padding' : null}
|
|
keyboardVerticalOffset={keyboardAvoidingViewVerticalOffset}
|
|
style={style.flex}
|
|
>
|
|
<AbsolutePositionedKeyboardAwareView
|
|
onLayout={this.onLayout}
|
|
previewHeight={previewHeight}
|
|
previewWidth={previewWidth}
|
|
>
|
|
<Animated.View style={previewWrapperStyles}>
|
|
<Animated.View style={previewStyles}>
|
|
<TouchableOpacity
|
|
accessible={false}
|
|
style={style.flex}
|
|
disabled={tabOpen === PREVIEW}
|
|
onPress={this.handleOpenPreview}
|
|
>
|
|
<StoryView url={url} events={events} selection={selection} storyFn={storyFn} />
|
|
</TouchableOpacity>
|
|
</Animated.View>
|
|
</Animated.View>
|
|
<Panel style={getNavigatorPanelPosition(this.animatedValue, previewWidth)}>
|
|
<StoryListView
|
|
stories={stories}
|
|
events={events}
|
|
selectedKind={selection.kind}
|
|
selectedStory={selection.story}
|
|
/>
|
|
</Panel>
|
|
<Panel style={getAddonPanelPosition(this.animatedValue, previewWidth)}>
|
|
<Addons />
|
|
</Panel>
|
|
</AbsolutePositionedKeyboardAwareView>
|
|
|
|
<Navigation
|
|
tabOpen={tabOpen}
|
|
onChangeTab={this.handleToggleTab}
|
|
initialUiVisible={!isUIHidden}
|
|
/>
|
|
</KeyboardAvoidingView>
|
|
);
|
|
}
|
|
}
|
|
|
|
OnDeviceUI.propTypes = {
|
|
stories: PropTypes.shape({
|
|
dumpStoryBook: PropTypes.func.isRequired,
|
|
on: PropTypes.func.isRequired,
|
|
emit: PropTypes.func.isRequired,
|
|
removeListener: PropTypes.func.isRequired,
|
|
}).isRequired,
|
|
events: PropTypes.shape({
|
|
on: PropTypes.func.isRequired,
|
|
emit: PropTypes.func.isRequired,
|
|
removeListener: PropTypes.func.isRequired,
|
|
}).isRequired,
|
|
url: PropTypes.string,
|
|
tabOpen: PropTypes.number,
|
|
isUIHidden: PropTypes.bool,
|
|
getInitialStory: PropTypes.func,
|
|
shouldDisableKeyboardAvoidingView: PropTypes.bool,
|
|
keyboardAvoidingViewVerticalOffset: PropTypes.number,
|
|
};
|
|
|
|
OnDeviceUI.defaultProps = {
|
|
url: '',
|
|
tabOpen: 0,
|
|
isUIHidden: false,
|
|
getInitialStory: null,
|
|
shouldDisableKeyboardAvoidingView: false,
|
|
keyboardAvoidingViewVerticalOffset: 0,
|
|
};
|