hypertoxin
Version:
A themeable ReactNative component library for developing native apps
1,169 lines (1,104 loc) • 42.9 kB
JavaScript
/**
* Copyright 2016-present Tuan Le.
*
* Licensed under the MIT License.
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://opensource.org/licenses/mit-license.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*------------------------------------------------------------------------
*
* @module HeaderScreen
* @description - Header screen component.
*
* @author Tuan Le (tuan.t.lei@gmail.com)
*
* @flow
*/
; // eslint-disable-line
import React from 'react';
import ReactNative from 'react-native'; // eslint-disable-line
import PropTypes from 'prop-types';
import { View as AnimatedView } from 'react-native-animatable';
import {
DefaultTheme,
DefaultThemeContext
} from '../../themes/default-theme';
const {
Dimensions,
ImageBackground,
StatusBar,
Text,
View
} = ReactNative;
const DEVICE_WIDTH = Dimensions.get(`window`).width;
const DEFAULT_ANIMATION_DURATION_MS = 300;
const DEFAULT_DROP_SHADOW_STYLE = {
shadowColor: `#000000`,
shadowRadius: 1,
shadowOpacity: 0.25,
shadowOffset: {
width: 0,
height: 1
}
};
const DEFAULT_HEADER_SCREEN_STYLE = {
container: {
position: `absolute`,
flexDirection: `column`,
alignItems: `stretch`,
justifyContent: `center`,
width: DEVICE_WIDTH,
zIndex: 10,
top: 0,
backgroundColor: `transparent`
},
status: {
position: `absolute`,
width: DEVICE_WIDTH,
height: DefaultTheme.screen.size.header.status,
zIndex: 12,
top: 0,
left: 0
},
navigation: {
small: {
flexDirection: `row`,
alignItems: `center`,
justifyContent: `space-between`,
width: DEVICE_WIDTH,
height: DefaultTheme.screen.size.header.navigation.small,
paddingHorizontal: 6,
zIndex: 11,
top: DefaultTheme.screen.size.header.status
},
normal: {
flexDirection: `row`,
alignItems: `center`,
justifyContent: `space-between`,
width: DEVICE_WIDTH,
height: DefaultTheme.screen.size.header.navigation.normal,
zIndex: 11,
top: DefaultTheme.screen.size.header.status
},
large: {
flexDirection: `row`,
alignItems: `center`,
justifyContent: `space-between`,
width: DEVICE_WIDTH,
height: DefaultTheme.screen.size.header.navigation.large,
zIndex: 11,
top: DefaultTheme.screen.size.header.status
}
},
contentLeftRoom: {
flexDirection: `row`,
alignItems: `center`,
justifyContent: `center`,
backgroundColor: `transparent`
},
contentMiddleRoom: {
flexGrow: 1,
flexDirection: `column`,
alignSelf: `stretch`,
alignItems: `flex-start`,
justifyContent: `center`,
backgroundColor: `transparent`
},
contentRightRoom: {
flexDirection: `row`,
alignItems: `center`,
justifyContent: `center`,
backgroundColor: `transparent`
},
mediaRoom: {
flexGrow: 1,
flexDirection: `column`,
alignItems: `stretch`,
justifyContent: `flex-start`,
width: DEVICE_WIDTH,
maxHeight: DefaultTheme.screen.size.header.media,
zIndex: 10,
top: DefaultTheme.screen.size.header.status,
backgroundColor: `transparent`
},
label: {
small: {
...DefaultTheme.screen.font.header.label.small,
paddingLeft: 6
},
normal: DefaultTheme.screen.font.header.label.normal,
large: DefaultTheme.screen.font.header.label.large
}
};
const readjustStyle = (newStyle = {
shade: `themed`,
overlay: `themed`,
dropShadowed: `themed`
}, prevAdjustedStyle = DEFAULT_HEADER_SCREEN_STYLE, Theme = DefaultTheme) => {
const {
shade,
overlay,
dropShadowed,
style
} = newStyle;
const themedShade = shade === `themed` ? Theme.screen.header.shade : shade;
const themedOverlay = overlay === `themed` ? Theme.screen.header.overlay : overlay;
let themedStatusColor;
let themedNavigationColor;
let themedMediaColor;
let themedLabelColor;
let dropShadow;
switch (themedOverlay) { // eslint-disable-line
case `opaque`:
themedStatusColor = Theme.screen.color.header.status[themedShade];
themedNavigationColor = Theme.screen.color.header.navigation[themedShade];
themedMediaColor = Theme.screen.color.header.media[themedShade];
break;
case `translucent`:
themedStatusColor = `${Theme.screen.color.header.status[themedShade]}${Theme.screen.color.header.opacity}`;
themedNavigationColor = `${Theme.screen.color.header.navigation[themedShade]}${Theme.screen.color.header.opacity}`;
themedMediaColor = `${Theme.screen.color.header.media[themedShade]}${Theme.screen.color.header.opacity}`;
break;
case `transparent`:
themedStatusColor = `transparent`;
themedNavigationColor = `transparent`;
themedMediaColor = `transparent`;
break;
}
themedLabelColor = Theme.screen.color.header.label[themedShade];
if ((typeof dropShadowed === `boolean` && dropShadowed) || (dropShadowed === `themed` && Theme.screen.header.dropShadowed)) {
dropShadow = DEFAULT_DROP_SHADOW_STYLE;
} else {
dropShadow = {
...DEFAULT_DROP_SHADOW_STYLE,
shadowRadius: 0,
shadowOpacity: 0,
shadowOffset: {
width: 0,
height: 0
}
};
}
return {
container: {
...prevAdjustedStyle.container,
...dropShadow,
...(typeof style === `object` && style.hasOwnProperty(`container`) && typeof style.container === `object` ? style.container : {})
},
status: {
...prevAdjustedStyle.status,
height: Theme.screen.size.header.status,
backgroundColor: themedStatusColor,
...(typeof style === `object` && style.hasOwnProperty(`status`) && typeof style.status ? style.status : {})
},
navigation: {
small: {
...prevAdjustedStyle.navigation.small,
height: Theme.screen.size.header.navigation.small,
top: Theme.screen.size.header.status,
backgroundColor: themedNavigationColor,
...(typeof style === `object` && style.hasOwnProperty(`navigation`) && typeof style.navigation ? style.navigation : {})
},
normal: {
...prevAdjustedStyle.navigation.normal,
height: Theme.screen.size.header.navigation.normal,
top: Theme.screen.size.header.status,
backgroundColor: themedNavigationColor,
...(typeof style === `object` && style.hasOwnProperty(`navigation`) && typeof style.navigation ? style.navigation : {})
}, large: {
...prevAdjustedStyle.navigation.large,
height: Theme.screen.size.header.navigation.large,
top: Theme.screen.size.header.status,
backgroundColor: themedNavigationColor,
...(typeof style === `object` && style.hasOwnProperty(`navigation`) && typeof style.navigation ? style.navigation : {})
}
},
contentLeftRoom: {
...prevAdjustedStyle.contentLeftRoom,
...(typeof style === `object` && style.hasOwnProperty(`contentLeftRoom`) && typeof style.contentLeftRoom ? style.contentLeftRoom : {})
},
contentMiddleRoom: {
...prevAdjustedStyle.contentMiddleRoom,
...(typeof style === `object` && style.hasOwnProperty(`contentMiddleRoom`) && typeof style.contentMiddleRoom ? style.contentMiddleRoom : {})
},
contentRightRoom: {
...prevAdjustedStyle.contentRightRoom,
...(typeof style === `object` && style.hasOwnProperty(`contentRightRoom`) && typeof style.contentRightRoom ? style.contentRightRoom : {})
},
mediaRoom: {
...prevAdjustedStyle.mediaRoom,
maxHeight: Theme.screen.size.header.media,
top: Theme.screen.size.header.status,
backgroundColor: themedMediaColor,
...(typeof style === `object` && style.hasOwnProperty(`mediaRoom`) && typeof style.mediaRoom ? style.mediaRoom : {})
},
label: {
small: {
...prevAdjustedStyle.label.small,
...Theme.screen.font.header.label.small,
color: themedLabelColor,
...(typeof style === `object` && style.hasOwnProperty(`label`) && typeof style.label === `object` ? style.label : {})
},
normal: {
...prevAdjustedStyle.label.normal,
...Theme.screen.font.header.label.normal,
color: themedLabelColor,
...(typeof style === `object` && style.hasOwnProperty(`label`) && typeof style.label === `object` ? style.label : {})
},
large: {
...prevAdjustedStyle.label.large,
...Theme.screen.font.header.label.large,
color: themedLabelColor,
...(typeof style === `object` && style.hasOwnProperty(`label`) && typeof style.label === `object` ? style.label : {})
}
}
};
};
const ImageURISourcePropType = PropTypes.shape({
uri: PropTypes.string,
bundle: PropTypes.string,
method: PropTypes.string,
headers: PropTypes.objectOf(PropTypes.string),
body: PropTypes.string,
cache: PropTypes.oneOf([
`themed`,
`reload`,
`force-cache`,
`only-if-cached`
]),
width: PropTypes.number,
height: PropTypes.number,
scale: PropTypes.number
});
export default class HeaderScreen extends React.Component {
static contextType = DefaultThemeContext
static propTypes = {
shade: PropTypes.oneOf([ `themed`, `light`, `dark` ]),
overlay: PropTypes.oneOf([ `themed`, `opaque`, `translucent`, `transparent` ]),
size: PropTypes.oneOf([ `themed`, `small`, `normal`, `large` ]),
dropShadowed: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.string
]),
coverImageSource: PropTypes.oneOfType([
() => null,
PropTypes.string,
PropTypes.number,
ImageURISourcePropType,
PropTypes.arrayOf(ImageURISourcePropType)
]),
label: PropTypes.string,
initialAnimation: PropTypes.oneOfType([
PropTypes.string,
PropTypes.shape({
refName: PropTypes.string,
transitions: PropTypes.arrayOf(PropTypes.shape({
to: PropTypes.oneOfType([
PropTypes.func,
PropTypes.object
]),
from: PropTypes.oneOfType([
PropTypes.func,
PropTypes.object
]),
option: PropTypes.shape({
duration: PropTypes.number,
delay: PropTypes.number,
easing: PropTypes.string
})
})),
onTransitionBegin: PropTypes.func,
onTransitionEnd: PropTypes.func,
onAnimationBegin: PropTypes.func,
onAnimationEnd: PropTypes.func
})
]),
onHideNavigation: PropTypes.func,
onShowNavigation: PropTypes.func,
onHideMedia: PropTypes.func,
onShowMedia: PropTypes.func
}
static defaultProps = {
shade: `themed`,
overlay: `themed`,
size: `themed`,
dropShadowed: `themed`,
coverImageSource: null,
label: ``,
initialAnimation: `themed`,
onHideNavigation: () => null,
onShowNavigation: () => null,
onHideMedia: () => null,
onShowMedia: () => null
}
static getDerivedStateFromProps (props, state) {
const {
shade,
overlay,
dropShadowed,
style
} = props;
const {
Theme
} = state.context;
return {
context: {
Theme
},
adjustedStyle: readjustStyle({
shade,
overlay,
dropShadowed,
style
}, state.adjustedStyle, Theme)
};
}
constructor (props) {
super(props);
const component = this;
component.refCache = {};
component.state = {
context: {
Theme: DefaultTheme
},
visibility: {
navigation: true,
media: true
},
adjustedStyle: DEFAULT_HEADER_SCREEN_STYLE
};
}
isNavigationVisible = () => {
const component = this;
const {
visibility
} = component.state;
return visibility.navigation;
}
isMediaVisible = () => {
const component = this;
const {
visibility
} = component.state;
return visibility.media;
}
hideNavigation = (animation = {
transitions: null,
onTransitionBegin: () => null,
onTransitionEnd: () => null,
onAnimationBegin: () => null,
onAnimationEnd: () => null
}) => {
const component = this;
const {
size,
onHideNavigation
} = component.props;
const {
visibility
} = component.state;
const {
Theme
} = component.context;
const themedSize = size === `themed` ? Theme.screen.header.size : size;
if (typeof animation === `object`) {
const {
transitions,
onTransitionBegin,
onTransitionEnd,
onAnimationBegin,
onAnimationEnd
} = animation;
let animationPromises = [];
if (visibility.navigation) {
animationPromises = component.animate({
refName: `animated-navigation-view`,
transitions: Array.isArray(transitions) ? transitions : [{
to: {
opacity: 0,
translateY: -2 * Theme.screen.size.header.navigation[themedSize]
},
option: {
duration: DEFAULT_ANIMATION_DURATION_MS,
delay: 0,
easing: `linear`
}
}],
onTransitionBegin,
onTransitionEnd,
onAnimationBegin,
onAnimationEnd
});
if (visibility.media) {
component.animate({
refName: `animated-media-room-view`,
transitions: Array.isArray(transitions) ? transitions : [{
to: {
translateY: -Theme.screen.size.header.navigation[themedSize]
},
option: {
duration: DEFAULT_ANIMATION_DURATION_MS,
delay: 0,
easing: `linear`
}
}]
});
}
component.setState((prevState) => {
return {
visibility: {
...prevState.visibility,
navigation: false
}
};
}, () => {
onHideNavigation();
});
}
return animationPromises;
}
}
showNavigation = (animation = {
transitions: null,
onTransitionBegin: () => null,
onTransitionEnd: () => null,
onAnimationBegin: () => null,
onAnimationEnd: () => null
}) => {
const component = this;
const {
onShowNavigation
} = component.props;
const {
visibility
} = component.state;
if (typeof animation === `object`) {
const {
transitions,
onTransitionBegin,
onTransitionEnd,
onAnimationBegin,
onAnimationEnd
} = animation;
let animationPromises = [];
if (!visibility.navigation) {
animationPromises = component.animate({
refName: `animated-navigation-view`,
transitions: Array.isArray(transitions) ? transitions : [{
to: {
opacity: 1,
translateY: 0
},
option: {
duration: DEFAULT_ANIMATION_DURATION_MS,
delay: 0,
easing: `linear`
}
}],
onTransitionBegin,
onTransitionEnd,
onAnimationBegin,
onAnimationEnd
});
if (visibility.media) {
component.animate({
refName: `animated-media-room-view`,
transitions: Array.isArray(transitions) ? transitions : [{
to: {
translateY: 0
},
option: {
duration: DEFAULT_ANIMATION_DURATION_MS,
delay: 0,
easing: `linear`
}
}]
});
}
component.setState((prevState) => {
return {
visibility: {
...prevState.visibility,
navigation: true
}
};
}, () => {
onShowNavigation();
});
}
return animationPromises;
}
}
hideMedia = (animation = {
transitions: null,
onTransitionBegin: () => null,
onTransitionEnd: () => null,
onAnimationBegin: () => null,
onAnimationEnd: () => null
}) => {
const component = this;
const {
size,
onHideMedia
} = component.props;
const {
visibility
} = component.state;
const {
Theme
} = component.context;
const themedSize = size === `themed` ? Theme.screen.header.size : size;
if (typeof animation === `object`) {
const {
transitions,
onTransitionBegin,
onTransitionEnd,
onAnimationBegin,
onAnimationEnd
} = animation;
let animationPromises = [];
if (visibility.media) {
animationPromises = component.animate({
refName: `animated-media-room-view`,
transitions: Array.isArray(transitions) ? transitions : [{
to: {
opacity: 0,
translateY: -2 * (Theme.screen.size.header.media + Theme.screen.size.header.navigation[themedSize] + Theme.screen.size.header.status)
},
option: {
duration: DEFAULT_ANIMATION_DURATION_MS,
delay: 0,
easing: `linear`
}
}],
onTransitionBegin,
onTransitionEnd,
onAnimationBegin,
onAnimationEnd
});
component.setState((prevState) => {
return {
visibility: {
...prevState.visibility,
media: false
}
};
}, () => {
onHideMedia();
});
}
return animationPromises;
}
}
showMedia = (animation = {
transitions: null,
onTransitionBegin: () => null,
onTransitionEnd: () => null,
onAnimationBegin: () => null,
onAnimationEnd: () => null
}) => {
const component = this;
const {
size,
onShowMedia
} = component.props;
const {
visibility
} = component.state;
const {
Theme
} = component.context;
const themedSize = size === `themed` ? Theme.screen.header.size : size;
if (typeof animation === `object`) {
const {
transitions,
onTransitionBegin,
onTransitionEnd,
onAnimationBegin,
onAnimationEnd
} = animation;
let animationPromises = [];
if (!visibility.media) {
if (!visibility.navigation) {
animationPromises = component.animate({
refName: `animated-media-room-view`,
transitions: Array.isArray(transitions) ? transitions : [{
to: {
opacity: 1,
translateY: -Theme.screen.size.header.navigation[themedSize]
},
option: {
duration: DEFAULT_ANIMATION_DURATION_MS,
delay: 0,
easing: `linear`
}
}],
onTransitionBegin,
onTransitionEnd,
onAnimationBegin,
onAnimationEnd
});
} else {
animationPromises = component.animate({
refName: `animated-media-room-view`,
transitions: Array.isArray(transitions) ? transitions : [{
to: {
opacity: 1,
translateY: 0
},
option: {
duration: DEFAULT_ANIMATION_DURATION_MS,
delay: 0,
easing: `linear`
}
}],
onTransitionBegin,
onTransitionEnd,
onAnimationBegin,
onAnimationEnd
});
}
component.setState((prevState) => {
return {
visibility: {
...prevState.visibility,
media: true
}
};
}, () => {
onShowMedia();
});
}
return animationPromises;
}
}
animate (animation = {
onTransitionBegin: () => null,
onTransitionEnd: () => null,
onAnimationBegin: () => null,
onAnimationEnd: () => null
}) {
const component = this;
const {
Theme
} = component.context;
if (typeof animation === `string` && animation !== `none`) {
const animationName = animation.replace(/-([a-z])/g, (match, word) => word.toUpperCase());
if (Theme.screen.animation.header.hasOwnProperty(animationName)) {
animation = Theme.screen.animation.header[animationName];
}
}
if (typeof animation === `object`) {
const {
refName,
transitions,
onTransitionBegin,
onTransitionEnd,
onAnimationBegin,
onAnimationEnd
} = animation;
const componentRef = component.refCache[refName];
if (componentRef !== undefined && Array.isArray(transitions)) {
let transitionDuration = 0;
const transitionPromises = transitions.map((transition, transitionIndex) => {
let transitionBeginPromise;
let transitionEndPromise;
if (typeof transition === `object`) {
let transitionType;
let componentRefTransition = {
from: {},
to: {}
};
let componentRefTransitionOption = {
duration: DEFAULT_ANIMATION_DURATION_MS,
delay: 0,
easing: `linear`
};
if (transition.hasOwnProperty(`from`)) {
let from = typeof transition.from === `function` ? transition.from(component.props, component.state, component.context) : transition.from;
componentRefTransition.from = typeof from === `object` ? from : {};
transitionType = `from`;
}
if (transition.hasOwnProperty(`to`)) {
let to = typeof transition.to === `function` ? transition.to(component.props, component.state, component.context) : transition.to;
componentRefTransition.to = typeof to === `object` ? to : {};
transitionType = transitionType === `from` ? `from-to` : `to`;
}
if (transition.hasOwnProperty(`option`) && typeof transition.option === `object`) {
componentRefTransitionOption = {
...componentRefTransitionOption,
...transition.option
};
}
transitionBeginPromise = new Promise((resolve) => {
setTimeout(() => {
if (transitionType === `to`) {
componentRef.transitionTo(
componentRefTransition.to,
componentRefTransitionOption.duration,
componentRefTransitionOption.easing,
componentRefTransitionOption.delay
);
} else if (transitionType === `from-to`) {
setTimeout(() => {
componentRef.transition(
componentRefTransition.from,
componentRefTransition.to,
componentRefTransitionOption.duration,
componentRefTransitionOption.easing
);
}, componentRefTransitionOption.delay);
}
(typeof onTransitionBegin === `function` ? onTransitionBegin : () => null)(transitionIndex);
resolve((_onTransitionBegin) => (typeof _onTransitionBegin === `function` ? _onTransitionBegin : () => null)(_onTransitionBegin));
}, transitionDuration + 5);
});
transitionDuration += componentRefTransitionOption.duration + componentRefTransitionOption.delay;
transitionEndPromise = new Promise((resolve) => {
setTimeout(() => {
(typeof onTransitionEnd === `function` ? onTransitionEnd : () => null)(transitionIndex);
resolve((_onTransitionEnd) => (typeof _onTransitionEnd === `function` ? _onTransitionEnd : () => null)(transitionIndex));
}, transitionDuration);
});
}
return [ transitionBeginPromise, transitionEndPromise ];
});
const animationBeginPromise = new Promise((resolve) => {
(typeof onAnimationBegin === `function` ? onAnimationBegin : () => null)();
resolve((_onAnimationBegin) => (typeof _onAnimationBegin === `function` ? _onAnimationBegin : () => null)());
});
const animationEndPromise = new Promise((resolve) => {
setTimeout(() => {
(typeof onAnimationEnd === `function` ? onAnimationEnd : () => null)();
resolve((_onAnimationEnd) => (typeof _onAnimationEnd === `function` ? _onAnimationEnd : () => null)());
}, transitionDuration + 5);
});
return Promise.all([
animationBeginPromise,
...transitionPromises.flat(),
animationEndPromise
].filter((animationPromise) => animationPromise !== undefined));
}
}
}
componentDidMount () {
const component = this;
const {
shade,
overlay,
dropShadowed,
initialAnimation,
style
} = component.props;
const {
Theme
} = component.context;
component.setState((prevState) => {
return {
context: {
Theme
},
adjustedStyle: readjustStyle({
shade,
overlay,
dropShadowed,
style
}, prevState.adjustedStyle, Theme)
};
}, () => {
if ((typeof initialAnimation === `string` && initialAnimation !== `none`) || typeof initialAnimation === `object`) {
component.animate(initialAnimation);
}
});
}
componentWillUnMount () {
const component = this;
component.refCache = {};
}
renderStatus () {
const component = this;
const {
shade
} = component.props;
const {
adjustedStyle
} = component.state;
const {
Theme
} = component.context;
const themedShade = shade === `themed` ? Theme.screen.header.shade : shade;
return (
<View style = { adjustedStyle.status }>
<StatusBar
barStyle = { `${themedShade}-content` }
networkActivityIndicatorVisible = { false }
/>
</View>
);
}
renderNavigation (contentLeftChildren = null, contentMiddleChildren = null, contentRightChildren = null) {
const component = this;
const {
size,
label
} = component.props;
const {
adjustedStyle
} = component.state;
const {
Theme
} = component.context;
const themedSize = size === `themed` ? Theme.screen.header.size : size;
return (
<AnimatedView
ref = {(componentRef) => {
component.refCache[`animated-navigation-view`] = componentRef;
}}
style = { adjustedStyle.navigation[themedSize] }
useNativeDriver = { true }
>
<AnimatedView
key = 'animated-navigation-content-left-room-view'
ref = {(componentRef) => {
component.refCache[`animated-navigation-content-left-room-view`] = componentRef;
}}
useNativeDriver = { true }
style = { adjustedStyle.contentLeftRoom }
>
{
contentLeftChildren
}
</AnimatedView>
<AnimatedView
key = 'animated-navigation-content-middle-room-view'
ref = {(componentRef) => {
component.refCache[`animated-navigation-content-middle-room-view`] = componentRef;
}}
useNativeDriver = { true }
style = { adjustedStyle.contentMiddleRoom }
>
{
contentMiddleChildren !== null ? contentMiddleChildren : <Text style = { adjustedStyle.label[themedSize] }>{ label }</Text>
}
</AnimatedView>
<AnimatedView
key = 'animated-navigation-content-right-room-view'
ref = {(componentRef) => {
component.refCache[`animated-navigation-content-right-room-view`] = componentRef;
}}
useNativeDriver = { true }
style = { adjustedStyle.contentRightRoom }
>
{
contentRightChildren
}
</AnimatedView>
</AnimatedView>
);
}
renderMedia (mediaChildren = null) {
const component = this;
const {
adjustedStyle
} = component.state;
return (
<AnimatedView
ref = {(componentRef) => {
component.refCache[`animated-media-room-view`] = componentRef;
}}
style = { adjustedStyle.mediaRoom }
useNativeDriver = { true }
>
{
mediaChildren
}
</AnimatedView>
);
}
render () {
const component = this;
const {
shade,
size,
coverImageSource,
children
} = component.props;
const {
visibility,
adjustedStyle
} = component.state;
const {
Theme
} = component.context;
const themedSize = size === `themed` ? Theme.screen.header.size : size;
let contentLeftChildren = null;
let contentMiddleChildren = null;
let contentRightChildren = null;
let mediaChildren = null;
if (React.Children.count(children) > 0) {
let fragments = React.Children.toArray(React.Children.map(children, (child) => {
if (child !== null) {
const {
exclusions,
room,
action
} = child.props;
let actionName = `none`;
let actionArgs = [];
if (typeof room === `string` && (room === `content-left` || room === `content-middle` || room === `content-right` || room === `media`)) {
const inheritedProps = Array.isArray(exclusions) ? Object.entries({
shade,
size: themedSize,
margin: 0,
indentation: 0,
color: adjustedStyle.label[themedSize].color
}).filter(([
propName,
prop // eslint-disable-line
]) => exclusions.every((exclusion) => propName !== exclusion)).reduce((_inheritedProps, [ propName, prop ]) => {
_inheritedProps[propName] = prop;
return _inheritedProps;
}, {}) : {
shade,
size: themedSize,
margin: 0,
indentation: 0,
color: adjustedStyle.label[themedSize].color
};
if (typeof action === `string`) {
actionName = action;
} else if (typeof action === `object` && action.hasOwnProperty(`name`)) {
actionName = action.name;
if (action.hasOwnProperty(`args`) && Array.isArray(action.args)) {
actionArgs = action.args;
}
}
switch (actionName) { // eslint-disable-line
case `show-media`:
if (!visibility.media) {
return React.cloneElement(child, {
...inheritedProps,
onPress: () => component.showMedia(...actionArgs)
});
}
return null;
case `hide-media`:
if (visibility.media) {
return React.cloneElement(child, {
...inheritedProps,
onPress: () => component.hideMedia(...actionArgs)
});
}
return null;
default:
return React.cloneElement(child, inheritedProps);
}
}
}
return child;
}));
contentLeftChildren = fragments.filter((child) => {
if (child !== null) {
const {
room
} = child.props;
return room === `content-left`;
}
return false;
});
contentLeftChildren = contentLeftChildren.length > 0 ? contentLeftChildren : null;
contentMiddleChildren = fragments.filter((child) => {
if (child !== null) {
const {
room
} = child.props;
return room === `content-middle`;
}
return false;
});
contentMiddleChildren = contentMiddleChildren.length > 0 ? contentMiddleChildren : null;
contentRightChildren = fragments.filter((child) => {
if (child !== null) {
const {
room
} = child.props;
return room === `content-right`;
}
return false;
});
contentRightChildren = contentRightChildren.length > 0 ? contentRightChildren : null;
mediaChildren = fragments.filter((child) => {
if (child !== null) {
const {
room
} = child.props;
return room === `media`;
}
return false;
});
mediaChildren = mediaChildren.length > 0 ? mediaChildren : null;
}
if (coverImageSource !== null) {
return (
<AnimatedView
ref = {(componentRef) => {
component.refCache[`animated-container-view`] = componentRef;
}}
style = { adjustedStyle.container }
useNativeDriver = { true }
>
<ImageBackground
style = {{
width: DEVICE_WIDTH,
height: Theme.screen.size.header.media + Theme.screen.size.header.navigation[themedSize] + Theme.screen.size.header.status
}}
source = {
typeof coverImageSource === `string` ? {
uri: coverImageSource,
cache: `only-if-cached`
} : coverImageSource
}
resizeMode = 'cover'
>
{
component.renderStatus()
}
{
component.renderNavigation(contentLeftChildren, contentMiddleChildren, contentRightChildren)
}
{
component.renderMedia(mediaChildren)
}
</ImageBackground>
</AnimatedView>
);
}
return (
<AnimatedView
ref = {(componentRef) => {
component.refCache[`animated-container-view`] = componentRef;
}}
style = { adjustedStyle.container }
useNativeDriver = { true }
>
{
component.renderStatus()
}
{
component.renderNavigation(contentLeftChildren, contentMiddleChildren, contentRightChildren)
}
{
component.renderMedia(mediaChildren)
}
</AnimatedView>
);
}
}